@orkestrel/reason 0.0.12 → 0.0.14

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,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
3
  let _orkestrel_emitter = require("@orkestrel/emitter");
4
- //#region src/core/constants.ts
4
+ // src/core/constants.ts
5
5
  /**
6
6
  * Holds the default `bail` for the `Reason` orchestrator — a reasoner throw is rethrown
7
7
  * after the `error` emit. Default: `true`.
@@ -98,8 +98,7 @@ var DEFINITION_BUILDER_BRAND = Symbol("reasons.definitionBuilder");
98
98
  * match the other's guard.
99
99
  */
100
100
  var SUBJECT_BUILDER_BRAND = Symbol("reasons.subjectBuilder");
101
- //#endregion
102
- //#region src/core/errors.ts
101
+ // src/core/errors.ts
103
102
  /**
104
103
  * Represents an error thrown by the reasons layer.
105
104
  *
@@ -123,6 +122,7 @@ var ReasonError = class extends Error {
123
122
  context;
124
123
  constructor(code, message, context) {
125
124
  super(message);
125
+ Object.defineProperty(this, Symbol.for("@orkestrel/reason.ReasonError"), { value: this });
126
126
  this.name = "ReasonError";
127
127
  this.code = code;
128
128
  if (context !== void 0) this.context = context;
@@ -131,6 +131,13 @@ var ReasonError = class extends Error {
131
131
  /**
132
132
  * Narrows an unknown caught value to a {@link ReasonError}.
133
133
  *
134
+ * @remarks
135
+ * Accepts errors from another installed copy and subclasses regardless of `name`.
136
+ * Requires the own self-valued `Symbol.for('@orkestrel/reason.ReasonError')`
137
+ * brand, a non-base global `Error` prototype, and valid declared members.
138
+ * Plain objects, unbranded look-alikes, and transparent proxies are rejected.
139
+ * Hostile reads return false through the contract intrinsics inside `holds`.
140
+ *
134
141
  * @param value - The value to test (typically a `catch` binding)
135
142
  * @returns True if `value` is a {@link ReasonError}; false otherwise
136
143
  *
@@ -144,10 +151,15 @@ var ReasonError = class extends Error {
144
151
  * ```
145
152
  */
146
153
  function isReasonError(value) {
147
- return (0, _orkestrel_contract.isInstance)(value, ReasonError);
154
+ return (0, _orkestrel_contract.holds)(() => {
155
+ if (!(0, _orkestrel_contract.isInstance)(value, Error) || _orkestrel_contract.INTRINSICS.prototype(value) === Error.prototype) return false;
156
+ if (_orkestrel_contract.INTRINSICS.describe(value, Symbol.for("@orkestrel/reason.ReasonError"))?.value !== value) return false;
157
+ const code = _orkestrel_contract.INTRINSICS.reflect.read(value, "code");
158
+ const context = _orkestrel_contract.INTRINSICS.reflect.read(value, "context");
159
+ return (code === "MISSING" || code === "INVALID" || code === "MISMATCH" || code === "DESTROYED" || code === "TARGET" || code === "OPERATOR") && (0, _orkestrel_contract.isString)(_orkestrel_contract.INTRINSICS.reflect.read(value, "message")) && (context === void 0 || (0, _orkestrel_contract.isObject)(context) || (0, _orkestrel_contract.isFunction)(context));
160
+ });
148
161
  }
149
- //#endregion
150
- //#region src/core/validators.ts
162
+ // src/core/validators.ts
151
163
  /**
152
164
  * Determines whether a value is a {@link Reasoning} literal.
153
165
  *
@@ -164,20 +176,20 @@ function isReasonError(value) {
164
176
  */
165
177
  var isReasoning = (0, _orkestrel_contract.literalOf)("quantitative", "logical", "symbolic", "inferential");
166
178
  /**
167
- * Determines whether a value is a {@link ChainingStrategy} literal.
179
+ * Determines whether a value is a {@link ChainStrategy} literal.
168
180
  *
169
181
  * @param value - The value to test
170
182
  * @returns True if `value` is `'forward'` or `'backward'`; false otherwise
171
183
  *
172
184
  * @example
173
185
  * ```ts
174
- * import { isChainingStrategy } from '@src/core'
186
+ * import { isChainStrategy } from '@src/core'
175
187
  *
176
- * isChainingStrategy('forward') // true
177
- * isChainingStrategy('upward') // false
188
+ * isChainStrategy('forward') // true
189
+ * isChainStrategy('upward') // false
178
190
  * ```
179
191
  */
180
- var isChainingStrategy = (0, _orkestrel_contract.literalOf)("forward", "backward");
192
+ var isChainStrategy = (0, _orkestrel_contract.literalOf)("forward", "backward");
181
193
  /**
182
194
  * Determines whether a value is a {@link MathOperation} literal.
183
195
  *
@@ -700,7 +712,7 @@ function isLogicalDefinition(value) {
700
712
  name: _orkestrel_contract.isString,
701
713
  description: _orkestrel_contract.isString,
702
714
  rules: (0, _orkestrel_contract.arrayOf)(isRule),
703
- strategy: isChainingStrategy,
715
+ strategy: isChainStrategy,
704
716
  depth: _orkestrel_contract.isFiniteNumber
705
717
  }, ["description", "depth"])(value);
706
718
  }
@@ -751,7 +763,7 @@ function isInferentialDefinition(value) {
751
763
  description: _orkestrel_contract.isString,
752
764
  inferences: (0, _orkestrel_contract.arrayOf)(isInference),
753
765
  facts: (0, _orkestrel_contract.arrayOf)(isFact),
754
- strategy: isChainingStrategy,
766
+ strategy: isChainStrategy,
755
767
  depth: _orkestrel_contract.isFiniteNumber
756
768
  }, ["description", "depth"])(value);
757
769
  }
@@ -1012,8 +1024,7 @@ function isProofNode(value) {
1012
1024
  if (!(0, _orkestrel_contract.isArray)(children)) return false;
1013
1025
  const entries = (0, _orkestrel_contract.readArrayEntries)(children);
1014
1026
  if (!entries.success || !entries.value.dense) return false;
1015
- for (let index = entries.value.entries.length - 1; index >= 0; index -= 1) {
1016
- const child = entries.value.entries[index];
1027
+ for (const child of entries.value.entries.toReversed()) {
1017
1028
  if (!(0, _orkestrel_contract.isObject)(child) || (0, _orkestrel_contract.isArray)(child)) return false;
1018
1029
  worklist.push([
1019
1030
  child,
@@ -1200,8 +1211,7 @@ function isDefinitionBuilder(value) {
1200
1211
  function isSubjectBuilder(value) {
1201
1212
  return (0, _orkestrel_contract.isObject)(value) && Reflect.get(value, SUBJECT_BUILDER_BRAND) === true;
1202
1213
  }
1203
- //#endregion
1204
- //#region src/core/helpers.ts
1214
+ // src/core/helpers.ts
1205
1215
  /**
1206
1216
  * Formats a {@link FieldPath} for display — the single string key itself, or the
1207
1217
  * array segments joined with `.`.
@@ -1223,6 +1233,19 @@ function formatField(field) {
1223
1233
  return (0, _orkestrel_contract.isString)(field) ? field : field.join(".");
1224
1234
  }
1225
1235
  /**
1236
+ * Iterates nonnegative indices below a numeric limit without allocating a range.
1237
+ *
1238
+ * @example
1239
+ * ```ts
1240
+ * [...iterateIndices(2.5)] // [0, 1, 2]
1241
+ * ```
1242
+ */
1243
+ function* iterateIndices(limit) {
1244
+ yield* Array.prototype.keys.call({ length: Math.max(0, Math.ceil(limit)) });
1245
+ if (limit > Number.MAX_SAFE_INTEGER) yield Number.MAX_SAFE_INTEGER;
1246
+ if (limit > Number.MAX_SAFE_INTEGER + 1) for (;;) yield Number.MAX_SAFE_INTEGER + 1;
1247
+ }
1248
+ /**
1226
1249
  * Clamps a number to inclusive {@link Bounds}.
1227
1250
  *
1228
1251
  * @remarks
@@ -1243,10 +1266,8 @@ function formatField(field) {
1243
1266
  */
1244
1267
  function clamp(value, limit) {
1245
1268
  if (!limit) return value;
1246
- let result = value;
1247
- if (limit.minimum !== void 0 && result < limit.minimum) result = limit.minimum;
1248
- if (limit.maximum !== void 0 && result > limit.maximum) result = limit.maximum;
1249
- return result;
1269
+ const minimum = limit.minimum !== void 0 && value < limit.minimum ? limit.minimum : value;
1270
+ return limit.maximum !== void 0 && minimum > limit.maximum ? limit.maximum : minimum;
1250
1271
  }
1251
1272
  /**
1252
1273
  * Checks whether a value falls inside an inclusive range expressed as an array.
@@ -1442,12 +1463,7 @@ function equalValues(left, right) {
1442
1463
  * ```
1443
1464
  */
1444
1465
  function sortByPriority(items) {
1445
- const usable = [];
1446
- for (const item of items) {
1447
- if (!(0, _orkestrel_contract.isObject)(item)) continue;
1448
- usable.push(item);
1449
- }
1450
- return usable.sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
1466
+ return Iterator.from(items).filter(_orkestrel_contract.isObject).toArray().sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
1451
1467
  }
1452
1468
  /**
1453
1469
  * Collects the ids that appear more than once in an id-carrying list — each
@@ -1470,9 +1486,10 @@ function sortByPriority(items) {
1470
1486
  * ```
1471
1487
  */
1472
1488
  function findDuplicates(items) {
1473
- const counts = /* @__PURE__ */ new Map();
1474
- for (const item of items) counts.set(item.id, (counts.get(item.id) ?? 0) + 1);
1475
- return [...counts.entries()].filter(([, count]) => count > 1).map(([id]) => id);
1489
+ const indexed = Iterator.from(items).map((item, index) => [item.id, index]).toArray();
1490
+ const first = new Map(indexed.toReversed());
1491
+ const duplicates = new Set(indexed.filter(([id, index]) => first.get(id) !== index).map(([id]) => id));
1492
+ return indexed.filter(([id, index]) => first.get(id) === index && duplicates.has(id)).map(([id]) => id);
1476
1493
  }
1477
1494
  /**
1478
1495
  * Derives a fact's predicate+arity bucket key — length-prefixed so the
@@ -1528,14 +1545,7 @@ function factToArityKey(fact) {
1528
1545
  * ```
1529
1546
  */
1530
1547
  function indexByArity(facts) {
1531
- const index = /* @__PURE__ */ new Map();
1532
- for (const entry of facts) {
1533
- const key = factToArityKey(entry);
1534
- const bucket = index.get(key);
1535
- if (bucket) bucket.push(entry);
1536
- else index.set(key, [entry]);
1537
- }
1538
- return index;
1548
+ return Map.groupBy(facts, factToArityKey);
1539
1549
  }
1540
1550
  /**
1541
1551
  * Derives one fact term's contribution to a dedup key — reference identity for
@@ -1635,21 +1645,20 @@ function factToKey(fact, identities) {
1635
1645
  function matchFacts(pattern, candidate) {
1636
1646
  if (pattern.predicate !== candidate.predicate) return void 0;
1637
1647
  if (pattern.terms.length !== candidate.terms.length) return void 0;
1638
- const bindings = {};
1639
- for (let index = 0; index < pattern.terms.length; index++) {
1640
- const patternTerm = pattern.terms[index];
1648
+ const bindings = /* @__PURE__ */ new Map();
1649
+ for (const [index, patternTerm] of pattern.terms.entries()) {
1641
1650
  const factTerm = candidate.terms[index];
1642
1651
  if ((0, _orkestrel_contract.isString)(patternTerm) && patternTerm.startsWith("?")) {
1643
- if (patternTerm in bindings) {
1644
- if (bindings[patternTerm] !== factTerm) return void 0;
1645
- } else bindings[patternTerm] = factTerm;
1652
+ if (bindings.has(patternTerm)) {
1653
+ if (bindings.get(patternTerm) !== factTerm) return void 0;
1654
+ } else bindings.set(patternTerm, factTerm);
1646
1655
  } else if ((0, _orkestrel_contract.isString)(factTerm) && factTerm.startsWith("?")) {
1647
- if (factTerm in bindings) {
1648
- if (bindings[factTerm] !== patternTerm) return void 0;
1649
- } else bindings[factTerm] = patternTerm;
1656
+ if (bindings.has(factTerm)) {
1657
+ if (bindings.get(factTerm) !== patternTerm) return void 0;
1658
+ } else bindings.set(factTerm, patternTerm);
1650
1659
  } else if (patternTerm !== factTerm) return;
1651
1660
  }
1652
- return bindings;
1661
+ return Object.fromEntries(bindings);
1653
1662
  }
1654
1663
  /**
1655
1664
  * Substitutes a fact's bound `'?'`-variables with their values — a fresh fact
@@ -1711,15 +1720,11 @@ function instantiateFact(fact, bindings) {
1711
1720
  * ```
1712
1721
  */
1713
1722
  function computePremiseConfidence(premises, index, bindings) {
1714
- let confidence = 1;
1715
- for (const premise of premises) {
1723
+ return Iterator.from(premises).reduce((confidence, premise) => {
1716
1724
  const instantiated = instantiateFact(premise, bindings);
1717
- for (const candidate of index.get(factToArityKey(premise)) ?? []) if (matchFacts(instantiated, candidate)) {
1718
- confidence *= candidate.confidence ?? 1;
1719
- break;
1720
- }
1721
- }
1722
- return confidence;
1725
+ for (const candidate of index.get(factToArityKey(premise)) ?? []) if (matchFacts(instantiated, candidate)) return confidence * (candidate.confidence ?? 1);
1726
+ return confidence;
1727
+ }, 1);
1723
1728
  }
1724
1729
  /**
1725
1730
  * Projects a subject's scalar fields into `has(key, value)` base facts — the
@@ -1743,25 +1748,24 @@ function computePremiseConfidence(premises, index, bindings) {
1743
1748
  * ```
1744
1749
  */
1745
1750
  function subjectToFacts(subject) {
1746
- const facts = [];
1747
- const trace = [];
1748
- for (const key of Object.keys(subject)) {
1749
- if (key === "id") continue;
1751
+ const rows = Object.keys(subject).flatMap((key) => {
1752
+ if (key === "id") return [];
1750
1753
  const value = subject[key];
1751
- if (value === void 0 || value === null) continue;
1752
- if ((0, _orkestrel_contract.isObject)(value)) continue;
1753
- facts.push({
1754
- id: `subject:${key}`,
1755
- predicate: "has",
1756
- terms: [key, value],
1757
- confidence: 1
1758
- });
1759
- trace.push(`Subject field "${key}" → has(${key}, ${String(value)})`);
1760
- }
1761
- if (facts.length > 0) trace.push(`Injected ${facts.length} fact(s) from subject`);
1754
+ if (value === void 0 || value === null) return [];
1755
+ if ((0, _orkestrel_contract.isObject)(value)) return [];
1756
+ return [{
1757
+ fact: {
1758
+ id: `subject:${key}`,
1759
+ predicate: "has",
1760
+ terms: [key, value],
1761
+ confidence: 1
1762
+ },
1763
+ trace: `Subject field "${key}" → has(${key}, ${String(value)})`
1764
+ }];
1765
+ });
1762
1766
  return {
1763
- facts,
1764
- trace
1767
+ facts: rows.map((row) => row.fact),
1768
+ trace: [...rows.map((row) => row.trace), ...rows.length > 0 ? [`Injected ${rows.length} fact(s) from subject`] : []]
1765
1769
  };
1766
1770
  }
1767
1771
  /**
@@ -1791,17 +1795,8 @@ function subjectToFacts(subject) {
1791
1795
  * ```
1792
1796
  */
1793
1797
  function findUnboundVariables(inference) {
1794
- const bound = /* @__PURE__ */ new Set();
1795
- for (const premise of inference.premises) for (const term of premise.terms) if ((0, _orkestrel_contract.isString)(term) && term.startsWith("?")) bound.add(term);
1796
- const unbound = [];
1797
- const seen = /* @__PURE__ */ new Set();
1798
- for (const term of inference.conclusion.terms) {
1799
- if (!(0, _orkestrel_contract.isString)(term) || !term.startsWith("?")) continue;
1800
- if (bound.has(term) || seen.has(term)) continue;
1801
- seen.add(term);
1802
- unbound.push(term);
1803
- }
1804
- return unbound;
1798
+ const bound = new Set(Iterator.from(inference.premises).flatMap((premise) => Iterator.from(premise.terms).filter((term) => (0, _orkestrel_contract.isString)(term) && term.startsWith("?"))));
1799
+ return [...new Set(Iterator.from(inference.conclusion.terms).filter((term) => (0, _orkestrel_contract.isString)(term) && term.startsWith("?") && !bound.has(term)))];
1805
1800
  }
1806
1801
  /**
1807
1802
  * Determines whether a symbolic expression contains an unbound occurrence of a
@@ -2018,8 +2013,9 @@ function extractAtoms(expression) {
2018
2013
  continue;
2019
2014
  }
2020
2015
  const operands = node.operands;
2021
- for (let index = operands.length - 1; index >= 0; index--) {
2022
- const operand = operands[index];
2016
+ const length = operands.length;
2017
+ for (const offset of iterateIndices(length)) {
2018
+ const operand = operands[length - 1 - offset];
2023
2019
  if (operand !== void 0) stack.push(operand);
2024
2020
  }
2025
2021
  }
@@ -2089,21 +2085,9 @@ function extractConclusions(expression) {
2089
2085
  * ```
2090
2086
  */
2091
2087
  function findOverlayMismatches(rules) {
2092
- const writeKeys = [];
2093
- const seenWrites = /* @__PURE__ */ new Set();
2094
- for (const candidate of rules) for (const atomLeaf of extractAtoms(candidate.conclusion)) {
2095
- if (!(0, _orkestrel_contract.isArray)(atomLeaf.check.field)) continue;
2096
- const key = formatField(atomLeaf.check.field);
2097
- if (seenWrites.has(key)) continue;
2098
- seenWrites.add(key);
2099
- writeKeys.push(key);
2100
- }
2101
- const readKeys = /* @__PURE__ */ new Set();
2102
- for (const candidate of rules) for (const premise of candidate.premises) for (const atomLeaf of extractAtoms(premise)) {
2103
- if (!(0, _orkestrel_contract.isArray)(atomLeaf.check.field)) continue;
2104
- readKeys.add(formatField(atomLeaf.check.field));
2105
- }
2106
- return writeKeys.filter((key) => readKeys.has(key));
2088
+ const writeKeys = new Set(Iterator.from(rules).flatMap((candidate) => extractAtoms(candidate.conclusion).flatMap((atom) => (0, _orkestrel_contract.isArray)(atom.check.field) ? [formatField(atom.check.field)] : [])));
2089
+ const readKeys = new Set(Iterator.from(rules).flatMap((candidate) => Iterator.from(candidate.premises).flatMap((premise) => extractAtoms(premise).flatMap((atom) => (0, _orkestrel_contract.isArray)(atom.check.field) ? [formatField(atom.check.field)] : []))));
2090
+ return [...writeKeys].filter((key) => readKeys.has(key));
2107
2091
  }
2108
2092
  /**
2109
2093
  * Builds the empty, type-shaped failure {@link ReasonResult} matching a
@@ -2371,20 +2355,20 @@ function removeById(items, id) {
2371
2355
  function mergeById(base, incoming, resolve) {
2372
2356
  const baseById = /* @__PURE__ */ new Map();
2373
2357
  for (const item of base) if (!baseById.has(item.id)) baseById.set(item.id, item);
2374
- const seen = /* @__PURE__ */ new Set();
2375
- const merged = [];
2358
+ const merged = /* @__PURE__ */ new Map();
2376
2359
  for (const item of incoming) {
2377
- if (seen.has(item.id)) continue;
2378
- seen.add(item.id);
2360
+ const query = item.id;
2361
+ if (merged.has(query)) continue;
2362
+ const id = item.id;
2379
2363
  const existing = baseById.get(item.id);
2380
- merged.push(existing === void 0 ? item : resolve ? resolve(existing, item) : item);
2364
+ merged.set(id, existing === void 0 ? item : resolve ? resolve(existing, item) : item);
2381
2365
  }
2382
2366
  for (const item of base) {
2383
- if (seen.has(item.id)) continue;
2384
- seen.add(item.id);
2385
- merged.push(item);
2367
+ const query = item.id;
2368
+ if (merged.has(query)) continue;
2369
+ merged.set(item.id, item);
2386
2370
  }
2387
- return merged;
2371
+ return [...merged.values()];
2388
2372
  }
2389
2373
  /**
2390
2374
  * Inserts `group` into a {@link QuantitativeDefinition}'s `groups` — dedup-then-
@@ -3343,15 +3327,12 @@ function mergeSubjects(base, incoming) {
3343
3327
  */
3344
3328
  function repeatSubject(subject, count) {
3345
3329
  const baseId = subject.id;
3346
- const clones = [];
3347
- for (let index = 0; index < count; index += 1) clones.push((0, _orkestrel_contract.isString)(baseId) ? {
3330
+ return iterateIndices(count).map((index) => (0, _orkestrel_contract.isString)(baseId) ? {
3348
3331
  ...subject,
3349
3332
  id: `${baseId}-${index}`
3350
- } : { ...subject });
3351
- return clones;
3333
+ } : { ...subject }).toArray();
3352
3334
  }
3353
- //#endregion
3354
- //#region src/core/parsers.ts
3335
+ // src/core/parsers.ts
3355
3336
  /**
3356
3337
  * Parses a JSON string into a {@link Definition}, failing safe to `undefined`.
3357
3338
  *
@@ -3378,8 +3359,7 @@ function repeatSubject(subject, count) {
3378
3359
  function parseDefinition(json) {
3379
3360
  return (0, _orkestrel_contract.parseJSONAs)(json, isDefinition);
3380
3361
  }
3381
- //#endregion
3382
- //#region src/core/Reason.ts
3362
+ // src/core/Reason.ts
3383
3363
  /**
3384
3364
  * Implements the reasoning orchestrator — a thin router over registered
3385
3365
  * {@link ReasonerInterface}s.
@@ -3506,8 +3486,7 @@ var Reason = class {
3506
3486
  if (this.#destroyed) throw new ReasonError("DESTROYED", "Reason has been destroyed");
3507
3487
  }
3508
3488
  };
3509
- //#endregion
3510
- //#region src/core/operators/Evaluator.ts
3489
+ // src/core/operators/Evaluator.ts
3511
3490
  /**
3512
3491
  * Evaluates {@link Check}s against subjects — the shared predicate engine of the
3513
3492
  * quantitative and logical reasoners.
@@ -3577,8 +3556,7 @@ var Evaluator = class {
3577
3556
  }
3578
3557
  }
3579
3558
  };
3580
- //#endregion
3581
- //#region src/core/operators/Transformer.ts
3559
+ // src/core/operators/Transformer.ts
3582
3560
  /**
3583
3561
  * Applies math {@link Transform}s to numbers — the quantitative reasoner's
3584
3562
  * per-factor pipeline stage.
@@ -3617,8 +3595,7 @@ var Transformer = class {
3617
3595
  return transforms.reduce((result, transform) => this.apply(result, transform), value);
3618
3596
  }
3619
3597
  };
3620
- //#endregion
3621
- //#region src/core/operators/Aggregator.ts
3598
+ // src/core/operators/Aggregator.ts
3622
3599
  /**
3623
3600
  * Reduces number lists to one number per {@link Aggregation} — the quantitative
3624
3601
  * reasoner's group and definition combiner.
@@ -3668,8 +3645,7 @@ var Aggregator = class {
3668
3645
  }
3669
3646
  }
3670
3647
  };
3671
- //#endregion
3672
- //#region src/core/reasoners/QuantitativeReasoner.ts
3648
+ // src/core/reasoners/QuantitativeReasoner.ts
3673
3649
  /**
3674
3650
  * Performs factor-based numeric scoring.
3675
3651
  *
@@ -3776,24 +3752,19 @@ var QuantitativeReasoner = class {
3776
3752
  };
3777
3753
  const trace = [];
3778
3754
  const errors = [];
3779
- let count = 0;
3780
- const groupResults = [];
3781
- for (const group of definition.groups) {
3782
- if (!(0, _orkestrel_contract.isObject)(group)) continue;
3755
+ const groupResults = Iterator.from(definition.groups).flatMap((group) => {
3756
+ if (!(0, _orkestrel_contract.isObject)(group)) return [];
3783
3757
  if (group.enabled === false) {
3784
3758
  trace.push(`Skipped group "${group.id}" (disabled)`);
3785
- continue;
3759
+ return [];
3786
3760
  }
3787
- const groupResult = this.#evaluateGroup(group, subject, trace, errors);
3788
- groupResults.push(groupResult);
3789
- if (groupResult.applied) count++;
3790
- }
3761
+ return [this.#evaluateGroup(group, subject, trace, errors)];
3762
+ }).toArray();
3791
3763
  const groupValues = groupResults.filter((group) => group.applied).map((group) => group.value);
3792
3764
  const base = definition.base ?? 0;
3793
- let value = base + this.#aggregator.aggregate(groupValues, definition.aggregation);
3794
- trace.push(`Aggregated ${groupValues.length} groups with "${definition.aggregation}": base=${base}, raw=${value}`);
3795
- value = clamp(value, definition.bounds);
3796
- value = roundTo(value, definition.precision ?? 4);
3765
+ const raw = base + this.#aggregator.aggregate(groupValues, definition.aggregation);
3766
+ trace.push(`Aggregated ${groupValues.length} groups with "${definition.aggregation}": base=${base}, raw=${raw}`);
3767
+ const value = roundTo(clamp(raw, definition.bounds), definition.precision ?? 4);
3797
3768
  if (!(0, _orkestrel_contract.isFiniteNumber)(value)) {
3798
3769
  const description = Number.isNaN(value) ? "NaN" : String(value);
3799
3770
  trace.push(`Definition "${definition.id}": produced non-finite value (${description})`);
@@ -3803,7 +3774,7 @@ var QuantitativeReasoner = class {
3803
3774
  reasoning: "quantitative",
3804
3775
  value,
3805
3776
  groups: groupResults,
3806
- count,
3777
+ count: groupValues.length,
3807
3778
  success: errors.length === 0,
3808
3779
  trace,
3809
3780
  errors
@@ -3819,14 +3790,13 @@ var QuantitativeReasoner = class {
3819
3790
  factors: []
3820
3791
  };
3821
3792
  }
3822
- const factorResults = [];
3823
- for (const factor of sortByPriority(group.factors)) {
3793
+ const factorResults = Iterator.from(sortByPriority(group.factors)).flatMap((factor) => {
3824
3794
  if (factor.enabled === false) {
3825
3795
  trace.push(`Skipped factor "${factor.id}" (disabled)`);
3826
- continue;
3796
+ return [];
3827
3797
  }
3828
- factorResults.push(this.#evaluateFactor(factor, subject, trace, errors));
3829
- }
3798
+ return [this.#evaluateFactor(factor, subject, trace, errors)];
3799
+ }).toArray();
3830
3800
  const appliedFactors = factorResults.filter((factor) => factor.applied);
3831
3801
  if (group.strict && appliedFactors.length !== factorResults.length) {
3832
3802
  trace.push(`Group "${group.id}" strict mode: not all factors applied`);
@@ -3844,8 +3814,7 @@ var QuantitativeReasoner = class {
3844
3814
  if (!weightById.has(original.id)) weightById.set(original.id, original.weight ?? 1);
3845
3815
  }
3846
3816
  const weights = appliedFactors.map((factor) => weightById.get(factor.id) ?? 1);
3847
- let value = (group.base ?? 0) + this.#aggregator.aggregate(factorValues, group.aggregation, weights);
3848
- value = clamp(value, group.bounds);
3817
+ const value = clamp((group.base ?? 0) + this.#aggregator.aggregate(factorValues, group.aggregation, weights), group.bounds);
3849
3818
  trace.push(`Group "${group.id}": ${appliedFactors.length}/${factorResults.length} factors applied, value=${value}`);
3850
3819
  return {
3851
3820
  id: group.id,
@@ -3889,9 +3858,7 @@ var QuantitativeReasoner = class {
3889
3858
  raw
3890
3859
  };
3891
3860
  }
3892
- let value = raw;
3893
- if (factor.transforms && factor.transforms.length > 0) value = this.#transformer.chain(value, factor.transforms);
3894
- value = clamp(value, factor.bounds);
3861
+ const value = clamp(factor.transforms && factor.transforms.length > 0 ? this.#transformer.chain(raw, factor.transforms) : raw, factor.bounds);
3895
3862
  if (!(0, _orkestrel_contract.isFiniteNumber)(value)) {
3896
3863
  const description = Number.isNaN(value) ? "NaN" : String(value);
3897
3864
  trace.push(`Factor "${factor.id}": produced non-finite value (${description})`);
@@ -3912,8 +3879,30 @@ var QuantitativeReasoner = class {
3912
3879
  };
3913
3880
  }
3914
3881
  };
3915
- //#endregion
3916
- //#region src/core/reasoners/LogicalReasoner.ts
3882
+ // src/core/LogicalState.ts
3883
+ /**
3884
+ * Stores the derived fields and rule results of one logical reasoning call.
3885
+ */
3886
+ var LogicalState = class {
3887
+ #fields = {};
3888
+ #rules = /* @__PURE__ */ new Map();
3889
+ get fields() {
3890
+ return this.#fields;
3891
+ }
3892
+ field(key) {
3893
+ return this.#fields[key];
3894
+ }
3895
+ set(key, value) {
3896
+ this.#fields[key] = value;
3897
+ }
3898
+ rule(id) {
3899
+ return this.#rules.get(id);
3900
+ }
3901
+ record(result) {
3902
+ this.#rules.set(result.id, result);
3903
+ }
3904
+ };
3905
+ // src/core/reasoners/LogicalReasoner.ts
3917
3906
  /**
3918
3907
  * Deduces booleans from rules with forward or backward chaining.
3919
3908
  *
@@ -4028,58 +4017,59 @@ var LogicalReasoner = class {
4028
4017
  }
4029
4018
  #forward(definition, subject, trace, errors) {
4030
4019
  const maxDepth = definition.depth ?? 10;
4031
- const derived = {};
4020
+ const state = new LogicalState();
4032
4021
  if (definition.rules.length === 0) trace.push("No rules defined");
4033
4022
  const sortedRules = sortByPriority(definition.rules);
4034
- const reportedErrors = /* @__PURE__ */ new Set();
4035
- for (const rule of sortedRules) {
4036
- if (!(0, _orkestrel_contract.isObject)(rule)) continue;
4037
- if (rule.enabled === false) continue;
4023
+ const reportedErrors = new Set(sortedRules.flatMap((rule) => {
4024
+ if (!(0, _orkestrel_contract.isObject)(rule)) return [];
4025
+ if (rule.enabled === false) return [];
4038
4026
  if (!rule.premises || rule.premises.length === 0) {
4039
4027
  errors.push(`Rule "${rule.id}" has no premises — skipped`);
4040
- reportedErrors.add(rule.id);
4028
+ return [rule.id];
4041
4029
  } else if (!rule.conclusion) {
4042
4030
  errors.push(`Rule "${rule.id}" has no conclusion — skipped`);
4043
- reportedErrors.add(rule.id);
4031
+ return [rule.id];
4044
4032
  }
4045
- }
4046
- for (let iteration = 0; iteration < maxDepth; iteration++) {
4047
- let newDerivation = false;
4033
+ return [];
4034
+ }));
4035
+ for (const iteration of iterateIndices(maxDepth)) {
4048
4036
  const currentSubject = {
4049
4037
  ...subject,
4050
- ...derived
4038
+ ...state.fields
4051
4039
  };
4052
- for (const rule of sortedRules) {
4040
+ if (!sortedRules.reduce((previous, rule) => {
4053
4041
  if (rule.enabled === false) {
4054
4042
  trace.push(`Skipped rule "${rule.id}" (disabled)`);
4055
- continue;
4043
+ return previous;
4056
4044
  }
4057
- if (reportedErrors.has(rule.id)) continue;
4045
+ if (reportedErrors.has(rule.id)) return previous;
4058
4046
  if (this.#evaluateRule(rule, currentSubject).applied) {
4059
4047
  const conclusions = extractConclusions(rule.conclusion);
4060
- for (const [key, value] of Object.entries(conclusions)) if (!equalValues(derived[key], value)) {
4061
- derived[key] = value;
4062
- newDerivation = true;
4063
- trace.push(`Rule "${rule.id}" derived: ${key}=${String(value)} (iteration ${iteration + 1})`);
4064
- }
4048
+ return Object.entries(conclusions).reduce((updated, [key, value]) => {
4049
+ if (!equalValues(state.field(key), value)) {
4050
+ state.set(key, value);
4051
+ trace.push(`Rule "${rule.id}" derived: ${key}=${String(value)} (iteration ${iteration + 1})`);
4052
+ return true;
4053
+ }
4054
+ return updated;
4055
+ }, previous);
4065
4056
  }
4066
- }
4067
- if (!newDerivation) {
4057
+ return previous;
4058
+ }, false)) {
4068
4059
  trace.push(`Forward chaining converged at iteration ${iteration + 1}`);
4069
4060
  break;
4070
4061
  }
4071
4062
  }
4072
4063
  const finalSubject = {
4073
4064
  ...subject,
4074
- ...derived
4065
+ ...state.fields
4075
4066
  };
4076
- const finalResults = [];
4077
- for (const rule of definition.rules) {
4078
- if (!(0, _orkestrel_contract.isObject)(rule)) continue;
4079
- if (rule.enabled === false) continue;
4080
- if (reportedErrors.has(rule.id)) continue;
4081
- finalResults.push(this.#evaluateRule(rule, finalSubject));
4082
- }
4067
+ const finalResults = Iterator.from(definition.rules).flatMap((rule) => {
4068
+ if (!(0, _orkestrel_contract.isObject)(rule)) return [];
4069
+ if (rule.enabled === false) return [];
4070
+ if (reportedErrors.has(rule.id)) return [];
4071
+ return [this.#evaluateRule(rule, finalSubject)];
4072
+ }).toArray();
4083
4073
  return {
4084
4074
  conclusion: finalResults.length > 0 ? finalResults[finalResults.length - 1]?.applied ?? false : false,
4085
4075
  rules: finalResults
@@ -4087,8 +4077,7 @@ var LogicalReasoner = class {
4087
4077
  }
4088
4078
  #backward(definition, subject, trace, errors) {
4089
4079
  const maxDepth = definition.depth ?? 10;
4090
- const derived = {};
4091
- const ruleResults = /* @__PURE__ */ new Map();
4080
+ const state = new LogicalState();
4092
4081
  if (definition.rules.length === 0) trace.push("No rules defined");
4093
4082
  const sortedRules = sortByPriority(definition.rules.filter((rule) => {
4094
4083
  if (!(0, _orkestrel_contract.isObject)(rule)) return false;
@@ -4107,52 +4096,45 @@ var LogicalReasoner = class {
4107
4096
  if (!(0, _orkestrel_contract.isObject)(rule)) continue;
4108
4097
  if (rule.enabled === false) trace.push(`Skipped rule "${rule.id}" (disabled)`);
4109
4098
  }
4110
- for (const rule of sortedRules) this.#proveRule(rule, 0, /* @__PURE__ */ new Set(), sortedRules, derived, subject, trace, ruleResults, maxDepth);
4111
- const finalResults = [];
4112
- for (const rule of sortedRules) {
4113
- const existing = ruleResults.get(rule.id);
4114
- if (existing) finalResults.push(existing);
4115
- else finalResults.push(this.#evaluateRule(rule, {
4116
- ...subject,
4117
- ...derived
4118
- }));
4119
- }
4099
+ for (const rule of sortedRules) this.#proveRule(rule, 0, /* @__PURE__ */ new Set(), sortedRules, state, subject, trace, maxDepth);
4100
+ const finalResults = sortedRules.map((rule) => state.rule(rule.id) ?? this.#evaluateRule(rule, {
4101
+ ...subject,
4102
+ ...state.fields
4103
+ }));
4120
4104
  return {
4121
4105
  conclusion: finalResults.length > 0 ? finalResults[finalResults.length - 1]?.applied ?? false : false,
4122
4106
  rules: finalResults
4123
4107
  };
4124
4108
  }
4125
- #proveRule(rule, depth, visited, rules, derived, subject, trace, ruleResults, maxDepth) {
4109
+ #proveRule(rule, depth, visited, rules, state, subject, trace, maxDepth) {
4126
4110
  if (depth > maxDepth) return false;
4127
4111
  if (visited.has(rule.id)) return false;
4128
- const nextVisited = new Set(visited);
4129
- nextVisited.add(rule.id);
4112
+ const nextVisited = /* @__PURE__ */ new Set([...visited, rule.id]);
4130
4113
  const currentSubject = {
4131
4114
  ...subject,
4132
- ...derived
4115
+ ...state.fields
4133
4116
  };
4134
- const premiseResults = [];
4135
- for (const premise of rule.premises) premiseResults.push(this.#establish(premise, currentSubject, rules, depth + 1, nextVisited, derived, subject, trace, ruleResults, maxDepth));
4117
+ const premiseResults = Iterator.from(rule.premises).map((premise) => this.#establish(premise, currentSubject, rules, depth + 1, nextVisited, state, subject, trace, maxDepth)).toArray();
4136
4118
  const allMet = premiseResults.every(Boolean);
4137
- ruleResults.set(rule.id, {
4119
+ state.record({
4138
4120
  id: rule.id,
4139
4121
  applied: allMet,
4140
4122
  premises: premiseResults
4141
4123
  });
4142
4124
  if (allMet) {
4143
4125
  const conclusions = extractConclusions(rule.conclusion);
4144
- for (const [key, value] of Object.entries(conclusions)) if (!equalValues(derived[key], value)) {
4145
- derived[key] = value;
4126
+ for (const [key, value] of Object.entries(conclusions)) if (!equalValues(state.field(key), value)) {
4127
+ state.set(key, value);
4146
4128
  trace.push(`Rule "${rule.id}" derived: ${key}=${String(value)} (backward, depth ${depth})`);
4147
4129
  }
4148
4130
  } else trace.push(`Rule "${rule.id}": does not hold (backward, depth ${depth})`);
4149
4131
  return allMet;
4150
4132
  }
4151
- #establish(expression, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth) {
4133
+ #establish(expression, currentSubject, rules, depth, visited, state, subject, trace, maxDepth) {
4152
4134
  if (this.#evaluateExpression(expression, currentSubject)) return true;
4153
- return this.#proveExpression(expression, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth);
4135
+ return this.#proveExpression(expression, rules, depth, visited, state, subject, trace, maxDepth);
4154
4136
  }
4155
- #proveExpression(expression, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth) {
4137
+ #proveExpression(expression, rules, depth, visited, state, subject, trace, maxDepth) {
4156
4138
  if (depth > maxDepth) return false;
4157
4139
  if (expression.form === "atom") for (const rule of rules) {
4158
4140
  if (!(0, _orkestrel_contract.isObject)(rule)) continue;
@@ -4161,30 +4143,25 @@ var LogicalReasoner = class {
4161
4143
  const field = formatField(expression.check.field);
4162
4144
  const value = expression.check.value;
4163
4145
  if (field in conclusionFacts && conclusionFacts[field] === value) {
4164
- const nextVisited = new Set(visited);
4165
- nextVisited.add(rule.id);
4146
+ const nextVisited = /* @__PURE__ */ new Set([...visited, rule.id]);
4166
4147
  const currentSubject = {
4167
4148
  ...subject,
4168
- ...derived
4149
+ ...state.fields
4169
4150
  };
4170
4151
  const premiseResults = [];
4171
- let allMet = true;
4172
- for (const premise of rule.premises) {
4173
- const met = this.#establish(premise, currentSubject, rules, depth + 1, nextVisited, derived, subject, trace, ruleResults, maxDepth);
4152
+ const allMet = Iterator.from(rule.premises).every((premise) => {
4153
+ const met = this.#establish(premise, currentSubject, rules, depth + 1, nextVisited, state, subject, trace, maxDepth);
4174
4154
  premiseResults.push(met);
4175
- if (!met) {
4176
- allMet = false;
4177
- break;
4178
- }
4179
- }
4180
- ruleResults.set(rule.id, {
4155
+ return met;
4156
+ });
4157
+ state.record({
4181
4158
  id: rule.id,
4182
4159
  applied: allMet,
4183
4160
  premises: premiseResults
4184
4161
  });
4185
4162
  if (allMet) {
4186
- for (const [key, entry] of Object.entries(conclusionFacts)) if (!equalValues(derived[key], entry)) {
4187
- derived[key] = entry;
4163
+ for (const [key, entry] of Object.entries(conclusionFacts)) if (!equalValues(state.field(key), entry)) {
4164
+ state.set(key, entry);
4188
4165
  trace.push(`Rule "${rule.id}" derived: ${key}=${String(entry)} (backward, depth ${depth})`);
4189
4166
  }
4190
4167
  return true;
@@ -4194,31 +4171,31 @@ var LogicalReasoner = class {
4194
4171
  if (expression.form === "compound") {
4195
4172
  const currentSubject = {
4196
4173
  ...subject,
4197
- ...derived
4174
+ ...state.fields
4198
4175
  };
4199
4176
  switch (expression.operator) {
4200
- case "and": return expression.operands.every((operand) => this.#establish(operand, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth));
4201
- case "or": return expression.operands.some((operand) => this.#establish(operand, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth));
4177
+ case "and": return expression.operands.every((operand) => this.#establish(operand, currentSubject, rules, depth, visited, state, subject, trace, maxDepth));
4178
+ case "or": return expression.operands.some((operand) => this.#establish(operand, currentSubject, rules, depth, visited, state, subject, trace, maxDepth));
4202
4179
  case "not": {
4203
4180
  if (expression.operands.length === 0) return true;
4204
4181
  const first = expression.operands[0];
4205
4182
  if (!first) return true;
4206
- return !this.#establish(first, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth);
4183
+ return !this.#establish(first, currentSubject, rules, depth, visited, state, subject, trace, maxDepth);
4207
4184
  }
4208
4185
  case "implies": {
4209
4186
  if (expression.operands.length < 2) return true;
4210
4187
  const antecedent = expression.operands[0];
4211
4188
  const consequent = expression.operands[1];
4212
4189
  if (!antecedent || !consequent) return true;
4213
- if (!this.#establish(antecedent, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth)) return true;
4214
- return this.#establish(consequent, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth);
4190
+ if (!this.#establish(antecedent, currentSubject, rules, depth, visited, state, subject, trace, maxDepth)) return true;
4191
+ return this.#establish(consequent, currentSubject, rules, depth, visited, state, subject, trace, maxDepth);
4215
4192
  }
4216
4193
  case "xor": {
4217
4194
  if (expression.operands.length < 2) return false;
4218
4195
  const left = expression.operands[0];
4219
4196
  const right = expression.operands[1];
4220
4197
  if (!left || !right) return false;
4221
- return this.#establish(left, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth) !== this.#establish(right, currentSubject, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth);
4198
+ return this.#establish(left, currentSubject, rules, depth, visited, state, subject, trace, maxDepth) !== this.#establish(right, currentSubject, rules, depth, visited, state, subject, trace, maxDepth);
4222
4199
  }
4223
4200
  default: return false;
4224
4201
  }
@@ -4256,8 +4233,23 @@ var LogicalReasoner = class {
4256
4233
  }
4257
4234
  }
4258
4235
  };
4259
- //#endregion
4260
- //#region src/core/reasoners/SymbolicReasoner.ts
4236
+ // src/core/SymbolicState.ts
4237
+ /**
4238
+ * Stores numeric bindings for one symbolic reasoning call.
4239
+ */
4240
+ var SymbolicState = class {
4241
+ #values;
4242
+ constructor(values) {
4243
+ this.#values = { ...values };
4244
+ }
4245
+ get values() {
4246
+ return this.#values;
4247
+ }
4248
+ set(name, value) {
4249
+ this.#values[name] = value;
4250
+ }
4251
+ };
4252
+ // src/core/reasoners/SymbolicReasoner.ts
4261
4253
  /**
4262
4254
  * Solves algebraic equations by variable isolation.
4263
4255
  *
@@ -4351,17 +4343,17 @@ var SymbolicReasoner = class {
4351
4343
  const trace = [];
4352
4344
  const errors = [];
4353
4345
  const precision = definition.precision ?? 4;
4354
- const bindings = { ...definition.variables };
4355
- let bound = 0;
4356
- for (const key of Object.keys(subject)) {
4357
- if (key === "id") continue;
4346
+ const state = new SymbolicState(definition.variables);
4347
+ const bound = Object.keys(subject).reduce((count, key) => {
4348
+ if (key === "id") return count;
4358
4349
  const value = (0, _orkestrel_contract.parseNumber)(subject[key]);
4359
4350
  if (value !== void 0) {
4360
- bindings[key] = value;
4351
+ state.set(key, value);
4361
4352
  trace.push(`Subject field "${key}" bound as ${key} = ${value}`);
4362
- bound++;
4353
+ return count + 1;
4363
4354
  }
4364
- }
4355
+ return count;
4356
+ }, 0);
4365
4357
  if (bound > 0) trace.push(`Bound ${bound} variable(s) from subject`);
4366
4358
  if (definition.equations.length === 0) {
4367
4359
  trace.push("No equations to solve");
@@ -4376,7 +4368,7 @@ var SymbolicReasoner = class {
4376
4368
  for (const equation of definition.equations) {
4377
4369
  if (!(0, _orkestrel_contract.isObject)(equation)) continue;
4378
4370
  try {
4379
- const value = this.#solve(equation, bindings);
4371
+ const value = this.#solve(equation, state.values);
4380
4372
  const rounded = roundTo(value, precision);
4381
4373
  if (!(0, _orkestrel_contract.isFiniteNumber)(value) || !(0, _orkestrel_contract.isFiniteNumber)(rounded)) {
4382
4374
  const offender = (0, _orkestrel_contract.isFiniteNumber)(value) ? rounded : value;
@@ -4385,7 +4377,7 @@ var SymbolicReasoner = class {
4385
4377
  trace.push(`Equation "${equation.id}": FAILED — produced non-finite value (${description})`);
4386
4378
  continue;
4387
4379
  }
4388
- bindings[equation.target] = rounded;
4380
+ state.set(equation.target, rounded);
4389
4381
  trace.push(`Equation "${equation.id}": ${equation.target} = ${rounded}`);
4390
4382
  } catch (error) {
4391
4383
  const message = (0, _orkestrel_contract.isError)(error) ? error.message : String(error);
@@ -4393,15 +4385,15 @@ var SymbolicReasoner = class {
4393
4385
  trace.push(`Equation "${equation.id}": FAILED — ${message}`);
4394
4386
  }
4395
4387
  }
4396
- const solutions = {};
4388
+ const solutions = new SymbolicState();
4397
4389
  for (const equation of definition.equations) {
4398
4390
  if (!(0, _orkestrel_contract.isObject)(equation)) continue;
4399
- const value = bindings[equation.target];
4400
- if (value !== void 0) solutions[equation.target] = value;
4391
+ const value = state.values[equation.target];
4392
+ if (value !== void 0) solutions.set(equation.target, value);
4401
4393
  }
4402
4394
  return {
4403
4395
  reasoning: "symbolic",
4404
- solutions,
4396
+ solutions: solutions.values,
4405
4397
  success: errors.length === 0,
4406
4398
  trace,
4407
4399
  errors
@@ -4459,8 +4451,37 @@ var SymbolicReasoner = class {
4459
4451
  }
4460
4452
  }
4461
4453
  };
4462
- //#endregion
4463
- //#region src/core/reasoners/InferentialReasoner.ts
4454
+ // src/core/InferenceJoin.ts
4455
+ /**
4456
+ * Joins premise bindings against a predicate-and-arity fact index.
4457
+ */
4458
+ var InferenceJoin = class {
4459
+ #index;
4460
+ #bindings = [{}];
4461
+ constructor(index) {
4462
+ this.#index = index;
4463
+ }
4464
+ resolve(premises) {
4465
+ this.#bindings = [{}];
4466
+ if (premises.length === 0) return this.#bindings;
4467
+ for (const premise of premises) {
4468
+ const candidates = this.#index.get(factToArityKey(premise)) ?? [];
4469
+ this.#bindings = this.#bindings.flatMap((existing) => {
4470
+ const instantiated = instantiateFact(premise, existing);
4471
+ return Iterator.from(candidates).flatMap((fact) => {
4472
+ const match = matchFacts(instantiated, fact);
4473
+ return match ? [{
4474
+ ...existing,
4475
+ ...match
4476
+ }] : [];
4477
+ }).toArray();
4478
+ });
4479
+ if (this.#bindings.length === 0) break;
4480
+ }
4481
+ return this.#bindings;
4482
+ }
4483
+ };
4484
+ // src/core/reasoners/InferentialReasoner.ts
4464
4485
  /**
4465
4486
  * Derives facts with unification variables and proof trees.
4466
4487
  *
@@ -4615,35 +4636,31 @@ var InferentialReasoner = class {
4615
4636
  }
4616
4637
  validInferences.push(inference);
4617
4638
  }
4618
- for (let iteration = 0; iteration < maxDepth; iteration++) {
4619
- let newDerivation = false;
4620
- for (const inference of validInferences) {
4621
- const allBindings = this.#findAllBindings(inference.premises, byArity);
4622
- for (const bindings of allBindings) {
4623
- const conclusion = instantiateFact(inference.conclusion, bindings);
4624
- const finalConfidence = computePremiseConfidence(inference.premises, byArity, bindings) * (inference.confidence ?? 1);
4625
- const derivedFact = {
4626
- ...conclusion,
4627
- confidence: roundTo(finalConfidence, 4)
4628
- };
4629
- const key = factToKey(derivedFact, identities);
4630
- if (!seen.has(key)) {
4631
- knownFacts.push(derivedFact);
4632
- const arityKey = factToArityKey(derivedFact);
4633
- const bucket = byArity.get(arityKey);
4634
- if (bucket) bucket.push(derivedFact);
4635
- else byArity.set(arityKey, [derivedFact]);
4636
- seen.add(key);
4637
- derived.push(derivedFact);
4638
- newDerivation = true;
4639
- trace.push(`Derived ${derivedFact.predicate}(${derivedFact.terms.join(", ")}) through "${inference.id}" [confidence: ${derivedFact.confidence}] (iteration ${iteration + 1})`);
4640
- }
4639
+ for (const iteration of iterateIndices(maxDepth)) if (!validInferences.reduce((previous, inference) => {
4640
+ return new InferenceJoin(byArity).resolve(inference.premises).reduce((updated, bindings) => {
4641
+ const conclusion = instantiateFact(inference.conclusion, bindings);
4642
+ const finalConfidence = computePremiseConfidence(inference.premises, byArity, bindings) * (inference.confidence ?? 1);
4643
+ const derivedFact = {
4644
+ ...conclusion,
4645
+ confidence: roundTo(finalConfidence, 4)
4646
+ };
4647
+ const key = factToKey(derivedFact, identities);
4648
+ if (!seen.has(key)) {
4649
+ knownFacts.push(derivedFact);
4650
+ const arityKey = factToArityKey(derivedFact);
4651
+ const bucket = byArity.get(arityKey);
4652
+ if (bucket) bucket.push(derivedFact);
4653
+ else byArity.set(arityKey, [derivedFact]);
4654
+ seen.add(key);
4655
+ derived.push(derivedFact);
4656
+ trace.push(`Derived ${derivedFact.predicate}(${derivedFact.terms.join(", ")}) through "${inference.id}" [confidence: ${derivedFact.confidence}] (iteration ${iteration + 1})`);
4657
+ return true;
4641
4658
  }
4642
- }
4643
- if (!newDerivation) {
4644
- trace.push(`Forward chaining converged at iteration ${iteration + 1}`);
4645
- break;
4646
- }
4659
+ return updated;
4660
+ }, previous);
4661
+ }, false)) {
4662
+ trace.push(`Forward chaining converged at iteration ${iteration + 1}`);
4663
+ break;
4647
4664
  }
4648
4665
  return { derived };
4649
4666
  }
@@ -4696,17 +4713,15 @@ var InferentialReasoner = class {
4696
4713
  const conclusionBindings = matchFacts(goal, inference.conclusion);
4697
4714
  if (!conclusionBindings) continue;
4698
4715
  const children = [];
4699
- let allProved = true;
4700
- for (const premise of inference.premises) {
4716
+ if (Iterator.from(inference.premises).every((premise) => {
4701
4717
  const instantiated = instantiateFact(premise, conclusionBindings);
4702
4718
  const child = this.#prove(instantiated, inferences, baseFacts, depth + 1, maxDepth);
4703
- if (child) children.push(child);
4704
- else {
4705
- allProved = false;
4706
- break;
4719
+ if (child) {
4720
+ children.push(child);
4721
+ return true;
4707
4722
  }
4708
- }
4709
- if (allProved) return {
4723
+ return false;
4724
+ })) return {
4710
4725
  fact: goal.id,
4711
4726
  inference: inference.id,
4712
4727
  children,
@@ -4714,30 +4729,8 @@ var InferentialReasoner = class {
4714
4729
  };
4715
4730
  }
4716
4731
  }
4717
- #findAllBindings(premises, byArity) {
4718
- if (premises.length === 0) return [{}];
4719
- let currentBindings = [{}];
4720
- for (const premise of premises) {
4721
- const nextBindings = [];
4722
- const candidates = byArity.get(factToArityKey(premise)) ?? [];
4723
- for (const existing of currentBindings) {
4724
- const instantiated = instantiateFact(premise, existing);
4725
- for (const fact of candidates) {
4726
- const match = matchFacts(instantiated, fact);
4727
- if (match) nextBindings.push({
4728
- ...existing,
4729
- ...match
4730
- });
4731
- }
4732
- }
4733
- currentBindings = nextBindings;
4734
- if (currentBindings.length === 0) break;
4735
- }
4736
- return currentBindings;
4737
- }
4738
4732
  };
4739
- //#endregion
4740
- //#region src/core/builders/managers/Collection.ts
4733
+ // src/core/builders/managers/Collection.ts
4741
4734
  /**
4742
4735
  * Holds the id-keyed collection state the `DefinitionBuilder`'s list managers
4743
4736
  * share — the array, the placement verbs, the silent re-seat, and the
@@ -4803,8 +4796,7 @@ var Collection = class {
4803
4796
  if (this.#destroyed) throw new ReasonError("DESTROYED", `${this.#owner} has been destroyed`);
4804
4797
  }
4805
4798
  };
4806
- //#endregion
4807
- //#region src/core/builders/managers/EquationManager.ts
4799
+ // src/core/builders/managers/EquationManager.ts
4808
4800
  /**
4809
4801
  * Implements the {@link EquationManagerInterface} — a self-owning,
4810
4802
  * kind-free manager over a symbolic definition's `equations`.
@@ -4875,11 +4867,7 @@ var EquationManager = class {
4875
4867
  for (const equation of this.#equations.items()) this.#removeOne(equation.id);
4876
4868
  return;
4877
4869
  }
4878
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
4879
- let all = true;
4880
- for (const id of idOrIds) if (!this.#removeOne(id)) all = false;
4881
- return all;
4882
- }
4870
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(id) && all, true);
4883
4871
  return this.#removeOne(idOrIds);
4884
4872
  }
4885
4873
  seat(equations) {
@@ -4896,8 +4884,7 @@ var EquationManager = class {
4896
4884
  return true;
4897
4885
  }
4898
4886
  };
4899
- //#endregion
4900
- //#region src/core/builders/managers/FactManager.ts
4887
+ // src/core/builders/managers/FactManager.ts
4901
4888
  /**
4902
4889
  * Implements the {@link FactManagerInterface} — a self-owning, kind-free
4903
4890
  * manager over an inferential definition's `facts`.
@@ -4963,11 +4950,7 @@ var FactManager = class {
4963
4950
  for (const fact of this.#facts.items()) this.#removeOne(fact.id);
4964
4951
  return;
4965
4952
  }
4966
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
4967
- let all = true;
4968
- for (const id of idOrIds) if (!this.#removeOne(id)) all = false;
4969
- return all;
4970
- }
4953
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(id) && all, true);
4971
4954
  return this.#removeOne(idOrIds);
4972
4955
  }
4973
4956
  seat(facts) {
@@ -4984,8 +4967,7 @@ var FactManager = class {
4984
4967
  return true;
4985
4968
  }
4986
4969
  };
4987
- //#endregion
4988
- //#region src/core/builders/managers/FactorManager.ts
4970
+ // src/core/builders/managers/FactorManager.ts
4989
4971
  /**
4990
4972
  * Implements the {@link FactorManagerInterface} — the one divergent
4991
4973
  * manager: factors nest inside groups, so it holds no collection state of its
@@ -5069,11 +5051,7 @@ var FactorManager = class {
5069
5051
  for (const factor of this.#locate(groupId).factors) this.#removeOne(groupId, factor.id);
5070
5052
  return;
5071
5053
  }
5072
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
5073
- let all = true;
5074
- for (const id of idOrIds) if (!this.#removeOne(groupId, id)) all = false;
5075
- return all;
5076
- }
5054
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(groupId, id) && all, true);
5077
5055
  return this.#removeOne(groupId, idOrIds);
5078
5056
  }
5079
5057
  destroy() {
@@ -5097,8 +5075,7 @@ var FactorManager = class {
5097
5075
  return group;
5098
5076
  }
5099
5077
  };
5100
- //#endregion
5101
- //#region src/core/builders/managers/GroupManager.ts
5078
+ // src/core/builders/managers/GroupManager.ts
5102
5079
  /**
5103
5080
  * Implements the {@link GroupManagerInterface} — a self-owning, kind-free
5104
5081
  * manager over a quantitative definition's `groups`.
@@ -5164,11 +5141,7 @@ var GroupManager = class {
5164
5141
  for (const group of this.#groups.items()) this.#removeOne(group.id);
5165
5142
  return;
5166
5143
  }
5167
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
5168
- let all = true;
5169
- for (const id of idOrIds) if (!this.#removeOne(id)) all = false;
5170
- return all;
5171
- }
5144
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(id) && all, true);
5172
5145
  return this.#removeOne(idOrIds);
5173
5146
  }
5174
5147
  seat(groups) {
@@ -5185,8 +5158,7 @@ var GroupManager = class {
5185
5158
  return true;
5186
5159
  }
5187
5160
  };
5188
- //#endregion
5189
- //#region src/core/builders/managers/InferenceManager.ts
5161
+ // src/core/builders/managers/InferenceManager.ts
5190
5162
  /**
5191
5163
  * Implements the {@link InferenceManagerInterface} — a self-owning,
5192
5164
  * kind-free manager over an inferential definition's `inferences`.
@@ -5262,11 +5234,7 @@ var InferenceManager = class {
5262
5234
  for (const inference of this.#inferences.items()) this.#removeOne(inference.id);
5263
5235
  return;
5264
5236
  }
5265
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
5266
- let all = true;
5267
- for (const id of idOrIds) if (!this.#removeOne(id)) all = false;
5268
- return all;
5269
- }
5237
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(id) && all, true);
5270
5238
  return this.#removeOne(idOrIds);
5271
5239
  }
5272
5240
  seat(inferences) {
@@ -5283,8 +5251,7 @@ var InferenceManager = class {
5283
5251
  return true;
5284
5252
  }
5285
5253
  };
5286
- //#endregion
5287
- //#region src/core/builders/managers/RuleManager.ts
5254
+ // src/core/builders/managers/RuleManager.ts
5288
5255
  /**
5289
5256
  * Implements the {@link RuleManagerInterface} — a self-owning, kind-free
5290
5257
  * manager over a logical definition's `rules`.
@@ -5353,11 +5320,7 @@ var RuleManager = class {
5353
5320
  for (const rule of this.#rules.items()) this.#removeOne(rule.id);
5354
5321
  return;
5355
5322
  }
5356
- if ((0, _orkestrel_contract.isArray)(idOrIds)) {
5357
- let all = true;
5358
- for (const id of idOrIds) if (!this.#removeOne(id)) all = false;
5359
- return all;
5360
- }
5323
+ if ((0, _orkestrel_contract.isArray)(idOrIds)) return Iterator.from(idOrIds).reduce((all, id) => this.#removeOne(id) && all, true);
5361
5324
  return this.#removeOne(idOrIds);
5362
5325
  }
5363
5326
  seat(rules) {
@@ -5374,8 +5337,7 @@ var RuleManager = class {
5374
5337
  return true;
5375
5338
  }
5376
5339
  };
5377
- //#endregion
5378
- //#region src/core/builders/managers/VariableManager.ts
5340
+ // src/core/builders/managers/VariableManager.ts
5379
5341
  /**
5380
5342
  * Implements the {@link VariableManagerInterface} — a self-owning,
5381
5343
  * kind-free manager over a symbolic definition's `variables`, a name-keyed
@@ -5442,11 +5404,7 @@ var VariableManager = class {
5442
5404
  for (const name of Object.keys(this.#variables)) this.#removeOne(name);
5443
5405
  return;
5444
5406
  }
5445
- if ((0, _orkestrel_contract.isArray)(nameOrNames)) {
5446
- let all = true;
5447
- for (const name of nameOrNames) if (!this.#removeOne(name)) all = false;
5448
- return all;
5449
- }
5407
+ if ((0, _orkestrel_contract.isArray)(nameOrNames)) return Iterator.from(nameOrNames).reduce((all, name) => this.#removeOne(name) && all, true);
5450
5408
  return this.#removeOne(nameOrNames);
5451
5409
  }
5452
5410
  seat(variables) {
@@ -5469,8 +5427,7 @@ var VariableManager = class {
5469
5427
  return true;
5470
5428
  }
5471
5429
  };
5472
- //#endregion
5473
- //#region src/core/builders/DefinitionBuilder.ts
5430
+ // src/core/builders/DefinitionBuilder.ts
5474
5431
  /**
5475
5432
  * Implements a stateful workspace builder accumulating a {@link Definition}
5476
5433
  * through always-present self-owning manager properties: a private scalar
@@ -5582,28 +5539,13 @@ var DefinitionBuilder = class {
5582
5539
  definition: this.#id,
5583
5540
  reasoning: current.reasoning
5584
5541
  });
5585
- let merged;
5586
- if (current.reasoning === "quantitative" && incoming.reasoning === "quantitative") merged = mergeQuantitativeDefinition(current, incoming);
5587
- else if (current.reasoning === "logical" && incoming.reasoning === "logical") merged = mergeLogicalDefinition(current, incoming);
5588
- else if (current.reasoning === "symbolic" && incoming.reasoning === "symbolic") merged = mergeSymbolicDefinition(current, incoming);
5589
- else if (current.reasoning === "inferential" && incoming.reasoning === "inferential") merged = mergeInferentialDefinition(current, incoming);
5590
- else merged = current;
5542
+ const merged = current.reasoning === "quantitative" && incoming.reasoning === "quantitative" ? mergeQuantitativeDefinition(current, incoming) : current.reasoning === "logical" && incoming.reasoning === "logical" ? mergeLogicalDefinition(current, incoming) : current.reasoning === "symbolic" && incoming.reasoning === "symbolic" ? mergeSymbolicDefinition(current, incoming) : current.reasoning === "inferential" && incoming.reasoning === "inferential" ? mergeInferentialDefinition(current, incoming) : current;
5591
5543
  this.#seat(merged);
5592
5544
  this.#emitter.emit("merge", merged.reasoning);
5593
5545
  }
5594
5546
  clear(key) {
5595
5547
  this.#ensureAlive();
5596
- const current = this.#compose();
5597
- let next;
5598
- if (current.reasoning === "quantitative" && isQuantitativeClearKey(key)) next = clearQuantitativeDefinition(current, key);
5599
- else if (current.reasoning === "logical" && isLogicalClearKey(key)) next = clearLogicalDefinition(current, key);
5600
- else if (current.reasoning === "symbolic" && isSymbolicClearKey(key)) next = clearSymbolicDefinition(current, key);
5601
- else if (current.reasoning === "inferential" && isInferentialClearKey(key)) next = clearInferentialDefinition(current, key);
5602
- else throw new ReasonError("MISMATCH", `"${key}" is not a clearable field for reasoning "${current.reasoning}"`, {
5603
- key,
5604
- reasoning: current.reasoning
5605
- });
5606
- this.#envelope = definitionToEnvelope(next);
5548
+ this.#envelope = definitionToEnvelope(this.#clear(key));
5607
5549
  this.#emitter.emit("clear", key);
5608
5550
  }
5609
5551
  destroy() {
@@ -5618,6 +5560,17 @@ var DefinitionBuilder = class {
5618
5560
  this.#emitter.emit("destroy");
5619
5561
  this.#emitter.destroy();
5620
5562
  }
5563
+ #clear(key) {
5564
+ const current = this.#compose();
5565
+ if (current.reasoning === "quantitative" && isQuantitativeClearKey(key)) return clearQuantitativeDefinition(current, key);
5566
+ else if (current.reasoning === "logical" && isLogicalClearKey(key)) return clearLogicalDefinition(current, key);
5567
+ else if (current.reasoning === "symbolic" && isSymbolicClearKey(key)) return clearSymbolicDefinition(current, key);
5568
+ else if (current.reasoning === "inferential" && isInferentialClearKey(key)) return clearInferentialDefinition(current, key);
5569
+ else throw new ReasonError("MISMATCH", `"${key}" is not a clearable field for reasoning "${current.reasoning}"`, {
5570
+ key,
5571
+ reasoning: current.reasoning
5572
+ });
5573
+ }
5621
5574
  #compose() {
5622
5575
  const envelope = this.#envelope;
5623
5576
  switch (envelope.reasoning) {
@@ -5663,8 +5616,7 @@ var DefinitionBuilder = class {
5663
5616
  if (this.#destroyed) throw new ReasonError("DESTROYED", "DefinitionBuilder has been destroyed");
5664
5617
  }
5665
5618
  };
5666
- //#endregion
5667
- //#region src/core/builders/SubjectBuilder.ts
5619
+ // src/core/builders/SubjectBuilder.ts
5668
5620
  /**
5669
5621
  * Implements a stateful workspace builder accumulating a {@link Subject} — one
5670
5622
  * flat key-value collection, no managers.
@@ -5752,11 +5704,7 @@ var SubjectBuilder = class {
5752
5704
  for (const key of Object.keys(this.#subject)) if (key !== "id") this.#removeOne(key);
5753
5705
  return;
5754
5706
  }
5755
- if ((0, _orkestrel_contract.isArray)(keyOrKeys)) {
5756
- let all = true;
5757
- for (const key of keyOrKeys) if (!this.#removeOne(key)) all = false;
5758
- return all;
5759
- }
5707
+ if ((0, _orkestrel_contract.isArray)(keyOrKeys)) return Iterator.from(keyOrKeys).reduce((all, key) => this.#removeOne(key) && all, true);
5760
5708
  return this.#removeOne(keyOrKeys);
5761
5709
  }
5762
5710
  merge(incoming) {
@@ -5800,8 +5748,7 @@ var SubjectBuilder = class {
5800
5748
  return existed;
5801
5749
  }
5802
5750
  };
5803
- //#endregion
5804
- //#region src/core/factories.ts
5751
+ // src/core/factories.ts
5805
5752
  /**
5806
5753
  * Creates a check evaluator.
5807
5754
  *
@@ -6873,7 +6820,6 @@ function createInferentialDefinition(id, name, facts, inferences, overrides) {
6873
6820
  ...overrides
6874
6821
  };
6875
6822
  }
6876
- //#endregion
6877
6823
  exports.AGGREGATOR_ID = AGGREGATOR_ID;
6878
6824
  exports.Aggregator = Aggregator;
6879
6825
  exports.CONFIDENCE_PRECISION = CONFIDENCE_PRECISION;
@@ -6988,7 +6934,7 @@ exports.invertLeft = invertLeft;
6988
6934
  exports.invertRight = invertRight;
6989
6935
  exports.isAggregation = isAggregation;
6990
6936
  exports.isBounds = isBounds;
6991
- exports.isChainingStrategy = isChainingStrategy;
6937
+ exports.isChainStrategy = isChainStrategy;
6992
6938
  exports.isCheck = isCheck;
6993
6939
  exports.isCheckResult = isCheckResult;
6994
6940
  exports.isComparison = isComparison;
@@ -7031,6 +6977,7 @@ exports.isSymbolicDefinition = isSymbolicDefinition;
7031
6977
  exports.isSymbolicExpression = isSymbolicExpression;
7032
6978
  exports.isSymbolicResult = isSymbolicResult;
7033
6979
  exports.isTransform = isTransform;
6980
+ exports.iterateIndices = iterateIndices;
7034
6981
  exports.matchFacts = matchFacts;
7035
6982
  exports.matchesBounds = matchesBounds;
7036
6983
  exports.mergeById = mergeById;