@orkestrel/scaffold 0.0.67 → 0.0.68
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.
- package/dist/bin/main.js +67 -44
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/templates/brief.md +9 -0
- package/dist/host/claude/agents/orkestrel.md +4 -4
- package/dist/host/claude/rules/names.md +15 -0
- package/dist/host/claude/rules/tests.md +33 -4
- package/dist/host/claude/rules/workspace.md +14 -2
- package/dist/host/dotfiles/prettierignore +3 -0
- package/dist/host/guides/README.md +65 -0
- package/dist/host/guides/abort.md +169 -0
- package/dist/host/guides/agent.md +1509 -0
- package/dist/host/guides/brief.md +1266 -0
- package/dist/host/guides/browser.md +2200 -0
- package/dist/host/guides/budget.md +196 -0
- package/dist/host/guides/codec.md +519 -0
- package/dist/host/guides/console.md +785 -0
- package/dist/host/guides/contract.md +1193 -0
- package/dist/host/guides/csv.md +541 -0
- package/dist/host/guides/database.md +2518 -0
- package/dist/host/guides/emitter.md +233 -0
- package/dist/host/guides/form.md +1791 -0
- package/dist/host/guides/html.md +717 -0
- package/dist/host/guides/indexeddb.md +505 -0
- package/dist/host/guides/interpret.md +1029 -0
- package/dist/host/guides/lsp.md +515 -0
- package/dist/host/guides/markdown.md +964 -0
- package/dist/host/guides/mcp.md +5554 -0
- package/dist/host/guides/middleware.md +927 -0
- package/dist/host/guides/msg.md +440 -0
- package/dist/host/guides/ndjson.md +120 -0
- package/dist/host/guides/ollama.md +380 -0
- package/dist/host/guides/pool.md +280 -0
- package/dist/host/guides/probe.md +1210 -0
- package/dist/host/guides/process.md +1620 -0
- package/dist/host/guides/program.md +1110 -0
- package/dist/host/guides/qualifier.md +854 -0
- package/dist/host/guides/queue.md +370 -0
- package/dist/host/guides/rater.md +330 -0
- package/dist/host/guides/reason.md +1122 -0
- package/dist/host/guides/relation.md +373 -0
- package/dist/host/guides/router.md +753 -0
- package/dist/host/guides/scaffold.md +192 -31
- package/dist/host/guides/sea.md +383 -0
- package/dist/host/guides/server.md +752 -0
- package/dist/host/guides/sqlite.md +330 -0
- package/dist/host/guides/sse.md +187 -0
- package/dist/host/guides/supervisor.md +4890 -0
- package/dist/host/guides/table.md +1556 -0
- package/dist/host/guides/template.md +280 -0
- package/dist/host/guides/terminal.md +1145 -0
- package/dist/host/guides/test.md +2969 -0
- package/dist/host/guides/timeout.md +252 -0
- package/dist/host/guides/tool.md +311 -0
- package/dist/host/guides/toolbox.md +1038 -0
- package/dist/host/guides/websocket.md +282 -0
- package/dist/host/guides/worker.md +615 -0
- package/dist/host/guides/workflow.md +1507 -0
- package/dist/host/guides/workspace.md +595 -0
- package/dist/host/manifest.json +1218 -10
- package/dist/host/tests/policy.test.ts +279 -2
- package/dist/host/tests/setupPolicy.ts +437 -6
- package/dist/src/core/index.cjs +38 -16
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +33 -9
- package/dist/src/core/index.d.ts +33 -9
- package/dist/src/core/index.js +37 -17
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +1750 -1567
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +106 -24
- package/dist/src/server/index.d.ts +106 -24
- package/dist/src/server/index.js +1751 -1570
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
# Reason
|
|
2
|
+
|
|
3
|
+
> A synchronous, deterministic reasoning engine: declarative JSON-serializable
|
|
4
|
+
> definitions evaluated against plain subject records to produce traceable
|
|
5
|
+
> results, through the `quantitative`, `logical`, `symbolic`, and `inferential`
|
|
6
|
+
> strategies behind one dispatch surface.
|
|
7
|
+
|
|
8
|
+
Each strategy is a `ReasonerInterface` registered on the thin `Reason` orchestrator: `quantitative` scores factors into a number, `logical` deduces booleans from rules by forward or backward chaining, `symbolic` solves algebraic equations by variable isolation, and `inferential` derives facts through unification variables and proof trees. The injectable `Evaluator`, `Transformer`, and `Aggregator` operators do the arithmetic every strategy shares. Every result is a fresh object carrying `success`, a human-readable `trace`, and accumulated `errors`; nothing mutates its inputs.
|
|
9
|
+
|
|
10
|
+
The design stance is data in, data out, no surprises. Definitions are pure data, built by hand or with the shipped value factories. The orchestrator holds no strategy logic — dispatch is a registry lookup by the `reasoning` discriminant. The operators are total: an unknown comparison surfaces as a `CheckResult.error`, an unknown math operation is a no-op, and divide-by-zero is `NaN` rather than a throw. A reasoner never assumes `validate` ran, so a malformed definition yields a failure result and a throw is reserved for caller misuse, as a coded `ReasonError`.
|
|
11
|
+
|
|
12
|
+
On top of the evaluation engine sits the definitions & subjects capability layer: a pure copy-on-write helper family that changes, extends, merges, and round-trips definitions as data, plus the brand-guarded `DefinitionBuilder` (one self-owning manager per collection) and `SubjectBuilder` (one flat collection) workspaces, which accumulate state through named methods and `build()` a fresh plain payload on demand. Building happens outside the engine: `reason` and `validate` take only the plain data, so a builder's `build()` output is passed at the call site. Deliberately absent: async reasoners, definition persistence, and probabilistic strategies beyond the multiplicative `confidence` of inferential facts. Source: [`src/core`](../src/core). Surfaced through the `@src/core` barrel.
|
|
13
|
+
|
|
14
|
+
## Surface
|
|
15
|
+
|
|
16
|
+
### Create an orchestrator and score a subject
|
|
17
|
+
|
|
18
|
+
Create an orchestrator over the reasoners you need, build a definition, then evaluate subjects against it:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
createFactorGroup,
|
|
23
|
+
createFieldFactor,
|
|
24
|
+
createQuantitativeDefinition,
|
|
25
|
+
createQuantitativeReasoner,
|
|
26
|
+
createReason,
|
|
27
|
+
createStaticFactor,
|
|
28
|
+
} from '@orkestrel/reason'
|
|
29
|
+
|
|
30
|
+
const reason = createReason({ reasoners: [createQuantitativeReasoner()] })
|
|
31
|
+
|
|
32
|
+
const definition = createQuantitativeDefinition('risk', 'Risk score', [
|
|
33
|
+
createFactorGroup('drivers', 'sum', [
|
|
34
|
+
createFieldFactor('age', 'age'), // reads subject.age, parseNumber-coerced
|
|
35
|
+
createStaticFactor('floor', 10), // a fixed contribution
|
|
36
|
+
]),
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
const result = reason.reason({ age: 25 }, definition) // one subject → one result
|
|
40
|
+
if (result.reasoning === 'quantitative') result.value // 35 — narrow by the discriminant
|
|
41
|
+
result.trace // the step-by-step account of how the value came to be
|
|
42
|
+
|
|
43
|
+
reason.supports('quantitative') // true — a reasoner is registered for this reasoning
|
|
44
|
+
reason.reasoner('quantitative')?.supports(definition) // the reasoner's own guard, same check
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`reason` dispatches by `definition.reasoning` — pass an array of subjects and the batch overload maps them in order onto an equal-length result array. Results are a discriminated union (`reasoning` names the axis): narrow with the discriminant and read the strategy-specific payload (`value` / `conclusion` / `solutions` / `derived`).
|
|
48
|
+
|
|
49
|
+
### Entity factories
|
|
50
|
+
|
|
51
|
+
| API | Kind | Summary |
|
|
52
|
+
| ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `createReason` | function | Creates the reasoning orchestrator. |
|
|
54
|
+
| `createQuantitativeReasoner` | function | Creates the quantitative reasoner — factor-based numeric scoring. |
|
|
55
|
+
| `createLogicalReasoner` | function | Creates the logical reasoner — rule-based deduction with forward / backward chaining. |
|
|
56
|
+
| `createSymbolicReasoner` | function | Creates the symbolic reasoner — algebraic equation solving by variable isolation. |
|
|
57
|
+
| `createInferentialReasoner` | function | Creates the inferential reasoner — fact derivation with unification variables and proof trees. |
|
|
58
|
+
| `createEvaluator` | function | Creates a check evaluator. |
|
|
59
|
+
| `createTransformer` | function | Creates a math transformer. |
|
|
60
|
+
| `createAggregator` | function | Creates a number aggregator. |
|
|
61
|
+
| `createDefinitionBuilder` | function | Creates a `DefinitionBuilder` — a stateful workspace builder accumulating a `Definition` through the `groups`, `factors`, `rules`, `equations`, `variables`, `facts`, and `inferences` self-owning manager properties. |
|
|
62
|
+
| `createSubjectBuilder` | function | Creates a `SubjectBuilder` — a stateful workspace builder accumulating a `Subject`. |
|
|
63
|
+
| `createGroupManager` | function | Creates a `GroupManager` — a self-owning manager over a quantitative definition's `groups`. |
|
|
64
|
+
| `createFactorManager` | function | Creates a `FactorManager` — the divergent manager over a group's `factors`, threaded through a required `groupId` locator. |
|
|
65
|
+
| `createRuleManager` | function | Creates a `RuleManager` — a self-owning manager over a logical definition's `rules`. |
|
|
66
|
+
| `createEquationManager` | function | Creates an `EquationManager` — a self-owning manager over a symbolic definition's `equations`. |
|
|
67
|
+
| `createVariableManager` | function | Creates a `VariableManager` — a self-owning manager over a symbolic definition's `variables` (a name-keyed record; `add` / `remove` only). |
|
|
68
|
+
| `createFactManager` | function | Creates a `FactManager` — a self-owning manager over an inferential definition's `facts`. |
|
|
69
|
+
| `createInferenceManager` | function | Creates an `InferenceManager` — a self-owning manager over an inferential definition's `inferences`. |
|
|
70
|
+
|
|
71
|
+
### Orchestrator & reasoners
|
|
72
|
+
|
|
73
|
+
| API | Kind | Summary |
|
|
74
|
+
| ---------------------- | ----- | ------------------------------------------------------------------------------------------- |
|
|
75
|
+
| `Reason` | class | Implements the reasoning orchestrator — a thin router over registered `ReasonerInterface`s. |
|
|
76
|
+
| `QuantitativeReasoner` | class | Performs factor-based numeric scoring. |
|
|
77
|
+
| `LogicalReasoner` | class | Deduces booleans from rules with forward or backward chaining. |
|
|
78
|
+
| `SymbolicReasoner` | class | Solves algebraic equations by variable isolation. |
|
|
79
|
+
| `InferentialReasoner` | class | Derives facts with unification variables and proof trees. |
|
|
80
|
+
|
|
81
|
+
### Operators
|
|
82
|
+
|
|
83
|
+
| API | Kind | Summary |
|
|
84
|
+
| ------------- | ----- | ----------------------------------------------------------------------------------------------------------------- |
|
|
85
|
+
| `Evaluator` | class | Evaluates `Check`s against subjects — the shared predicate engine of the quantitative and logical reasoners. |
|
|
86
|
+
| `Transformer` | class | Applies math `Transform`s to numbers — the quantitative reasoner's per-factor pipeline stage. |
|
|
87
|
+
| `Aggregator` | class | Reduces number lists to one number per `Aggregation` — the quantitative reasoner's group and definition combiner. |
|
|
88
|
+
|
|
89
|
+
### Classes
|
|
90
|
+
|
|
91
|
+
The definitions & subjects capability layer's stateful workspace builders: mutate through named methods, then `build()` a fresh plain payload to hand to `reason` at the call site. The managers are self-owning (each owns its own collection state and emitter, takes its own options, and has its own factory) and kind-free (an off-kind manager accumulates silently and is ignored by `build()` — never a `MISMATCH`). `DefinitionBuilder` and `SubjectBuilder` each carry a `unique symbol` brand — `DEFINITION_BUILDER_BRAND` and `SUBJECT_BUILDER_BRAND` — that `isDefinitionBuilder` and `isSubjectBuilder` read through `Reflect.get`, so plain data can never forge a builder.
|
|
92
|
+
|
|
93
|
+
| API | Kind | Summary |
|
|
94
|
+
| ------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `DefinitionBuilder` | class | Implements a stateful workspace builder accumulating a `Definition` through always-present self-owning manager properties: a private scalar envelope (reasoning / id / name plus the kind's scalars) composed with each collection read from its manager. |
|
|
96
|
+
| `GroupManager` | class | Implements the `GroupManagerInterface` — a self-owning, kind-free manager over a quantitative definition's `groups`. |
|
|
97
|
+
| `FactorManager` | class | Implements the `FactorManagerInterface` — the one divergent manager: factors nest inside groups, so it holds no collection state of its own and threads a required `groupId` locator. |
|
|
98
|
+
| `RuleManager` | class | Implements the `RuleManagerInterface` — a self-owning, kind-free manager over a logical definition's `rules`. |
|
|
99
|
+
| `EquationManager` | class | Implements the `EquationManagerInterface` — a self-owning, kind-free manager over a symbolic definition's `equations`. |
|
|
100
|
+
| `VariableManager` | class | Implements the `VariableManagerInterface` — a self-owning, kind-free manager over a symbolic definition's `variables`, a name-keyed unordered record. |
|
|
101
|
+
| `FactManager` | class | Implements the `FactManagerInterface` — a self-owning, kind-free manager over an inferential definition's `facts`. |
|
|
102
|
+
| `InferenceManager` | class | Implements the `InferenceManagerInterface` — a self-owning, kind-free manager over an inferential definition's `inferences`. |
|
|
103
|
+
| `SubjectBuilder` | class | Implements a stateful workspace builder accumulating a `Subject` — one flat key-value collection, no managers. |
|
|
104
|
+
|
|
105
|
+
### Value factories
|
|
106
|
+
|
|
107
|
+
Plain-data constructors for the declarative definition vocabulary — no lifecycle, no emitter, no identity. Reach for the entity factories earlier when you need a working instance instead.
|
|
108
|
+
|
|
109
|
+
| API | Kind | Summary |
|
|
110
|
+
| ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| `createCheck` | function | Creates a `Check` — one field predicate. |
|
|
112
|
+
| `createAtom` | function | Creates an atom `Expression` — a leaf wrapping one `Check`. |
|
|
113
|
+
| `createCompound` | function | Creates a compound `Expression` — a logical connective over nested operands. |
|
|
114
|
+
| `createRule` | function | Creates a `Rule` — premises and a conclusion. |
|
|
115
|
+
| `createTransform` | function | Creates a `Transform` — one math step. |
|
|
116
|
+
| `createBounds` | function | Creates a `Bounds` — an inclusive numeric clamp. |
|
|
117
|
+
| `createVariable` | function | Creates a variable `SymbolicExpression` leaf. |
|
|
118
|
+
| `createConstant` | function | Creates a constant `SymbolicExpression` leaf. |
|
|
119
|
+
| `createOperation` | function | Creates an operation `SymbolicExpression` node. |
|
|
120
|
+
| `createEquation` | function | Creates an `Equation` — `left = right`, solved for `target`. |
|
|
121
|
+
| `createFact` | function | Creates a `Fact` — a predicate over positional terms. |
|
|
122
|
+
| `createInference` | function | Creates an `Inference` — premise patterns and a conclusion pattern. |
|
|
123
|
+
| `createStaticSource` | function | Creates a static `Source` — a fixed number. |
|
|
124
|
+
| `createFieldSource` | function | Creates a field `Source` — a subject field read as a number. |
|
|
125
|
+
| `createLookupSource` | function | Creates a lookup `Source` — a subject field mapped through a table. |
|
|
126
|
+
| `createRangeSource` | function | Creates a range `Source` — a numeric subject field banded through ordered ranges (first match wins). |
|
|
127
|
+
| `createStaticFactor` | function | Creates a `Factor` over a static `Source`. |
|
|
128
|
+
| `createFieldFactor` | function | Creates a `Factor` over a field `Source`. |
|
|
129
|
+
| `createLookupFactor` | function | Creates a `Factor` over a lookup `Source`. |
|
|
130
|
+
| `createRangeFactor` | function | Creates a `Factor` over a range `Source`. |
|
|
131
|
+
| `createFactorGroup` | function | Creates a `FactorGroup`. |
|
|
132
|
+
| `createQuantitativeDefinition` | function | Creates a `QuantitativeDefinition`. |
|
|
133
|
+
| `createLogicalDefinition` | function | Creates a `LogicalDefinition`. |
|
|
134
|
+
| `createSymbolicDefinition` | function | Creates a `SymbolicDefinition`. |
|
|
135
|
+
| `createInferentialDefinition` | function | Creates an `InferentialDefinition`. |
|
|
136
|
+
|
|
137
|
+
Every value factory returns a fresh object and omits absent optional keys entirely, so its output round-trips the exact-record validators in § Validators.
|
|
138
|
+
|
|
139
|
+
### Helpers
|
|
140
|
+
|
|
141
|
+
| API | Kind | Summary |
|
|
142
|
+
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `formatField` | function | Formats a `FieldPath` for display — the single string key itself, or the array segments joined with `.`. |
|
|
144
|
+
| `clamp` | function | Clamps a number to inclusive `Bounds`. |
|
|
145
|
+
| `roundTo` | function | Rounds a number to a fixed count of decimal places. |
|
|
146
|
+
| `matchesBounds` | function | Checks whether a value falls inside an inclusive range expressed as an array. |
|
|
147
|
+
| `emptyAggregate` | function | Returns the empty-input identity of one `Aggregation`. |
|
|
148
|
+
| `resolveSource` | function | Resolves one `Source` against a subject, falling back when it cannot. |
|
|
149
|
+
| `equalValues` | function | Determines whether two values are SameValueZero-equal — strict `===` with `NaN` equal to itself (and, unlike `Object.is`, `+0` equal to `-0`). |
|
|
150
|
+
| `sortByPriority` | function | Sorts items ascending by `priority ?? DEFAULT_PRIORITY` — a stable copy sort. |
|
|
151
|
+
| `findDuplicates` | function | Collects the ids that appear more than once in an id-carrying list — each duplicated id reported once, in first-occurrence order. |
|
|
152
|
+
| `factToArityKey` | function | Derives a fact's predicate+arity bucket key — length-prefixed so the delimiter cannot be forged. |
|
|
153
|
+
| `indexByArity` | function | Buckets facts by predicate+arity, preserving append order within each bucket. |
|
|
154
|
+
| `termToKey` | function | Derives one fact term's contribution to a dedup key — reference identity for non-null objects / functions, a SameValueZero value string for primitives. |
|
|
155
|
+
| `factToKey` | function | Derives a fact's canonical dedup key — predicate + arity + per-term SameValueZero identity, with confidence excluded from it. |
|
|
156
|
+
| `matchFacts` | function | Unifies a pattern fact positionally against a candidate fact — returning the variable bindings on success, `undefined` on mismatch. |
|
|
157
|
+
| `instantiateFact` | function | Substitutes a fact's bound `'?'`-variables with their values — a fresh fact with unbound terms passed through unchanged. |
|
|
158
|
+
| `subjectToFacts` | function | Projects a subject's scalar fields into `has(key, value)` base facts — the inferential reasoner's subject-injection step. |
|
|
159
|
+
| `computePremiseConfidence` | function | Computes the confidence a set of matched premises contributes to a derived fact — the product of each premise's first matching fact's confidence. |
|
|
160
|
+
| `containsVariable` | function | Determines whether a symbolic expression contains an unbound occurrence of a target variable. |
|
|
161
|
+
| `invertLeft` | function | Inverts a `x op right = value` step, solving for the left operand `x`. |
|
|
162
|
+
| `invertRight` | function | Inverts a `left op x = value` step, solving for the right operand `x`. |
|
|
163
|
+
| `applyOperation` | function | Applies one binary/unary math operation to already-evaluated operands. |
|
|
164
|
+
| `resolveOperand` | function | Resolves the effective right operand of a math operation — the supplied `operand`, or the operation's own identity-preserving default when absent. |
|
|
165
|
+
| `extractAtoms` | function | Returns every atom leaf of an expression tree, depth-first, left-to-right. |
|
|
166
|
+
| `extractConclusions` | function | Flattens a logical conclusion expression into its asserted `field = value` pairs, ignoring the connectives. |
|
|
167
|
+
| `findOverlayMismatches` | function | Collects the flattened overlay keys written through an array path and also read through an array path. |
|
|
168
|
+
| `findUnboundVariables` | function | Collects the `'?'`-prefixed conclusion variables no premise binds. |
|
|
169
|
+
| `buildErrorResult` | function | Builds the empty, type-shaped failure `ReasonResult` matching a definition's reasoning. |
|
|
170
|
+
|
|
171
|
+
The definitions & subjects capability layer (§ Classes) adds a pure, copy-on-write change / extend / merge / store surface over every definition kind, plus a subject engine of pure helpers — none of it mutates an input, and every helper returns a fresh value.
|
|
172
|
+
|
|
173
|
+
| API | Kind | Summary |
|
|
174
|
+
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| `appendById` | function | Inserts `item` into an id-keyed collection, deduping any existing element sharing its id, then placing it at the end, or immediately after `target`. |
|
|
176
|
+
| `prependById` | function | Inserts `item` into an id-keyed collection, deduping any existing element sharing its id, then placing it at the start, or immediately before `target`. |
|
|
177
|
+
| `replaceById` | function | Swaps the element sharing `item.id` in place, preserving its position. |
|
|
178
|
+
| `removeById` | function | Filters every element sharing `id` out of an id-keyed collection. |
|
|
179
|
+
| `mergeById` | function | Reconciles two id-keyed collections — an incoming-order upsert with base-only survivors appended after. |
|
|
180
|
+
| `definitionToEnvelope` | function | Projects a `Definition` to its scalar envelope — the kind's collections drop out. |
|
|
181
|
+
| `appendGroup` | function | Inserts `group` into a `QuantitativeDefinition`'s `groups` — dedup-then- insert at the end, or immediately after `target`. |
|
|
182
|
+
| `prependGroup` | function | Inserts `group` into a `QuantitativeDefinition`'s `groups` — dedup-then- insert at the start, or immediately before `target`. |
|
|
183
|
+
| `replaceGroup` | function | Swaps the group sharing `group.id` in a `QuantitativeDefinition` in place, preserving its position (appended when absent). |
|
|
184
|
+
| `removeGroup` | function | Removes every group sharing `id` from a `QuantitativeDefinition` (no-op when absent). |
|
|
185
|
+
| `appendFactor` | function | Inserts `factor` into a `FactorGroup`'s `factors` — dedup-then-insert at the end, or immediately after `target`. |
|
|
186
|
+
| `prependFactor` | function | Inserts `factor` into a `FactorGroup`'s `factors` — dedup-then-insert at the start, or immediately before `target`. |
|
|
187
|
+
| `replaceFactor` | function | Swaps the factor sharing `factor.id` in a `FactorGroup` in place, preserving its position (appended when absent). |
|
|
188
|
+
| `removeFactor` | function | Removes every factor sharing `id` from a `FactorGroup` (no-op when absent). |
|
|
189
|
+
| `appendRule` | function | Inserts a rule into a `LogicalDefinition`'s `rules` — dedup-then-insert at the end, or immediately after `target`. |
|
|
190
|
+
| `prependRule` | function | Inserts a rule into a `LogicalDefinition`'s `rules` — dedup-then-insert at the start, or immediately before `target`. |
|
|
191
|
+
| `replaceRule` | function | Swaps the rule sharing `rule.id` in a `LogicalDefinition` in place, preserving its position (appended when absent). |
|
|
192
|
+
| `removeRule` | function | Removes every rule sharing `id` from a `LogicalDefinition` (no-op when absent). |
|
|
193
|
+
| `appendEquation` | function | Inserts an equation into a `SymbolicDefinition`'s `equations` — dedup- then-insert at the end, or immediately after `target`. |
|
|
194
|
+
| `prependEquation` | function | Inserts an equation into a `SymbolicDefinition`'s `equations` — dedup- then-insert at the start, or immediately before `target`. |
|
|
195
|
+
| `replaceEquation` | function | Swaps the equation sharing `equation.id` in a `SymbolicDefinition` in place, preserving its position (appended when absent). |
|
|
196
|
+
| `removeEquation` | function | Removes every equation sharing `id` from a `SymbolicDefinition` (no-op when absent). |
|
|
197
|
+
| `addVariable` | function | Upserts one entry of a `SymbolicDefinition`'s `variables`. |
|
|
198
|
+
| `removeVariable` | function | Removes one entry of a `SymbolicDefinition`'s `variables`. |
|
|
199
|
+
| `appendFact` | function | Inserts a fact into an `InferentialDefinition`'s `facts` — dedup-then- insert at the end, or immediately after `target`. |
|
|
200
|
+
| `prependFact` | function | Inserts a fact into an `InferentialDefinition`'s `facts` — dedup-then- insert at the start, or immediately before `target`. |
|
|
201
|
+
| `replaceFact` | function | Swaps the fact sharing `fact.id` in an `InferentialDefinition` in place, preserving its position (appended when absent). |
|
|
202
|
+
| `removeFact` | function | Removes every fact sharing `id` from an `InferentialDefinition` (no-op when absent). |
|
|
203
|
+
| `appendInference` | function | Inserts an inference into an `InferentialDefinition`'s `inferences` — dedup-then-insert at the end, or immediately after `target`. |
|
|
204
|
+
| `prependInference` | function | Inserts an inference into an `InferentialDefinition`'s `inferences` — dedup-then-insert at the start, or immediately before `target`. |
|
|
205
|
+
| `replaceInference` | function | Swaps the inference sharing `inference.id` in an `InferentialDefinition` in place, preserving its position (appended when absent). |
|
|
206
|
+
| `removeInference` | function | Removes every inference sharing `id` from an `InferentialDefinition` (no-op when absent). |
|
|
207
|
+
| `mergeQuantitativeDefinition` | function | Reconciles two `QuantitativeDefinition`s onto `base`'s id. |
|
|
208
|
+
| `mergeLogicalDefinition` | function | Reconciles two `LogicalDefinition`s onto `base`'s id. |
|
|
209
|
+
| `mergeSymbolicDefinition` | function | Reconciles two `SymbolicDefinition`s onto `base`'s id. |
|
|
210
|
+
| `mergeInferentialDefinition` | function | Reconciles two `InferentialDefinition`s onto `base`'s id. |
|
|
211
|
+
| `clearQuantitativeDefinition` | function | Deletes one optional field of a `QuantitativeDefinition`. |
|
|
212
|
+
| `clearLogicalDefinition` | function | Deletes one optional field of a `LogicalDefinition`. |
|
|
213
|
+
| `clearSymbolicDefinition` | function | Deletes one optional field of a `SymbolicDefinition`. |
|
|
214
|
+
| `clearInferentialDefinition` | function | Deletes one optional field of an `InferentialDefinition`. |
|
|
215
|
+
| `parseDefinition` | function | Parses a JSON string into a `Definition`, failing safe to `undefined`. |
|
|
216
|
+
| `assignField` | function | Upserts one field of a `Subject` — copy-on-write spread. |
|
|
217
|
+
| `removeField` | function | Deletes one field of a `Subject` — destructure-rest omit. |
|
|
218
|
+
| `mergeSubjects` | function | Reconciles two `Subject`s — incoming-wins spread, with the base `id` preserved when present. |
|
|
219
|
+
| `repeatSubject` | function | Produces `count` deterministic clones of a `Subject`. |
|
|
220
|
+
|
|
221
|
+
### Validators
|
|
222
|
+
|
|
223
|
+
Total guards return `false`, never throw, for adversarial input such as junk, cycles, and hostile prototypes. Input guards compose the [contracts](contract.md) combinators. Result guards use bespoke open member checks and retain those combinators for nested values. Exactness follows who produces the value, not who declares the type: caller-supplied input records are exact, while package or foreign-interface result records are open to extra members. Numeric input fields guard with `isFiniteNumber` (JSON cannot carry `NaN` / `±Infinity`); the recursive input shapes recurse through `lazyOf`.
|
|
224
|
+
|
|
225
|
+
In a guard table a `Shape` cell holds the type the guard narrows to.
|
|
226
|
+
|
|
227
|
+
| API | Kind | Shape | Summary |
|
|
228
|
+
| -------------------------- | -------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
229
|
+
| `isReasoning` | const | `Reasoning` | Determines whether a value is a `Reasoning` literal. |
|
|
230
|
+
| `isChainingStrategy` | const | `ChainingStrategy` | Determines whether a value is a `ChainingStrategy` literal. |
|
|
231
|
+
| `isMathOperation` | const | `MathOperation` | Determines whether a value is a `MathOperation` literal. |
|
|
232
|
+
| `isAggregation` | const | `Aggregation` | Determines whether a value is an `Aggregation` literal. |
|
|
233
|
+
| `isComparison` | const | `Comparison` | Determines whether a value is a `Comparison` literal. |
|
|
234
|
+
| `isLogicalOperator` | const | `LogicalOperator` | Determines whether a value is a `LogicalOperator` literal. |
|
|
235
|
+
| `isFieldPath` | const | `FieldPath` | Determines whether a value is a `FieldPath` — a single string key or an array of keys descending into nested objects. |
|
|
236
|
+
| `isNumberRecord` | const | `Readonly<Record<string, number>>` | Determines whether a value is a record whose every value is a finite number — the shape of a `LookupSource.table` and a `SymbolicDefinition.variables`. |
|
|
237
|
+
| `isCheck` | function | `Check` | Determines whether a value is a `Check` — a field / operator / value predicate. |
|
|
238
|
+
| `isTransform` | function | `Transform` | Determines whether a value is a `Transform` — one math step. |
|
|
239
|
+
| `isBounds` | function | `Bounds` | Determines whether a value is a `Bounds` — an inclusive numeric clamp. |
|
|
240
|
+
| `isFactorRange` | function | `FactorRange` | Determines whether a value is a `FactorRange` — one band of a range source. |
|
|
241
|
+
| `isSource` | function | `Source` | Determines whether a value is a `Source` — a static, field, lookup, or range factor source, discriminated by `origin`. |
|
|
242
|
+
| `isFactor` | function | `Factor` | Determines whether a value is a `Factor` — one scored input of a quantitative group. |
|
|
243
|
+
| `isFactorGroup` | function | `FactorGroup` | Determines whether a value is a `FactorGroup` — a group of factors aggregated into one value. |
|
|
244
|
+
| `isExpression` | function | `Expression` | Determines whether a value is an `Expression` — a boolean expression tree of atoms and compounds, discriminated by `form`. |
|
|
245
|
+
| `isRule` | function | `Rule` | Determines whether a value is a `Rule` — premises and a conclusion. |
|
|
246
|
+
| `isSymbolicExpression` | function | `SymbolicExpression` | Determines whether a value is a `SymbolicExpression` — an algebraic expression tree of variables, constants, and operations, discriminated by `form`. |
|
|
247
|
+
| `isEquation` | function | `Equation` | Determines whether a value is an `Equation` — `left = right`, solved for `target`. |
|
|
248
|
+
| `isFact` | function | `Fact` | Determines whether a value is a `Fact` — a predicate over positional terms. |
|
|
249
|
+
| `isInference` | function | `Inference` | Determines whether a value is an `Inference` — premise patterns and a conclusion pattern. |
|
|
250
|
+
| `isQuantitativeDefinition` | function | `QuantitativeDefinition` | Determines whether a value is a `QuantitativeDefinition`. |
|
|
251
|
+
| `isLogicalDefinition` | function | `LogicalDefinition` | Determines whether a value is a `LogicalDefinition`. |
|
|
252
|
+
| `isSymbolicDefinition` | function | `SymbolicDefinition` | Determines whether a value is a `SymbolicDefinition`. |
|
|
253
|
+
| `isInferentialDefinition` | function | `InferentialDefinition` | Determines whether a value is an `InferentialDefinition`. |
|
|
254
|
+
| `isDefinition` | function | `Definition` | Determines whether a value is a `Definition` — a quantitative, logical, symbolic, or inferential definition shape, discriminated by `reasoning`. |
|
|
255
|
+
| `isQuantitativeClearKey` | const | `QuantitativeClearKey` | Determines whether a value is a `QuantitativeClearKey` — an optional field `clearQuantitativeDefinition` can delete. |
|
|
256
|
+
| `isLogicalClearKey` | const | `LogicalClearKey` | Determines whether a value is a `LogicalClearKey` — an optional field `clearLogicalDefinition` can delete. |
|
|
257
|
+
| `isSymbolicClearKey` | const | `SymbolicClearKey` | Determines whether a value is a `SymbolicClearKey` — an optional field `clearSymbolicDefinition` can delete. |
|
|
258
|
+
| `isInferentialClearKey` | const | `InferentialClearKey` | Determines whether a value is an `InferentialClearKey` — an optional field `clearInferentialDefinition` can delete. |
|
|
259
|
+
| `isResultFact` | function | `Fact` | Determines whether a value is a result-side `Fact`. |
|
|
260
|
+
| `isCheckResult` | function | `CheckResult` | Determines whether a value is a `CheckResult`. |
|
|
261
|
+
| `isFactorResult` | function | `FactorResult` | Determines whether a value is a `FactorResult`. |
|
|
262
|
+
| `isGroupResult` | function | `GroupResult` | Determines whether a value is a `GroupResult`. |
|
|
263
|
+
| `isRuleResult` | function | `RuleResult` | Determines whether a value is a `RuleResult`. |
|
|
264
|
+
| `isProofNode` | function | `ProofNode` | Determines whether a value is a depth-bounded, acyclic `ProofNode` tree. |
|
|
265
|
+
| `isQuantitativeResult` | function | `QuantitativeResult` | Determines whether a value is a `QuantitativeResult`. |
|
|
266
|
+
| `isLogicalResult` | function | `LogicalResult` | Determines whether a value is a `LogicalResult`. |
|
|
267
|
+
| `isSymbolicResult` | function | `SymbolicResult` | Determines whether a value is a `SymbolicResult`. |
|
|
268
|
+
| `isInferentialResult` | function | `InferentialResult` | Determines whether a value is an `InferentialResult`. |
|
|
269
|
+
| `isReasonResult` | function | `ReasonResult` | Determines whether a value is any `ReasonResult` arm. |
|
|
270
|
+
| `isReasonValidationResult` | function | `ReasonValidationResult` | Determines whether a value is a `ReasonValidationResult`. |
|
|
271
|
+
| `isDefinitionBuilder` | function | `DefinitionBuilderInterface` | Determines whether a value is a `DefinitionBuilder` entity — the brand-guarded stateful workspace, not the plain `Definition` data union. |
|
|
272
|
+
| `isSubjectBuilder` | function | `SubjectBuilderInterface` | Determines whether a value is a `SubjectBuilder` entity — the brand-guarded stateful workspace, not the plain `Subject` data record. |
|
|
273
|
+
|
|
274
|
+
### Errors
|
|
275
|
+
|
|
276
|
+
| API | Kind | Summary |
|
|
277
|
+
| --------------- | -------- | --------------------------------------------------- |
|
|
278
|
+
| `ReasonError` | class | Represents an error thrown by the reasons layer. |
|
|
279
|
+
| `isReasonError` | function | Narrows an unknown caught value to a `ReasonError`. |
|
|
280
|
+
|
|
281
|
+
### Constants
|
|
282
|
+
|
|
283
|
+
A `Shape` cell holds the constant's declared type.
|
|
284
|
+
|
|
285
|
+
| API | Kind | Shape | Summary |
|
|
286
|
+
| -------------------------- | ----- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
287
|
+
| `DEFAULT_REASON_BAIL` | const | `boolean` | Holds the default `bail` for the `Reason` orchestrator — a reasoner throw is rethrown after the `error` emit. Default: `true`. |
|
|
288
|
+
| `DEFAULT_VALIDATE` | const | `boolean` | Holds the default `validate` for the `Reason` orchestrator — per-call validation is skipped. Default: `false`. |
|
|
289
|
+
| `DEFAULT_DEPTH` | const | `number` | Holds the default `depth` for chaining definitions — the forward-iteration / backward-recursion cap of the logical and inferential reasoners. Default: `10`. |
|
|
290
|
+
| `DEFAULT_BASE` | const | `number` | Holds the default `base` added before aggregation, at both group and definition level. Default: `0`. |
|
|
291
|
+
| `DEFAULT_PRECISION` | const | `number` | Holds the default `precision` (decimal places) for quantitative values and symbolic solutions. Default: `4`. |
|
|
292
|
+
| `DEFAULT_CONFIDENCE` | const | `number` | Holds the default `confidence` for facts, inferences, and injected subject facts. Default: `1`. |
|
|
293
|
+
| `DEFAULT_WEIGHT` | const | `number` | Holds the default factor `weight` at group aggregation. Default: `1`. |
|
|
294
|
+
| `DEFAULT_PRIORITY` | const | `number` | Holds the default factor / rule `priority` — evaluation order is ascending and stable. Default: `0`. |
|
|
295
|
+
| `CONFIDENCE_PRECISION` | const | `number` | Holds the decimal places a derived fact's confidence is rounded to during forward inferential chaining. |
|
|
296
|
+
| `INVERTIBLE_OPERATIONS` | const | `ReadonlySet<MathOperation>` | Lists the math operations the symbolic reasoner can invert while isolating a target variable — anything else (a `power`, an `abs`) fails the equation with a non-invertible error. |
|
|
297
|
+
| `EVALUATOR_ID` | const | `string` | Names the default `id` for an `Evaluator`. |
|
|
298
|
+
| `TRANSFORMER_ID` | const | `string` | Names the default `id` for a `Transformer`. |
|
|
299
|
+
| `AGGREGATOR_ID` | const | `string` | Names the default `id` for an `Aggregator`. |
|
|
300
|
+
| `QUANTITATIVE_ID` | const | `string` | Names the default `id` for a `QuantitativeReasoner`. |
|
|
301
|
+
| `LOGICAL_ID` | const | `string` | Names the default `id` for a `LogicalReasoner`. |
|
|
302
|
+
| `SYMBOLIC_ID` | const | `string` | Names the default `id` for a `SymbolicReasoner`. |
|
|
303
|
+
| `INFERENTIAL_ID` | const | `string` | Names the default `id` for an `InferentialReasoner`. |
|
|
304
|
+
| `DEFINITION_BUILDER_BRAND` | const | `unique symbol` | Holds the `DefinitionBuilder` entity brand — a `unique symbol` key carrying `readonly true` on every `DefinitionBuilderInterface` instance. |
|
|
305
|
+
| `SUBJECT_BUILDER_BRAND` | const | `unique symbol` | Holds the `SubjectBuilder` entity brand — a `unique symbol` key carrying `readonly true` on every `SubjectBuilderInterface` instance. |
|
|
306
|
+
|
|
307
|
+
### Types
|
|
308
|
+
|
|
309
|
+
A `Shape` cell holds an interface's data members as bare names in braces, `?` marking an optional member and `plus` introducing its call-signature members, and a type alias's own type literal with a union's arms escaped as `\|`.
|
|
310
|
+
|
|
311
|
+
| Type | Kind | Shape | Summary |
|
|
312
|
+
| ----------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
313
|
+
| `Reasoning` | type | `'quantitative' \| 'logical' \| 'symbolic' \| 'inferential'` | Names the reasoning strategies — the axis a `Definition` and a `ReasonResult` discriminate on. |
|
|
314
|
+
| `ChainingStrategy` | type | `'forward' \| 'backward'` | Names how a chaining reasoner walks its rules: `forward` (data-driven fixpoint) or `backward` (goal-driven proving). |
|
|
315
|
+
| `MathOperation` | type | `'add' \| 'subtract' \| 'multiply' \| 'divide' \| 'percentage' \| 'minimum' \| 'maximum' \| 'average' \| 'power' \| 'round' \| 'ceil' \| 'floor' \| 'abs'` | Names a math operation applied by the `TransformerInterface` and inside `SymbolicExpression` trees. |
|
|
316
|
+
| `Aggregation` | type | `'sum' \| 'product' \| 'average' \| 'minimum' \| 'maximum'` | Names how the `AggregatorInterface` reduces a list of numbers to one. |
|
|
317
|
+
| `Comparison` | type | `'equals' \| 'not' \| 'above' \| 'below' \| 'from' \| 'to' \| 'any' \| 'none' \| 'between' \| 'outside'` | Names the comparison a `Check` applies between a resolved subject field and its expected value. |
|
|
318
|
+
| `LogicalOperator` | type | `'and' \| 'or' \| 'not' \| 'implies' \| 'xor'` | Names a logical connective inside a compound `Expression`. |
|
|
319
|
+
| `Subject` | type | `Readonly<Record<string, unknown>>` | Represents the data record being reasoned about — a plain readonly bag of fields, read by `FieldPath`. |
|
|
320
|
+
| `Check` | interface | `{ field, operator, value }` | Represents a single field predicate: resolves `field` from the subject and compares it to `value` with `operator`. |
|
|
321
|
+
| `CheckResult` | interface | `{ field, met, actual, error? }` | Represents the outcome of one `Check` evaluation. |
|
|
322
|
+
| `Transform` | interface | `{ operation, operand? }` | Represents one math step applied to a number by the `TransformerInterface`. |
|
|
323
|
+
| `Bounds` | interface | `{ minimum?, maximum? }` | Represents an inclusive numeric clamp — either side may be absent (unbounded). |
|
|
324
|
+
| `StaticSource` | interface | `{ origin, value }` | Represents a factor source yielding a fixed number. |
|
|
325
|
+
| `FieldSource` | interface | `{ origin, field }` | Represents a factor source reading a subject field as a number. |
|
|
326
|
+
| `LookupSource` | interface | `{ origin, field, table }` | Represents a factor source mapping a subject field through a lookup table. |
|
|
327
|
+
| `RangeSource` | interface | `{ origin, field, ranges }` | Represents a factor source banding a numeric subject field through ordered ranges. |
|
|
328
|
+
| `Source` | type | `StaticSource \| FieldSource \| LookupSource \| RangeSource` | Represents a static, field, lookup, or range factor source, discriminated by `origin`. |
|
|
329
|
+
| `FactorRange` | interface | `{ bounds?, value }` | Represents one band of a `RangeSource` — an optional inclusive bounds test and the value it yields. |
|
|
330
|
+
| `Factor` | interface | `{ id, name, description?, source, fallback?, checks?, transforms?, bounds?, weight?, priority?, enabled?, required? }` | Represents one scored input of a quantitative group. |
|
|
331
|
+
| `FactorGroup` | interface | `{ id, name, description?, factors, aggregation, base?, bounds?, enabled?, strict? }` | Represents a group of factors aggregated into one value. |
|
|
332
|
+
| `QuantitativeDefinition` | interface | `{ reasoning, id, name, description?, groups, aggregation, base?, bounds?, precision? }` | Defines factor-based numeric scoring. |
|
|
333
|
+
| `Atom` | interface | `{ form, check }` | Represents a leaf boolean expression — one `Check` against the subject. |
|
|
334
|
+
| `Compound` | interface | `{ form, operator, operands }` | Represents a compound boolean expression — a `LogicalOperator` over nested operands. |
|
|
335
|
+
| `Expression` | type | `Atom \| Compound` | Represents a boolean expression tree, discriminated by `form`. |
|
|
336
|
+
| `Rule` | interface | `{ id, name, description?, premises, conclusion, priority?, enabled? }` | Represents one deduction rule: when every premise holds, the `conclusion`'s atoms are asserted as derived facts. |
|
|
337
|
+
| `LogicalDefinition` | interface | `{ reasoning, id, name, description?, rules, strategy, depth? }` | Defines rule-based deduction. |
|
|
338
|
+
| `Variable` | interface | `{ form, name }` | Represents a symbolic expression leaf naming a variable. |
|
|
339
|
+
| `Constant` | interface | `{ form, value }` | Represents a symbolic expression leaf holding a fixed number. |
|
|
340
|
+
| `Operation` | interface | `{ form, operator, left, right? }` | Represents a symbolic operation node. |
|
|
341
|
+
| `SymbolicExpression` | type | `Variable \| Constant \| Operation` | Represents an algebraic expression tree, discriminated by `form`. |
|
|
342
|
+
| `Equation` | interface | `{ id, name, description?, left, right, target }` | Represents one equation `left = right`, solved for the `target` variable. |
|
|
343
|
+
| `SymbolicDefinition` | interface | `{ reasoning, id, name, description?, equations, variables, precision? }` | Defines equation-solving. |
|
|
344
|
+
| `Fact` | interface | `{ id, predicate, terms, confidence? }` | Represents one fact: a `predicate` over positional `terms`. |
|
|
345
|
+
| `Inference` | interface | `{ id, name, description?, premises, conclusion, confidence?, enabled? }` | Represents one inference rule: when every premise pattern unifies against known facts (with consistent variable bindings), the instantiated `conclusion` is derived. |
|
|
346
|
+
| `InferentialDefinition` | interface | `{ reasoning, id, name, description?, inferences, facts, strategy, depth? }` | Defines fact-derivation. |
|
|
347
|
+
| `Definition` | type | `QuantitativeDefinition \| LogicalDefinition \| SymbolicDefinition \| InferentialDefinition` | Represents any reasoning definition, discriminated by `reasoning`. |
|
|
348
|
+
| `DefinitionEnvelope` | type | `Omit<QuantitativeDefinition, 'groups'> \| Omit<LogicalDefinition, 'rules'> \| Omit<SymbolicDefinition, 'equations' \| 'variables'> \| Omit<InferentialDefinition, 'facts' \| 'inferences'>` | Represents the scalar-only projection of each definition kind — the `DefinitionBuilderInterface` implementation's private envelope holds the non-collection fields; `build()` re-composes the kind's collections from the managers' plural accessors. |
|
|
349
|
+
| `QuantitativeClearKey` | type | `'description' \| 'base' \| 'bounds' \| 'precision'` | Names the optional `QuantitativeDefinition` fields `clearQuantitativeDefinition` (and a quantitative `DefinitionBuilderInterface`'s `clear`) can delete. |
|
|
350
|
+
| `LogicalClearKey` | type | `'description' \| 'depth'` | Names the optional `LogicalDefinition` fields `clearLogicalDefinition` (and a logical `DefinitionBuilderInterface`'s `clear`) can delete. |
|
|
351
|
+
| `SymbolicClearKey` | type | `'description' \| 'precision'` | Names the optional `SymbolicDefinition` fields `clearSymbolicDefinition` (and a symbolic `DefinitionBuilderInterface`'s `clear`) can delete. |
|
|
352
|
+
| `InferentialClearKey` | type | `'description' \| 'depth'` | Names the optional `InferentialDefinition` fields `clearInferentialDefinition` (and an inferential `DefinitionBuilderInterface`'s `clear`) can delete. |
|
|
353
|
+
| `LogicalChainingResult` | interface | `{ conclusion, rules }` | Represents one chaining pass of the `LogicalReasoner` — the overall `conclusion` plus the per-rule results the pass produced. |
|
|
354
|
+
| `InferentialChainingResult` | interface | `{ derived, proof? }` | Represents one chaining pass of the `InferentialReasoner` — the facts the pass derived plus the proof tree, when the pass produced one. |
|
|
355
|
+
| `FactorResult` | interface | `{ id, applied, value, raw?, checks? }` | Represents one factor's evaluation outcome. |
|
|
356
|
+
| `GroupResult` | interface | `{ id, applied, value, factors }` | Represents one group's evaluation outcome — its clamped value and the per-factor results (disabled factors omitted entirely). |
|
|
357
|
+
| `QuantitativeResult` | interface | `{ reasoning, value, groups, count, success, trace, errors }` | Represents the outcome of quantitative reasoning. |
|
|
358
|
+
| `RuleResult` | interface | `{ id, applied, premises }` | Represents one rule's evaluation outcome. |
|
|
359
|
+
| `LogicalResult` | interface | `{ reasoning, conclusion, rules, count, success, trace, errors }` | Represents the outcome of logical reasoning. |
|
|
360
|
+
| `SymbolicResult` | interface | `{ reasoning, solutions, success, trace, errors }` | Represents the outcome of symbolic reasoning — final bindings keyed by each equation's `target` (a failed equation's target still appears when bound elsewhere). |
|
|
361
|
+
| `ProofNode` | interface | `{ fact, inference?, children?, depth }` | Represents one node of a backward-chaining proof tree. |
|
|
362
|
+
| `InferentialResult` | interface | `{ reasoning, derived, proof?, success, trace, errors }` | Represents the outcome of inferential reasoning. |
|
|
363
|
+
| `ReasonResult` | type | `QuantitativeResult \| LogicalResult \| SymbolicResult \| InferentialResult` | Represents any reasoning result, discriminated by `reasoning`. |
|
|
364
|
+
| `ReasonValidationResult` | interface | `{ valid, errors, warnings }` | Represents the outcome of validating a definition — hard `errors` (definition unusable) and soft `warnings` (suspicious but runnable). `valid` is `true` exactly when `errors` is empty. |
|
|
365
|
+
| `EvaluatorOptions` | interface | `{ id? }` | Configures `createEvaluator` / the `Evaluator` constructor. |
|
|
366
|
+
| `TransformerOptions` | interface | `{ id? }` | Configures `createTransformer` / the `Transformer` constructor. |
|
|
367
|
+
| `AggregatorOptions` | interface | `{ id? }` | Configures `createAggregator` / the `Aggregator` constructor. |
|
|
368
|
+
| `QuantitativeReasonerOptions` | interface | `{ id?, evaluator?, transformer?, aggregator? }` | Configures `createQuantitativeReasoner` / the `QuantitativeReasoner` constructor. |
|
|
369
|
+
| `LogicalReasonerOptions` | interface | `{ id?, evaluator? }` | Configures `createLogicalReasoner` / the `LogicalReasoner` constructor. |
|
|
370
|
+
| `SymbolicReasonerOptions` | interface | `{ id? }` | Configures `createSymbolicReasoner` / the `SymbolicReasoner` constructor. |
|
|
371
|
+
| `InferentialReasonerOptions` | interface | `{ id? }` | Configures `createInferentialReasoner` / the `InferentialReasoner` constructor. |
|
|
372
|
+
| `EvaluatorInterface` | interface | `{ id } plus evaluate, batch` | Evaluates `Check`s against subjects. |
|
|
373
|
+
| `TransformerInterface` | interface | `{ id } plus apply, chain` | Applies math `Transform`s to numbers. |
|
|
374
|
+
| `AggregatorInterface` | interface | `{ id } plus aggregate` | Reduces number lists to one number per `Aggregation`. |
|
|
375
|
+
| `ReasonerInterface` | interface | `{ id, reasoning } plus supports, validate, reason` | Declares a reasoning strategy adapter — one per `Reasoning`. |
|
|
376
|
+
| `ReasonErrorCode` | type | `'MISSING' \| 'INVALID' \| 'MISMATCH' \| 'DESTROYED' \| 'TARGET' \| 'OPERATOR'` | Names a machine-readable `ReasonError` code. |
|
|
377
|
+
| `ReasonEventMap` | type | `{ register, reason, error, destroy }` | Represents the push observation surface of a `ReasonInterface`. |
|
|
378
|
+
| `ReasonOptions` | interface | `{ reasoners?, bail?, validate?, on?, error? }` | Configures `createReason` / the `Reason` constructor. |
|
|
379
|
+
| `ReasonInterface` | interface | `{ emitter } plus reason, register, reasoner, reasoners, supports, validate, destroy` | Declares the reasoning orchestrator — a thin router over registered `ReasonerInterface`s. |
|
|
380
|
+
| `GroupManagerInterface` | interface | `{ emitter } plus group, groups, append, prepend, replace, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over a quantitative definition's `groups` — a self-owning, kind-free collection manager. |
|
|
381
|
+
| `GroupManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of a `GroupManagerInterface`. |
|
|
382
|
+
| `GroupManagerOptions` | interface | `{ groups?, on?, error? }` | Configures `createGroupManager` / the `GroupManager` constructor. |
|
|
383
|
+
| `FactorManagerInterface` | interface | `{ emitter } plus factor, factors, append, prepend, replace, remove, destroy` | Declares the `DefinitionBuilderInterface` manager over a `FactorGroup`'s `factors`, threaded through the required `groupId` locator (a factor lives inside its group). |
|
|
384
|
+
| `FactorManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of a `FactorManagerInterface`. |
|
|
385
|
+
| `FactorManagerOptions` | interface | `{ on?, error? }` | Configures `createFactorManager` / the `FactorManager` constructor. |
|
|
386
|
+
| `RuleManagerInterface` | interface | `{ emitter } plus rule, rules, append, prepend, replace, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over a logical definition's `rules` — a self-owning, kind-free collection manager. |
|
|
387
|
+
| `RuleManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of a `RuleManagerInterface`. |
|
|
388
|
+
| `RuleManagerOptions` | interface | `{ rules?, on?, error? }` | Configures `createRuleManager` / the `RuleManager` constructor. |
|
|
389
|
+
| `EquationManagerInterface` | interface | `{ emitter } plus equation, equations, append, prepend, replace, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over a symbolic definition's `equations` — a self-owning, kind-free collection manager. |
|
|
390
|
+
| `EquationManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of an `EquationManagerInterface`. |
|
|
391
|
+
| `EquationManagerOptions` | interface | `{ equations?, on?, error? }` | Configures `createEquationManager` / the `EquationManager` constructor. |
|
|
392
|
+
| `FactManagerInterface` | interface | `{ emitter } plus fact, facts, append, prepend, replace, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over an inferential definition's `facts` — a self-owning, kind-free collection manager. |
|
|
393
|
+
| `FactManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of a `FactManagerInterface`. |
|
|
394
|
+
| `FactManagerOptions` | interface | `{ facts?, on?, error? }` | Configures `createFactManager` / the `FactManager` constructor. |
|
|
395
|
+
| `InferenceManagerInterface` | interface | `{ emitter } plus inference, inferences, append, prepend, replace, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over an inferential definition's `inferences` — a self-owning, kind-free collection manager. |
|
|
396
|
+
| `InferenceManagerEventMap` | type | `{ append, prepend, replace, remove, destroy }` | Represents the push observation surface of an `InferenceManagerInterface`. |
|
|
397
|
+
| `InferenceManagerOptions` | interface | `{ inferences?, on?, error? }` | Configures `createInferenceManager` / the `InferenceManager` constructor. |
|
|
398
|
+
| `VariableManagerInterface` | interface | `{ emitter } plus variable, variables, add, remove, seat, destroy` | Declares the `DefinitionBuilderInterface` manager over a symbolic definition's `variables` — a name-keyed unordered record, so `add` / `remove` are the only write verbs (no placement). A self-owning, kind-free manager. |
|
|
399
|
+
| `VariableManagerEventMap` | type | `{ add, remove, destroy }` | Represents the push observation surface of a `VariableManagerInterface`. |
|
|
400
|
+
| `VariableManagerOptions` | interface | `{ variables?, on?, error? }` | Configures `createVariableManager` / the `VariableManager` constructor. |
|
|
401
|
+
| `DefinitionBuilderEventMap` | type | `{ merge, clear, destroy }` | Represents the push observation surface of a `DefinitionBuilderInterface` — the builder-level lifecycle events; per-element mutation events live on the individual managers' own emitters. |
|
|
402
|
+
| `DefinitionBuilderInterface` | interface | `{ [DEFINITION_BUILDER_BRAND], id, reasoning, emitter, groups, factors, rules, equations, variables, facts, inferences } plus build, merge, clear, destroy` | Declares a stateful workspace builder accumulating a `Definition` through always-present self-owning manager properties: a private scalar envelope plus one manager per collection. |
|
|
403
|
+
| `DefinitionBuilderOptions` | interface | `{ id?, groups?, factors?, rules?, equations?, variables?, facts?, inferences?, on?, error? }` | Configures `createDefinitionBuilder` / the `DefinitionBuilder` constructor. |
|
|
404
|
+
| `SubjectBuilderEventMap` | type | `{ set, remove, merge, clear, destroy }` | Represents the push observation surface of a `SubjectBuilderInterface` — the verb-named `set`, `remove`, `merge`, `clear`, and `destroy` events, no generic `change` / `status`. |
|
|
405
|
+
| `SubjectBuilderInterface` | interface | `{ [SUBJECT_BUILDER_BRAND], id, emitter } plus field, fields, set, remove, merge, clear, repeat, build, destroy` | Declares a stateful workspace builder accumulating a `Subject` — one flat key-value collection, no managers. |
|
|
406
|
+
| `SubjectBuilderOptions` | interface | `{ id?, on?, error? }` | Configures `createSubjectBuilder` / the `SubjectBuilder` constructor. |
|
|
407
|
+
|
|
408
|
+
## Methods
|
|
409
|
+
|
|
410
|
+
The public methods of each behavioral interface — one table per type, keyed by its backticked name, every call-signature member listed (the `readonly` data members — `emitter` on the orchestrator, the builders, and every manager; `id` / `reasoning` on reasoners and operators — stay off the method tables). Each implementing class (`Reason`; the reasoners; `Evaluator` / `Transformer` / `Aggregator`; the `DefinitionBuilder` / `SubjectBuilder` builders and the manager classes) exposes exactly its interface's methods, so this doubles as the per-instance method surface.
|
|
411
|
+
|
|
412
|
+
#### `ReasonInterface`
|
|
413
|
+
|
|
414
|
+
The array overload of `reason` is declared first so a subject list resolves to the batch form. After `destroy()`, every method except `destroy` itself throws `DESTROYED` (the `emitter` getter keeps working). `reason` and `validate` take plain data only — a `DefinitionBuilderInterface` / `SubjectBuilderInterface`'s `build()` output is passed instead, by the caller.
|
|
415
|
+
|
|
416
|
+
| Method | Returns | Summary |
|
|
417
|
+
| ----------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
418
|
+
| `reason` | `ReasonResult` (or array) | Dispatches one subject — or maps a subject array in order — to the registered reasoner. |
|
|
419
|
+
| `register` | `void` | Registers a reasoner, replacing one already registered for the same reasoning, and emits the `register` event. |
|
|
420
|
+
| `reasoner` | `ReasonerInterface \| undefined` | Returns the one reasoner registered for a reasoning, or `undefined` when none is. |
|
|
421
|
+
| `reasoners` | `readonly ReasonerInterface[]` | Lists every registered reasoner as a fresh array. |
|
|
422
|
+
| `supports` | `boolean` | Reports whether a reasoner is registered for a reasoning. |
|
|
423
|
+
| `validate` | `ReasonValidationResult` | Delegates validation to the registered reasoner — a missing reasoner is an invalid result here rather than a throw. |
|
|
424
|
+
| `destroy` | `void` | Clears the registry, emits the `destroy` event, and destroys the emitter last; the call is idempotent. |
|
|
425
|
+
|
|
426
|
+
#### `ReasonerInterface`
|
|
427
|
+
|
|
428
|
+
`supports` / `validate` / `reason` take plain data only — a `DefinitionBuilderInterface` / `SubjectBuilderInterface`'s `build()` output is passed instead, by the caller.
|
|
429
|
+
|
|
430
|
+
| Method | Returns | Summary |
|
|
431
|
+
| ---------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
432
|
+
| `supports` | `boolean` | Reports whether the definition's `reasoning` equals this adapter's own. |
|
|
433
|
+
| `validate` | `ReasonValidationResult` | Reports a definition's structural errors and soft warnings, evaluating nothing. |
|
|
434
|
+
| `reason` | `ReasonResult` | Evaluates one subject against a definition, throwing only `MISMATCH` for a wrong reasoning — a malformed definition yields a failure result. |
|
|
435
|
+
|
|
436
|
+
#### `EvaluatorInterface`
|
|
437
|
+
|
|
438
|
+
| Method | Returns | Summary |
|
|
439
|
+
| ---------- | ------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
440
|
+
| `evaluate` | `CheckResult` | Resolves `check.field` from the subject and compares it; an unknown operator becomes an in-result `error`. |
|
|
441
|
+
| `batch` | `readonly CheckResult[]` | Evaluates many checks positionally against one subject. |
|
|
442
|
+
|
|
443
|
+
#### `TransformerInterface`
|
|
444
|
+
|
|
445
|
+
| Method | Returns | Summary |
|
|
446
|
+
| ------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
447
|
+
| `apply` | `number` | Applies one math step — an absent operand defaults to `1` for `multiply` / `divide` / `power`, and to `0` otherwise. |
|
|
448
|
+
| `chain` | `number` | Left-folds a transform list over the value; `NaN` flows through and no step is skipped. |
|
|
449
|
+
|
|
450
|
+
#### `AggregatorInterface`
|
|
451
|
+
|
|
452
|
+
| Method | Returns | Summary |
|
|
453
|
+
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
454
|
+
| `aggregate` | `number` | Reduces the values per aggregation; `weights` are honored only on an exact length match, and `minimum` / `maximum` ignore them. |
|
|
455
|
+
|
|
456
|
+
#### `GroupManagerInterface`
|
|
457
|
+
|
|
458
|
+
The self-owning manager over a quantitative definition's `groups`. Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
459
|
+
|
|
460
|
+
| Method | Returns | Summary |
|
|
461
|
+
| --------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
462
|
+
| `group` | `FactorGroup \| undefined` | Returns the one group carrying an id, or `undefined` when none does. |
|
|
463
|
+
| `groups` | `readonly FactorGroup[]` | Lists every group in order. |
|
|
464
|
+
| `append` | `void` | Inserts a group at the end, or after `target`, removing a same-id group first; a `target` naming no group throws `TARGET`. |
|
|
465
|
+
| `prepend` | `void` | Inserts a group at the start, or before `target`, removing a same-id group first; a `target` naming no group throws `TARGET`. |
|
|
466
|
+
| `replace` | `void` | Swaps a same-id group in place, appending it when the collection carries none. |
|
|
467
|
+
| `remove` | `boolean \| void` | Removes groups: every group with no argument, one group by id, or the groups an id list names; the id forms report whether every named id existed. |
|
|
468
|
+
| `seat` | `void` | Replaces the whole collection in one silent call — the owning builder's bulk re-seat channel. |
|
|
469
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
470
|
+
|
|
471
|
+
#### `FactorManagerInterface`
|
|
472
|
+
|
|
473
|
+
The divergent manager over a `FactorGroup`'s `factors`, threaded through the required `groupId` locator — it holds no state of its own, reading and writing through the sibling `GroupManager`. `groupId` naming no existing group throws `TARGET` (with `groupId` in the context); a call after `destroy()` throws `DESTROYED`.
|
|
474
|
+
|
|
475
|
+
| Method | Returns | Summary |
|
|
476
|
+
| --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
477
|
+
| `factor` | `Factor \| undefined` | Returns the one factor of a named group carrying an id, or `undefined` when none does. |
|
|
478
|
+
| `factors` | `readonly Factor[]` | Lists every factor of one named group in order. |
|
|
479
|
+
| `append` | `void` | Inserts a factor into the named group at the end, or after `target`, removing a same-id factor first. |
|
|
480
|
+
| `prepend` | `void` | Inserts a factor into the named group at the start, or before `target`, removing a same-id factor first. |
|
|
481
|
+
| `replace` | `void` | Swaps a same-id factor in place within the named group, appending it when the group carries none. |
|
|
482
|
+
| `remove` | `boolean \| void` | Removes factors of the named group: every factor with the locator alone, one factor by a further id, or the factors a further id list names; the id forms report whether every named id existed. |
|
|
483
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
484
|
+
|
|
485
|
+
#### `RuleManagerInterface`
|
|
486
|
+
|
|
487
|
+
The self-owning manager over a logical definition's `rules`. Rule order is load-bearing — the forward conclusion is the last declared non-disabled rule. Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
488
|
+
|
|
489
|
+
| Method | Returns | Summary |
|
|
490
|
+
| --------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
491
|
+
| `rule` | `Rule \| undefined` | Returns the one rule carrying an id, or `undefined` when none does. |
|
|
492
|
+
| `rules` | `readonly Rule[]` | Lists every rule in order. |
|
|
493
|
+
| `append` | `void` | Inserts a rule at the end, or after `target`, removing a same-id rule first — an absent `target` makes it the new forward conclusion. |
|
|
494
|
+
| `prepend` | `void` | Inserts a rule at the start, or before `target`, removing a same-id rule first. |
|
|
495
|
+
| `replace` | `void` | Swaps a same-id rule in place, appending it when the collection carries none. |
|
|
496
|
+
| `remove` | `boolean \| void` | Removes rules: every rule with no argument, one rule by id, or the rules an id list names; the id forms report whether every named id existed. |
|
|
497
|
+
| `seat` | `void` | Replaces the whole collection in one silent call — the owning builder's bulk re-seat channel. |
|
|
498
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
499
|
+
|
|
500
|
+
#### `EquationManagerInterface`
|
|
501
|
+
|
|
502
|
+
The self-owning manager over a symbolic definition's `equations`. Equation order is strongly load-bearing — equations solve strictly in order and each rounded solution feeds forward. Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
503
|
+
|
|
504
|
+
| Method | Returns | Summary |
|
|
505
|
+
| ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
506
|
+
| `equation` | `Equation \| undefined` | Returns the one equation carrying an id, or `undefined` when none does. |
|
|
507
|
+
| `equations` | `readonly Equation[]` | Lists every equation in solve order. |
|
|
508
|
+
| `append` | `void` | Inserts an equation at the end, or after `target`, removing a same-id equation first. |
|
|
509
|
+
| `prepend` | `void` | Inserts an equation at the start, or before `target`, removing a same-id equation first. |
|
|
510
|
+
| `replace` | `void` | Swaps a same-id equation in place, appending it when the collection carries none. |
|
|
511
|
+
| `remove` | `boolean \| void` | Removes equations: every equation with no argument, one equation by id, or the equations an id list names; the id forms report whether every named id existed. |
|
|
512
|
+
| `seat` | `void` | Replaces the whole collection in one silent call — the owning builder's bulk re-seat channel. |
|
|
513
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
514
|
+
|
|
515
|
+
#### `FactManagerInterface`
|
|
516
|
+
|
|
517
|
+
The self-owning manager over an inferential definition's `facts`. Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
518
|
+
|
|
519
|
+
| Method | Returns | Summary |
|
|
520
|
+
| --------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
521
|
+
| `fact` | `Fact \| undefined` | Returns the one fact carrying an id, or `undefined` when none does. |
|
|
522
|
+
| `facts` | `readonly Fact[]` | Lists every fact in order. |
|
|
523
|
+
| `append` | `void` | Inserts a fact at the end, or after `target`, removing a same-id fact first. |
|
|
524
|
+
| `prepend` | `void` | Inserts a fact at the start, or before `target`, removing a same-id fact first. |
|
|
525
|
+
| `replace` | `void` | Swaps a same-id fact in place, appending it when the collection carries none. |
|
|
526
|
+
| `remove` | `boolean \| void` | Removes facts: every fact with no argument, one fact by id, or the facts an id list names; the id forms report whether every named id existed. |
|
|
527
|
+
| `seat` | `void` | Replaces the whole collection in one silent call — the owning builder's bulk re-seat channel. |
|
|
528
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
529
|
+
|
|
530
|
+
#### `InferenceManagerInterface`
|
|
531
|
+
|
|
532
|
+
The self-owning manager over an inferential definition's `inferences`. Inference order is load-bearing — backward proving iterates in declaration order and returns on first success. Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
533
|
+
|
|
534
|
+
| Method | Returns | Summary |
|
|
535
|
+
| ------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
536
|
+
| `inference` | `Inference \| undefined` | Returns the one inference carrying an id, or `undefined` when none does. |
|
|
537
|
+
| `inferences` | `readonly Inference[]` | Lists every inference in order. |
|
|
538
|
+
| `append` | `void` | Inserts an inference at the end, or after `target`, removing a same-id inference first. |
|
|
539
|
+
| `prepend` | `void` | Inserts an inference at the start, or before `target`, removing a same-id inference first. |
|
|
540
|
+
| `replace` | `void` | Swaps a same-id inference in place, appending it when the collection carries none. |
|
|
541
|
+
| `remove` | `boolean \| void` | Removes inferences: every inference with no argument, one inference by id, or the inferences an id list names; the id forms report whether every named id existed. |
|
|
542
|
+
| `seat` | `void` | Replaces the whole collection in one silent call — the owning builder's bulk re-seat channel. |
|
|
543
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
544
|
+
|
|
545
|
+
#### `VariableManagerInterface`
|
|
546
|
+
|
|
547
|
+
The self-owning manager over a symbolic definition's `variables` — a name-keyed unordered record, so `add` / `remove` are the only write verbs (no `append` / `prepend`). Managers are kind-free — an off-kind collection is ignored by `build()`, never a throw. A call after `destroy()` throws `DESTROYED`.
|
|
548
|
+
|
|
549
|
+
| Method | Returns | Summary |
|
|
550
|
+
| ----------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
551
|
+
| `variable` | `number \| undefined` | Returns the one variable's value carrying a name, or `undefined` when none does. |
|
|
552
|
+
| `variables` | `Readonly<Record<string, number>>` | Returns the whole name-keyed record. |
|
|
553
|
+
| `add` | `void` | Upserts one entry and emits the `add` event with the variable name. |
|
|
554
|
+
| `remove` | `boolean \| void` | Removes variables: every variable with no argument, one variable by name, or the variables a name list names; the name forms report whether every named variable existed. |
|
|
555
|
+
| `seat` | `void` | Replaces the whole record in one silent call — the owning builder's bulk re-seat channel. |
|
|
556
|
+
| `destroy` | `void` | Tears the manager down idempotently — emits the `destroy` event, then destroys the emitter last. |
|
|
557
|
+
|
|
558
|
+
#### `DefinitionBuilderInterface`
|
|
559
|
+
|
|
560
|
+
The `DEFINITION_BUILDER_BRAND`-carrying stateful builder accumulating a `Definition` through its self-owning manager properties (`groups` / `factors` / `rules` / `equations` / `variables` / `facts` / `inferences`, each listed earlier) plus a private scalar envelope. After `destroy()`, every method except `destroy` itself and the `emitter` / manager-property getters throws `DESTROYED`.
|
|
561
|
+
|
|
562
|
+
| Method | Returns | Summary |
|
|
563
|
+
| --------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
564
|
+
| `build` | `Definition` | Returns a fresh plain `Definition` snapshot — the envelope plus the kind's managers — total and deterministic on every call. |
|
|
565
|
+
| `merge` | `void` | Reconciles with an incoming plain `Definition` of the same `reasoning`, distributing scalars into the envelope and collections into the managers. |
|
|
566
|
+
| `clear` | `void` | Deletes one optional envelope field for the instance's `reasoning`; a key the reasoning cannot clear throws `MISMATCH`. |
|
|
567
|
+
| `destroy` | `void` | Tears the builder down idempotently — cascades `destroy` to every manager, then destroys the builder emitter last. |
|
|
568
|
+
|
|
569
|
+
#### `SubjectBuilderInterface`
|
|
570
|
+
|
|
571
|
+
The `SUBJECT_BUILDER_BRAND`-carrying stateful builder accumulating a `Subject`. The array overload of `remove` is declared first so a key list resolves to the batch form. After `destroy()`, every method except `destroy` itself and the `emitter` getter throws `DESTROYED`.
|
|
572
|
+
|
|
573
|
+
| Method | Returns | Summary |
|
|
574
|
+
| --------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
575
|
+
| `field` | `unknown` | Reads one top-level field by key. |
|
|
576
|
+
| `fields` | `Subject` | Reads the whole current record live. |
|
|
577
|
+
| `set` | `void` | Upserts one field; setting `id` throws, because the id is immutable for an id-ful and an anonymous builder alike. |
|
|
578
|
+
| `remove` | `boolean \| void` | Removes non-id fields: every field with no argument, one field by key, or the fields a key list names; the keyed forms report whether every named key existed. |
|
|
579
|
+
| `merge` | `void` | Reconciles with an incoming plain `Subject`; the incoming record wins and the base `id` is kept. |
|
|
580
|
+
| `clear` | `void` | Removes every non-id field. |
|
|
581
|
+
| `repeat` | `readonly Subject[]` | Produces `count` deterministic minted-id clones as plain payloads — a pure read that emits nothing. |
|
|
582
|
+
| `build` | `Subject` | Returns a fresh durable payload snapshot of the current state — total and deterministic on every call. |
|
|
583
|
+
| `destroy` | `void` | Tears the builder down idempotently — destroys the emitter last. |
|
|
584
|
+
|
|
585
|
+
## Contract
|
|
586
|
+
|
|
587
|
+
These invariants hold across `src/core` ↔ `reason.md`:
|
|
588
|
+
|
|
589
|
+
1. **DOC ↔ SOURCE bijection.** Every `function` / `class` / `const` / `interface` / `type` row in the `## Surface` tables is a real export of the reason source tree, and every export appears as a Surface row — exhaustive in each direction.
|
|
590
|
+
2. **Deterministic, synchronous, immutable.** Same subject + same definition → the same result, every time — no clocks, no randomness, no I/O, nothing async. No input is ever mutated; every result (and every builder output) is a fresh object. A result always carries `success`, a `trace` narrating each step, and the accumulated `errors` — an error does not abort the run (the value / conclusion / solutions are still computed from whatever applied), it makes `success` false. Expression evaluation and symbolic isolation recurse with the input expression's own depth, so a pathologically deep hand-built expression (on the order of `10,000` levels of nesting) is outside the supported contract and may exhaust the call stack; `extractAtoms` and `containsVariable` are the exception — both walk iteratively and stay total at any depth. The workspace builders (and their managers) are the deliberate stateful exception to statelessness, not to immutability: they mutate nothing they are given (the seed is spread on construction, every mutation is copy-on-write through the pure helper family), and `build()` returns a fresh payload deterministically derived from the current state, every call — handed to `reason` by the caller, never built inside the engine.
|
|
591
|
+
3. **Dispatch and `bail`.** The orchestrator is a thin router: registry lookup by `definition.reasoning`, one reasoner per reasoning, re-registration replaces. A registry miss throws `MISSING` and a pre-run validation failure (only when the `validate` option is on) throws `INVALID` — both are caller misuse, bypass `bail`, and emit nothing. A reasoner throw always emits `error` with the raw thrown value; under `bail: true` (the default) it is rethrown, under `bail: false` it becomes an empty type-shaped failure result. Only successful results emit `reason`. The batch overload maps subjects in order (per-subject validation when on).
|
|
592
|
+
4. **Failure results, not throws, inside a reasoner.** A reasoner's single throw is `MISMATCH` (handed a definition of a different reasoning); every structural malformation — missing / non-array `groups` / `rules` / `equations` / `facts` — yields a failure result, because the runtime never assumes `validate` ran. The operators are total the same way: an unknown `Comparison` surfaces as `CheckResult.error` (`met: false`), an unknown `MathOperation` returns the value unchanged, divide-by-zero is `NaN`, and the `Aggregator` has fixed empty-input identities (`sum` / `average` → `0`, `product` → `1`, `minimum` / `maximum` → `NaN`). Definition arrays (`facts` / `inferences` / `rules` / `equations` / `groups` / `factors`) are iterated defensively — an array hole, `null`, or other ill-typed junk entry is skipped rather than crashing evaluation.
|
|
593
|
+
5. **Guard totality and exactness.** Every validator is a total `Guard` — adversarial input (cycles, depth, hostile prototypes) returns `false`, never throws. Caller-supplied input records are exact and their numeric fields require `isFiniteNumber` because definitions must survive JSON. result guards accept open objects, including class instances, and guard published numeric members with `isNumber`, including `NaN` and infinities. Nested result members follow the same posture; `isInferentialResult` checks `derived` with the open `isResultFact` guard. The recursive input shapes enter through `lazyOf`; `isProofNode` uses a bounded iterative traversal. Builders omit absent optional input keys so their outputs round-trip the input guards. Numeric subject reads coerce through the contracts `parseNumber` — an unresolvable field (including `NaN` / `±Infinity` subject values) takes the `fallback` path, never the non-finite error path (still reachable through static sources and transform / inversion results).
|
|
594
|
+
6. **Observation is a pure side-channel.** The `Reason` owns a typed `emitter` (`ReasonEventMap` — `register(reasoning)` / `reason(result)` / `error(error)` / `destroy()`); each manager owns its own (`{X}ManagerEventMap` — its mutation verbs, element-id payloads, plus `destroy`), and the builders keep reduced maps of the operations that are theirs alone (`DefinitionBuilderEventMap` — `merge` / `clear` / `destroy`; `SubjectBuilderEventMap` — `set` / `remove` / `merge` / `clear` / `destroy`); reasoners and operators are event-free by design (stateless evaluators have no observable lifecycle). Every event is emitted directly and synchronously, after the mutation it reports; listener isolation is the emitter's own — a throwing listener routes to the `error` option handler (`(error, event)`), never onto the domain map. `destroy()` clears the registry, emits `destroy`, then destroys the emitter last; it is idempotent, and afterwards every method except the `emitter` getter and `destroy` itself throws `DESTROYED`. The managers follow the identical lifecycle, and `DefinitionBuilder.destroy()` cascades to every manager first, its own emitter last.
|
|
595
|
+
7. **Coded errors.** Every throw out of this module is a `ReasonError` with a machine-readable `code` (`MISSING` / `INVALID` / `MISMATCH` / `DESTROYED` / `TARGET` / `OPERATOR`) and — except `DESTROYED` — a `context` carrying the definition id and the reasoning involved, or the offending `id` / `target` / `groupId` for `TARGET`, the `key` for a non-clearable `clear`, and the `operator` for `OPERATOR`; `catch` blocks narrow with `isReasonError`, never `as`. `MISMATCH` covers a cross-reasoning definition handed to a reasoner or to `DefinitionBuilder.merge`, a `clear` key that is not clearable for the builder's reasoning, and a write to a `SubjectBuilder`'s immutable `id`; `DESTROYED` covers any use of a destroyed orchestrator, builder, or manager; `OPERATOR` covers a math operator outside the accepted vocabulary — `invertLeft` / `invertRight` on a non-invertible operation, and `applyOperation` on an unknown one.
|
|
596
|
+
8. **DOC ↔ SOURCE method bijection.** Every behavioral interface's `## Methods` table lists exactly its public methods (call-signature members) — exhaustive in each direction — and each implementing class exposes the same public methods, no more. A renamed / added / removed method breaks the gate until the table is reconciled.
|
|
597
|
+
|
|
598
|
+
These runtime rules hold alongside the numbered invariants. Derivation bookkeeping compares with SameValueZero (`equalValues`), so a NaN-valued conclusion or fact term derives once and the fixpoint converges. The definition-level quantitative value is finite-checked after rounding (`Definition "<id>" produced non-finite value: <v>`). `roundTo` passes the value through unchanged at extreme precisions whose scale factor overflows. Inverting a left-operand divide by zero (`x / 0 = c`) yields the non-finite equation error. A lookup reads only own table keys, and a missing / `null` field takes the `fallback` before any `''` key. The malformed-shape paths stay graceful — a missing / non-array `premises` on a backward logical rule errors and excludes it, on a backward inferential candidate skips it silently, and a missing factor `source` resolves to the fallback path. `validate` adds uniqueness / confidence / overlay-mismatch / unbound-variable warnings (`Duplicate <noun> id "<id>"`, `confidence outside [0, 1]`, the array-path overlay-key mismatch, the unbound `?variable` conclusion) while runtime behavior around duplicates — and around every other warned shape — is unchanged. Still out of scope: asynchronous reasoners, definition persistence, and contract-DSL shapes for the definition family (the plain guards in § Validators suffice) — all additive, leaving the preceding surface unchanged.
|
|
599
|
+
|
|
600
|
+
## Patterns
|
|
601
|
+
|
|
602
|
+
### Quantitative scoring
|
|
603
|
+
|
|
604
|
+
Each factor runs a pipeline — `checks` gate (all must be met) → `source` resolve (`fallback` when unresolvable) → finite check → `transforms` chain → `bounds` clamp — in ascending `priority` order (stable). A group's value is its `base` plus the weighted aggregation of the factors that applied, clamped but never rounded; `strict: true` makes the group all-or-nothing. The definition's value aggregates the applied groups (no weights at this level), clamps, rounds to `precision`, then is finite-checked: the `Aggregator`'s empty-input `NaN` for `minimum` / `maximum` is its deliberate "no data" signal, so aggregating zero applied groups under those surfaces as a `Definition "<id>" produced non-finite value: NaN` error (`success: false`, the `NaN` left visible in `value`) rather than a silent success. An unapplied group's `GroupResult.value` may still be `NaN` the same way — it is excluded from the definition aggregate, so only its own record shows it.
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
import {
|
|
608
|
+
createBounds,
|
|
609
|
+
createCheck,
|
|
610
|
+
createFactorGroup,
|
|
611
|
+
createFieldFactor,
|
|
612
|
+
createLookupFactor,
|
|
613
|
+
createQuantitativeDefinition,
|
|
614
|
+
createQuantitativeReasoner,
|
|
615
|
+
createReason,
|
|
616
|
+
createTransform,
|
|
617
|
+
} from '@orkestrel/reason'
|
|
618
|
+
|
|
619
|
+
const reason = createReason({ reasoners: [createQuantitativeReasoner()] })
|
|
620
|
+
|
|
621
|
+
const definition = createQuantitativeDefinition('premium', 'Premium', [
|
|
622
|
+
createFactorGroup(
|
|
623
|
+
'risk',
|
|
624
|
+
'sum',
|
|
625
|
+
[
|
|
626
|
+
createFieldFactor('age', 'age', {
|
|
627
|
+
checks: [createCheck('licensed', 'equals', true)], // gate: all checks must be met
|
|
628
|
+
transforms: [createTransform('percentage', 50)], // then 50% of the raw value
|
|
629
|
+
bounds: createBounds(0, 40), // then clamp
|
|
630
|
+
required: true, // a gate/resolve failure becomes a result error
|
|
631
|
+
}),
|
|
632
|
+
createLookupFactor('region', 'region', { CA: 12, NY: 8 }, { fallback: 5, weight: 2 }),
|
|
633
|
+
],
|
|
634
|
+
{ base: 100 },
|
|
635
|
+
),
|
|
636
|
+
])
|
|
637
|
+
|
|
638
|
+
const result = reason.reason({ age: 40, licensed: true, region: 'CA' }, definition)
|
|
639
|
+
if (result.reasoning === 'quantitative') {
|
|
640
|
+
result.value // 144 — 100 + sum(20 · 1, 12 · 2), precision-rounded
|
|
641
|
+
result.groups[0]?.factors // the per-factor breakdown (raw vs value)
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
An `enabled: false` factor or group is skipped and omitted from results; a `required` factor that fails its gate or cannot resolve adds an error (`success: false`) while the rest of the run continues.
|
|
646
|
+
|
|
647
|
+
The `Evaluator`, `Transformer`, and `Aggregator` operators are usable directly, independent of any reasoner — the `QuantitativeReasoner` composes them internally, but each is a total, injectable seam:
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
import {
|
|
651
|
+
createAggregator,
|
|
652
|
+
createCheck,
|
|
653
|
+
createEvaluator,
|
|
654
|
+
createTransform,
|
|
655
|
+
createTransformer,
|
|
656
|
+
} from '@orkestrel/reason'
|
|
657
|
+
|
|
658
|
+
const evaluator = createEvaluator()
|
|
659
|
+
evaluator.evaluate(createCheck('age', 'above', 18), { age: 25 }) // { field: 'age', met: true, actual: 25 }
|
|
660
|
+
evaluator.batch([createCheck('age', 'above', 18)], { age: 25 }) // one CheckResult per check, positionally
|
|
661
|
+
|
|
662
|
+
const transformer = createTransformer()
|
|
663
|
+
transformer.apply(10, createTransform('multiply', 2)) // 20 — one math step
|
|
664
|
+
transformer.chain(10, [createTransform('add', 5), createTransform('multiply', 2)]) // 30 — left-folded
|
|
665
|
+
|
|
666
|
+
const aggregator = createAggregator()
|
|
667
|
+
aggregator.aggregate([10, 20, 30], 'sum') // 60
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Numeric domains
|
|
671
|
+
|
|
672
|
+
`reason` runs on ordinary JS `number` — binary floating point, not decimal. A definition's `value` rounds to `precision` (`DEFAULT_PRECISION = 4`) only once, at the end of the pipeline (`transforms` → `bounds` → terminal round, preceding), so intermediate float error from earlier steps has already accumulated before that single round ever sees it. TC39 `Decimal` is still Stage 1, so until it lands the compliant answer for a money-like domain is a scaled-integer recipe, not a new dependency.
|
|
673
|
+
|
|
674
|
+
**Scaled integers.** Represent a money amount as integer minor units (cents), a rate as integer basis points (`1e-4` units), a ppm quantity as integer `1e-6` units — do the arithmetic on the scaled integers and divide back only for display. Binary floating point cannot represent most decimal fractions exactly:
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
0.1 + 0.2 // 0.30000000000000004 — not 0.3
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Summing one hundred `$0.1` line items as floats drifts the same way (`9.99999999999998`, not `10`), while summing the equivalent `10`-cent integers is exact: `1000` cents accumulated, divided back once → `$10.00`.
|
|
681
|
+
|
|
682
|
+
**When `roundTo(4)` is sufficient.** A single terminal rounding of a shallow computation recovers the intended decimal — the preceding float noise is well inside the half-ulp window at 4 places:
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
roundTo(0.1 + 0.2, 4) // 0.3
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**When `roundTo(4)` is not sufficient.**
|
|
689
|
+
|
|
690
|
+
(a) A domain finer than 4 decimals loses precision to `precision: 4` itself, not to float error — a ppm quantity (`1e-6` units) truncates to `0`:
|
|
691
|
+
|
|
692
|
+
```ts
|
|
693
|
+
roundTo(1 / 1_000_000, 4) // 0 — the ppm value is gone
|
|
694
|
+
roundTo(1 / 1_000_000, 6) // 0.000001 — recovered only at a finer precision
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
(b) Error that compounds across several `percentage` / `divide` steps before the terminal round can cross a half-ulp boundary at the 4th place even though rounding still happens only once. Chained 5% / 5% / 15% increases on `100` followed by a `divide` by `6` land exactly on a rounding boundary, `21.13125`, which rounds half-up to `21.1313` — but the float chain's accumulated binary error lands slightly under it, so `roundTo` rounds the wrong way:
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
;(100 * 1.05 * 1.05 * 1.15) / 6 // 21.131249999999998 — not the exact 21.13125
|
|
701
|
+
roundTo((100 * 1.05 * 1.05 * 1.15) / 6, 4) // 21.1312 — rounds down, wrong
|
|
702
|
+
roundTo(21.13125, 4) // 21.1313 — the exact value would round UP
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
The scaled-integer fix carries the same chain as an exact rational (numerator over denominator) instead of folding a float at every step, and divides + rounds only once at the very end:
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
const numerator = 100n * 105n * 105n * 115n // the +5% / +5% / +15% multipliers
|
|
709
|
+
const denominator = 100n * 100n * 100n * 6n // their scale, plus the final /6
|
|
710
|
+
const scaled = (numerator * 20000n) / denominator // ×2 so the half bit survives truncation
|
|
711
|
+
const rounded = scaled % 2n >= 1n ? scaled / 2n + 1n : scaled / 2n // one terminal round-half-up
|
|
712
|
+
Number(rounded) / 10000 // 21.1313 — matches the exact value
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
**Cross-reference.** The quantitative pipeline's `transforms` are opaque math steps and its terminal round is a single `roundTo(precision)` call — inject the scale factor before the first `transform` (convert the field/static source into scaled-integer minor units) and divide back out after reading `result.value`, rather than trying to make the pipeline itself decimal-exact.
|
|
716
|
+
|
|
717
|
+
### Logical chaining — forward and backward
|
|
718
|
+
|
|
719
|
+
Forward chaining runs the rules (ascending `priority`) to a fixpoint: each firing rule asserts its conclusion's atoms as derived facts overlaid on the subject (keyed by `formatField`), until an iteration derives nothing or `depth` is hit; the reported `rules` are then re-evaluated in original order against the final overlay, and `conclusion` is the last rule's conclusion. Backward chaining proves every enabled, conclusion-bearing rule goal-first in priority order (each proof sharing the growing derived overlay) — recursing into rules whose conclusions can establish a premise, with a visited-rule cycle guard, the `depth` cap, and negation-as-failure for `not`; the overall `conclusion` is the last priority-sorted rule's result.
|
|
720
|
+
|
|
721
|
+
```ts
|
|
722
|
+
import {
|
|
723
|
+
createAtom,
|
|
724
|
+
createCompound,
|
|
725
|
+
createLogicalDefinition,
|
|
726
|
+
createLogicalReasoner,
|
|
727
|
+
createReason,
|
|
728
|
+
createRule,
|
|
729
|
+
} from '@orkestrel/reason'
|
|
730
|
+
|
|
731
|
+
const reason = createReason({ reasoners: [createLogicalReasoner()] })
|
|
732
|
+
|
|
733
|
+
const rules = [
|
|
734
|
+
createRule('adult', [createAtom('age', 'from', 18)], createAtom('adult', 'equals', true)),
|
|
735
|
+
createRule(
|
|
736
|
+
'eligible',
|
|
737
|
+
[
|
|
738
|
+
createCompound('and', [
|
|
739
|
+
createAtom('adult', 'equals', true),
|
|
740
|
+
createAtom('accidents', 'below', 2),
|
|
741
|
+
]),
|
|
742
|
+
],
|
|
743
|
+
createAtom('eligible', 'equals', true),
|
|
744
|
+
),
|
|
745
|
+
]
|
|
746
|
+
|
|
747
|
+
const forward = reason.reason(
|
|
748
|
+
{ age: 25, accidents: 0 },
|
|
749
|
+
createLogicalDefinition('e', 'Eligibility', rules),
|
|
750
|
+
)
|
|
751
|
+
if (forward.reasoning === 'logical') forward.conclusion // true — 'eligible' through the derived 'adult'
|
|
752
|
+
|
|
753
|
+
// Backward: prove the last rule's conclusion, recursing only where needed.
|
|
754
|
+
const goal = createLogicalDefinition('e', 'Eligibility', rules, { strategy: 'backward', depth: 5 })
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
Connectives evaluate eagerly (every operand evaluates — no short-circuit): `not` reads only its first operand, `implies` is vacuously true below two operands, `xor` is false. Conclusion extraction ignores connectives — every atom inside a firing rule's conclusion is asserted.
|
|
758
|
+
|
|
759
|
+
Quirks to design around. **Derived-overlay keys are `formatField` strings**: a conclusion written with an array path derives the dot-joined flat key, so a chained premise must read it with the dotted-string form — an array-path premise descends into nesting the overlay never creates. **A `premises: []` rule diverges by strategy**: forward reports `Rule "<id>" has no premises — skipped` (an error, the rule excluded); backward applies it vacuously (no premise can fail), which is the intended rule rather than a defect. **The fixpoint snapshots per iteration**: a derivation made mid-pass is invisible until the next pass (unlike the inferential reasoner's live fact list), so a `depth` cap truncates chains one hop per iteration regardless of declaration order.
|
|
760
|
+
|
|
761
|
+
### Symbolic solving
|
|
762
|
+
|
|
763
|
+
Bindings start from the definition's `variables`, then numeric subject fields override same-named variables (every own key except `id`, coerced with `parseNumber`). Equations solve strictly in order: when the `target` is unbound and sits on exactly one side, it is isolated algebraically through the `INVERTIBLE_OPERATIONS`; each solution is rounded to `precision` before feeding forward into later equations. A failing equation (unbound variable, non-invertible isolation, non-finite value) records an error and a `FAILED` trace — the run continues.
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
import {
|
|
767
|
+
createConstant,
|
|
768
|
+
createEquation,
|
|
769
|
+
createOperation,
|
|
770
|
+
createReason,
|
|
771
|
+
createSymbolicDefinition,
|
|
772
|
+
createSymbolicReasoner,
|
|
773
|
+
createVariable,
|
|
774
|
+
} from '@orkestrel/reason'
|
|
775
|
+
|
|
776
|
+
const reason = createReason({ reasoners: [createSymbolicReasoner()] })
|
|
777
|
+
|
|
778
|
+
const definition = createSymbolicDefinition(
|
|
779
|
+
'pricing',
|
|
780
|
+
'Pricing',
|
|
781
|
+
[
|
|
782
|
+
// net + tax = total → isolate: net = total - tax
|
|
783
|
+
createEquation(
|
|
784
|
+
'net',
|
|
785
|
+
createOperation('add', createVariable('net'), createVariable('tax')),
|
|
786
|
+
createVariable('total'),
|
|
787
|
+
'net',
|
|
788
|
+
),
|
|
789
|
+
// discount = net * 10 / 100 — 'net' has just been fed forward
|
|
790
|
+
createEquation(
|
|
791
|
+
'discount',
|
|
792
|
+
createVariable('discount'),
|
|
793
|
+
createOperation(
|
|
794
|
+
'divide',
|
|
795
|
+
createOperation('multiply', createVariable('net'), createConstant(10)),
|
|
796
|
+
createConstant(100),
|
|
797
|
+
),
|
|
798
|
+
'discount',
|
|
799
|
+
),
|
|
800
|
+
],
|
|
801
|
+
{ variables: { tax: 5 } },
|
|
802
|
+
)
|
|
803
|
+
|
|
804
|
+
const result = reason.reason({ total: 25 }, definition) // subject overrides / supplies bindings
|
|
805
|
+
if (result.reasoning === 'symbolic') result.solutions // { net: 20, discount: 2 }
|
|
806
|
+
definition.equations // the two equations in the preceding fence, in solve order
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Inferential derivation and proof
|
|
810
|
+
|
|
811
|
+
Scalar subject fields are injected as `has(key, value)` base facts. Forward chaining unifies each enabled inference's premise patterns against the known facts (a `'?'`-prefixed string term is a variable; bindings must be consistent within a match, relational-join style) and derives every instantiated conclusion to a fixpoint — deduplicated, each with confidence = the product of its premise facts' confidences × the inference's own, rounded to `CONFIDENCE_PRECISION`. Backward proving returns the first provable conclusion with its `ProofNode` tree.
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
import {
|
|
815
|
+
createFact,
|
|
816
|
+
createInference,
|
|
817
|
+
createInferentialDefinition,
|
|
818
|
+
createInferentialReasoner,
|
|
819
|
+
createReason,
|
|
820
|
+
} from '@orkestrel/reason'
|
|
821
|
+
|
|
822
|
+
const reason = createReason({ reasoners: [createInferentialReasoner()] })
|
|
823
|
+
|
|
824
|
+
const definition = createInferentialDefinition(
|
|
825
|
+
'family',
|
|
826
|
+
'Family',
|
|
827
|
+
[createFact('f1', 'parent', ['alice', 'bob']), createFact('f2', 'parent', ['bob', 'carol'], 0.9)],
|
|
828
|
+
[
|
|
829
|
+
createInference(
|
|
830
|
+
'grand',
|
|
831
|
+
[createFact('p1', 'parent', ['?x', '?y']), createFact('p2', 'parent', ['?y', '?z'])],
|
|
832
|
+
createFact('c1', 'grandparent', ['?x', '?z']),
|
|
833
|
+
),
|
|
834
|
+
],
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
const result = reason.reason({}, definition)
|
|
838
|
+
if (result.reasoning === 'inferential') {
|
|
839
|
+
result.derived // grandparent('alice', 'carol') — confidence 0.9 (1 × 0.9 × 1)
|
|
840
|
+
}
|
|
841
|
+
definition.facts // the two seed facts in the preceding fence
|
|
842
|
+
definition.inferences // the one inference rule in the preceding fence
|
|
843
|
+
|
|
844
|
+
// Backward: prove one conclusion and return its proof tree.
|
|
845
|
+
const proved = reason.reason(
|
|
846
|
+
{},
|
|
847
|
+
createInferentialDefinition(
|
|
848
|
+
'family',
|
|
849
|
+
'Family',
|
|
850
|
+
[createFact('f1', 'parent', ['alice', 'bob']), createFact('f2', 'parent', ['bob', 'carol'])],
|
|
851
|
+
[
|
|
852
|
+
createInference(
|
|
853
|
+
'grand',
|
|
854
|
+
[createFact('p1', 'parent', ['?x', '?y']), createFact('p2', 'parent', ['?y', '?z'])],
|
|
855
|
+
createFact('c1', 'grandparent', ['?x', '?z']),
|
|
856
|
+
),
|
|
857
|
+
],
|
|
858
|
+
{ strategy: 'backward' },
|
|
859
|
+
),
|
|
860
|
+
)
|
|
861
|
+
if (proved.reasoning === 'inferential') proved.proof // the ProofNode tree, depth-annotated
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
Backward proving is a predicate-level reachability heuristic, not full resolution: each premise is proved independently under the goal's bindings (no cross-premise binding consistency), and a proven goal's `derived` fact may keep uninstantiated `?variables` in its `terms`. A goal that is already a base fact still reports a `derived` duplicate stamped with the inference's confidence, over a bare fact-leaf proof node (no `inference` / `children` keys). Forward chaining is the sound engine — reach for backward when a cheap proof tree is the point. Forward's `knownFacts` also grows live within an iteration (a fact derived early in a pass can feed a later inference in the same pass), so a `depth`-capped run derives more when inferences are declared in dependency order — the opposite temperament to the logical reasoner's per-iteration snapshot.
|
|
865
|
+
|
|
866
|
+
### Shaping definitions as data
|
|
867
|
+
|
|
868
|
+
A definition is plain data, so deriving a changed one is a pure function call — the capability-layer helpers cover every collection with `append` / `prepend` / `replace` / `remove`, whole definitions with `merge*`, optional fields with `clear*`, and the JSON boundary with `parseDefinition`. Every call returns a fresh definition; the input is never touched. Insertions dedup-then-insert (re-adding an id moves it — `replace*` is the position-preserving update), and an optional `target` id places the new element relative to an existing one; a `target` naming nothing throws `TARGET`.
|
|
869
|
+
|
|
870
|
+
```ts
|
|
871
|
+
import {
|
|
872
|
+
appendFactor,
|
|
873
|
+
appendGroup,
|
|
874
|
+
createFactorGroup,
|
|
875
|
+
createFieldFactor,
|
|
876
|
+
createQuantitativeDefinition,
|
|
877
|
+
createStaticFactor,
|
|
878
|
+
mergeQuantitativeDefinition,
|
|
879
|
+
parseDefinition,
|
|
880
|
+
replaceGroup,
|
|
881
|
+
} from '@orkestrel/reason'
|
|
882
|
+
|
|
883
|
+
const base = createQuantitativeDefinition('risk', 'Risk', [
|
|
884
|
+
createFactorGroup('drivers', 'sum', [createStaticFactor('floor', 10)]),
|
|
885
|
+
])
|
|
886
|
+
|
|
887
|
+
// Grow a group, then swap the grown group back in — position preserved.
|
|
888
|
+
const drivers = base.groups[0]
|
|
889
|
+
const grown =
|
|
890
|
+
drivers === undefined
|
|
891
|
+
? base
|
|
892
|
+
: replaceGroup(base, appendFactor(drivers, createFieldFactor('age', 'age')))
|
|
893
|
+
|
|
894
|
+
// Append a sibling group after 'drivers' (target names the anchor id).
|
|
895
|
+
const wide = appendGroup(
|
|
896
|
+
grown,
|
|
897
|
+
createFactorGroup('region', 'sum', [createStaticFactor('flat', 5)]),
|
|
898
|
+
'drivers',
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
// Reconcile a revision wholesale — id-keyed collections merge, incoming scalars win.
|
|
902
|
+
const merged = mergeQuantitativeDefinition(
|
|
903
|
+
wide,
|
|
904
|
+
createQuantitativeDefinition('risk', 'Risk v2', []),
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
// The JSON round-trip: stringify any definition, narrow it back fail-safe.
|
|
908
|
+
const restored = parseDefinition(JSON.stringify(merged)) // Definition | undefined — junk parses to undefined
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
The subject engine mirrors this on the other end of `reason(subject, definition)` — `assignField` / `removeField` upsert and delete top-level fields copy-on-write (deleting omits the key, never writes `undefined`), `mergeSubjects` reconciles incoming-wins with the base `id` preserved, and `repeatSubject` mints `count` deterministic clones (`` `${baseId}-0` ``, `` `${baseId}-1` ``, … — no randomness) for batch runs.
|
|
912
|
+
|
|
913
|
+
### The definition workspace — `DefinitionBuilder`
|
|
914
|
+
|
|
915
|
+
For incremental authoring — a builder UI, an MCP session, anything that accumulates a definition across many steps — wrap the data in the stateful builder. Each manager is self-owning: it owns one collection with single-word verbs, mutations delegate to the preceding pure helpers, copy-on-write into the manager's own private state, and emit through the manager's own emitter. Managers are also bring-your-own: the builder constructs seed-filled defaults, but any slot accepts a pre-built manager (`createDefinitionBuilder(seed, { rules: createRuleManager({ rules }) })`) — the bring-your-own-manager construction pattern. The engine never builds for you: call `build()` and hand the fresh plain `Definition` to `reason` / `validate` at the call site.
|
|
916
|
+
|
|
917
|
+
```ts
|
|
918
|
+
import {
|
|
919
|
+
createCheck,
|
|
920
|
+
createQuantitativeReasoner,
|
|
921
|
+
createReason,
|
|
922
|
+
createDefinitionBuilder,
|
|
923
|
+
createFactorGroup,
|
|
924
|
+
createFieldFactor,
|
|
925
|
+
createQuantitativeDefinition,
|
|
926
|
+
createStaticFactor,
|
|
927
|
+
} from '@orkestrel/reason'
|
|
928
|
+
|
|
929
|
+
const draft = createDefinitionBuilder(
|
|
930
|
+
createQuantitativeDefinition('risk', 'Risk', [
|
|
931
|
+
createFactorGroup('drivers', 'sum', [createStaticFactor('floor', 10)]),
|
|
932
|
+
]),
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
draft.factors.append('drivers', createFieldFactor('age', 'age')) // into the named group
|
|
936
|
+
draft.factors.replace(
|
|
937
|
+
'drivers',
|
|
938
|
+
createFieldFactor('age', 'age', { checks: [createCheck('licensed', 'equals', true)] }),
|
|
939
|
+
) // in place
|
|
940
|
+
draft.groups.append(createFactorGroup('region', 'sum', [createStaticFactor('flat', 5)]))
|
|
941
|
+
draft.groups.prepend(createFactorGroup('base', 'sum', [createStaticFactor('seed', 1)])) // insert at the start
|
|
942
|
+
draft.groups.group('region') // FactorGroup | undefined — the accessors read
|
|
943
|
+
draft.clear('description') // delete one optional field for this reasoning
|
|
944
|
+
|
|
945
|
+
const reason = createReason({ reasoners: [createQuantitativeReasoner()] })
|
|
946
|
+
const result = reason.reason({ age: 25, licensed: true }, draft.build()) // build outside, pass the payload
|
|
947
|
+
if (result.reasoning === 'quantitative') result.value // 41 — 1 + (10 + 25) + 5
|
|
948
|
+
|
|
949
|
+
draft.groups.seat([createFactorGroup('only', 'sum', [])]) // swap a whole collection in one silent step — an authoring surface's "load this revision"
|
|
950
|
+
const payload = draft.build() // a fresh plain Definition every call — store it, ship it, reason over it
|
|
951
|
+
draft.destroy() // idempotent; cascades to the managers; afterwards mutation throws DESTROYED
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
Managers are kind-free: an off-kind mutation (`draft.rules.append(...)` on a quantitative draft) is inert, never a throw — the rules accumulate in the `RuleManager` but `build()` composes only the collections belonging to the draft's `reasoning`, so they never surface in the payload. Design around this deliberately: nothing warns about off-kind state. `merge` requires the same `reasoning` (else `MISMATCH`) and re-seats collections through each manager's `seat` silently (no per-element events) — the same channel an authoring surface calls to swap a whole collection in one step; re-seeding is `createDefinitionBuilder(parseDefinition(text) ?? fallback)` — `build()` output and `parseDefinition` are exact inverses across the JSON boundary. Rule and equation order stay load-bearing exactly as in the plain data: `rules.append` without a `target` makes the new rule the forward conclusion; `equations.append` places it last in the solve order. One nesting echo: factors live inside groups, so a `factors` mutation writes the updated group back through the sibling `GroupManager` — the factor event fires on `factors.emitter` and a `replace` fires on `groups.emitter` for the containing group.
|
|
955
|
+
|
|
956
|
+
Every manager exposes the same accessor pair over its own collection — here each one is read on a draft of its own `reasoning`:
|
|
957
|
+
|
|
958
|
+
```ts
|
|
959
|
+
import {
|
|
960
|
+
createAtom,
|
|
961
|
+
createConstant,
|
|
962
|
+
createDefinitionBuilder,
|
|
963
|
+
createEquation,
|
|
964
|
+
createFact,
|
|
965
|
+
createInference,
|
|
966
|
+
createInferentialDefinition,
|
|
967
|
+
createLogicalDefinition,
|
|
968
|
+
createRule,
|
|
969
|
+
createSymbolicDefinition,
|
|
970
|
+
createVariable,
|
|
971
|
+
} from '@orkestrel/reason'
|
|
972
|
+
|
|
973
|
+
const logical = createDefinitionBuilder(createLogicalDefinition('elig', 'Eligibility', []))
|
|
974
|
+
logical.rules.append(
|
|
975
|
+
createRule('adult', [createAtom('age', 'from', 18)], createAtom('adult', 'equals', true)),
|
|
976
|
+
)
|
|
977
|
+
logical.rules.rule('adult')?.name // 'adult' — `name` default: the id
|
|
978
|
+
logical.rules.rules().length // 1
|
|
979
|
+
|
|
980
|
+
const symbolic = createDefinitionBuilder(createSymbolicDefinition('rate', 'Rate', []))
|
|
981
|
+
symbolic.equations.append(createEquation('e1', createVariable('x'), createConstant(42), 'x'))
|
|
982
|
+
symbolic.equations.equation('e1')?.target // 'x'
|
|
983
|
+
symbolic.variables.add('x', 42)
|
|
984
|
+
symbolic.variables.variable('x') // 42
|
|
985
|
+
|
|
986
|
+
const inferential = createDefinitionBuilder(
|
|
987
|
+
createInferentialDefinition('mortality', 'Mortality', [], []),
|
|
988
|
+
)
|
|
989
|
+
inferential.facts.append(createFact('f1', 'human', ['socrates']))
|
|
990
|
+
inferential.facts.fact('f1')?.predicate // 'human'
|
|
991
|
+
inferential.inferences.append(
|
|
992
|
+
createInference(
|
|
993
|
+
'mortal',
|
|
994
|
+
[createFact('p1', 'human', ['?x'])],
|
|
995
|
+
createFact('c1', 'mortal', ['?x']),
|
|
996
|
+
),
|
|
997
|
+
)
|
|
998
|
+
inferential.inferences.inference('mortal')?.name // 'mortal'
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
### The subject workspace — `SubjectBuilder`
|
|
1002
|
+
|
|
1003
|
+
The subject side is one flat collection of fields, so verbs sit directly on the builder (no managers, no `append` / `prepend`). The `id` is optional and immutable through the builder — when absent the builder is anonymous (`.id` is `undefined`, `build()` emits no `id` key); `repeat` turns one accumulated subject into a deterministic batch.
|
|
1004
|
+
|
|
1005
|
+
```ts
|
|
1006
|
+
import { createQuantitativeReasoner, createReason, createSubjectBuilder } from '@orkestrel/reason'
|
|
1007
|
+
|
|
1008
|
+
const reason = createReason({ reasoners: [createQuantitativeReasoner()] })
|
|
1009
|
+
|
|
1010
|
+
const applicant = createSubjectBuilder({ id: 'alice', age: 25 })
|
|
1011
|
+
applicant.set('region', 'CA')
|
|
1012
|
+
applicant.merge({ licensed: true, accidents: 0 }) // incoming wins, id kept
|
|
1013
|
+
applicant.remove(['accidents']) // batch form first — returns whether every key existed
|
|
1014
|
+
applicant.fields() // { id: 'alice', age: 25, region: 'CA', licensed: true } — the plural read
|
|
1015
|
+
|
|
1016
|
+
const result = reason.reason(applicant.build(), definition) // build outside, pass the payload
|
|
1017
|
+
const cohort = applicant.repeat(3) // plain subjects: ids 'alice-0', 'alice-1', 'alice-2'
|
|
1018
|
+
const results = reason.reason(cohort, definition) // the batch overload, as ever
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
`fields()` reads the whole current record and `field(key)` one top-level key — nested records are composed as values (read deep at evaluation time through `FieldPath` arrays), not navigated by the builder. Setting `id` or removing `id` throws `MISMATCH`: the id is the builder's identity and the `repeat` minting base.
|
|
1022
|
+
|
|
1023
|
+
### Observing
|
|
1024
|
+
|
|
1025
|
+
The `Reason` exposes a typed `emitter` for fire-and-forget observers — logging, metrics, an audit trail. Subscribe through `reason.emitter.on(...)`, or wire initial listeners through the reserved `on` option with the `error` option as the emitter's own listener-error handler.
|
|
1026
|
+
|
|
1027
|
+
```ts
|
|
1028
|
+
import { createQuantitativeReasoner, createReason } from '@orkestrel/reason'
|
|
1029
|
+
|
|
1030
|
+
const reason = createReason({
|
|
1031
|
+
reasoners: [createQuantitativeReasoner()],
|
|
1032
|
+
on: { error: (error) => console.error('reasoner threw:', error) }, // initial listeners
|
|
1033
|
+
error: (error, event) => console.warn(`listener threw on "${event}"`, error), // the isolation handler
|
|
1034
|
+
})
|
|
1035
|
+
|
|
1036
|
+
reason.emitter.on('register', (reasoning) => console.log(`registered ${reasoning}`))
|
|
1037
|
+
reason.emitter.on('reason', (result) => metrics.record(result.reasoning, result.success))
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
The event vocabulary:
|
|
1041
|
+
|
|
1042
|
+
| Entity | Event map | Events |
|
|
1043
|
+
| ------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
1044
|
+
| `Reason` | `ReasonEventMap` | `register(reasoning)` · `reason(result)` · `error(error)` · `destroy()` |
|
|
1045
|
+
| `DefinitionBuilder` | `DefinitionBuilderEventMap` | `merge(reasoning)` · `clear(key)` · `destroy()` (per-element mutations fire on the managers' own emitters) |
|
|
1046
|
+
| the list managers | `{X}ManagerEventMap` | `append(id)` · `prepend(id)` · `replace(id)` · `remove(id)` · `destroy()` — Group / Factor / Rule / Equation / Fact / Inference |
|
|
1047
|
+
| `VariableManager` | `VariableManagerEventMap` | `add(name)` · `remove(name)` · `destroy()` — the name-keyed record has no placement verbs |
|
|
1048
|
+
| `SubjectBuilder` | `SubjectBuilderEventMap` | `set(key, value)` · `remove(key)` · `merge(incoming)` · `clear()` · `destroy()` |
|
|
1049
|
+
|
|
1050
|
+
`register` fires per registration (constructor seeding does not fire it); `reason` fires once per result the reasoner returns, synchronously before it returns — this includes a `success: false` result the reasoner returns normally (a malformed definition, say), not only successes. A reasoner throw never fires `reason`: it fires `error` with the raw thrown value instead, then either rethrows (`bail: true`, the default) or is converted into a type-shaped failure result (`bail: false`) that is returned to the caller but does not fire `reason`; `destroy` fires once. `MISSING` / `INVALID` throws emit nothing — they are caller misuse, not reasoning outcomes. Reasoners and operators are event-free by design; observe the orchestrator — or the workspace builders, which take the same `on` / `error` options and emit one verb-named event per mutation through their own emitters (each `DefinitionBuilder` manager owns its own emitter — the builder's own emitter carries only `merge` / `clear` / `destroy`; `variables.add` reports as `add` and `variables.remove` as `remove`, each carrying the variable name; a pure read — `build`, `repeat`, the accessors — never emits).
|
|
1051
|
+
|
|
1052
|
+
```ts
|
|
1053
|
+
const draft = createDefinitionBuilder(seed, {
|
|
1054
|
+
on: { merge: (reasoning) => audit.record('merge', reasoning) }, // builder-level listeners
|
|
1055
|
+
})
|
|
1056
|
+
draft.groups.emitter.on('append', (id) => audit.record('group.append', id)) // per-manager mutation events
|
|
1057
|
+
draft.emitter.on('merge', (reasoning) => console.log(`merged a ${reasoning} revision`))
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
### Narrowing untrusted definitions
|
|
1061
|
+
|
|
1062
|
+
Definitions are JSON-serializable data, so they arrive from storage / the wire as `unknown`. Narrow first with the structural guard (`isDefinition` — exact records, total on adversarial input), then with the semantic pass (`validate` — ids present, sources declared, non-empty rule sets) whose `warnings` flag runnable-but-suspicious definitions.
|
|
1063
|
+
|
|
1064
|
+
```ts
|
|
1065
|
+
import { createLogicalReasoner, createReason, isDefinition } from '@orkestrel/reason'
|
|
1066
|
+
import { parseJSON } from '@orkestrel/contract'
|
|
1067
|
+
|
|
1068
|
+
const reason = createReason({ reasoners: [createLogicalReasoner()] })
|
|
1069
|
+
const parsed = parseJSON(text) // unknown — the JSON boundary: narrow, never assert
|
|
1070
|
+
|
|
1071
|
+
if (isDefinition(parsed)) {
|
|
1072
|
+
const validation = reason.validate(parsed) // the semantic pass — returns, never throws
|
|
1073
|
+
if (validation.valid) reason.reason(subject, parsed)
|
|
1074
|
+
else console.warn(validation.errors, validation.warnings)
|
|
1075
|
+
}
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
`warnings` flag the runnable-but-suspicious: empty collections, duplicate rule / group / factor / equation / inference ids (`Duplicate <noun> id "<id>"`, once per duplicated id), inferential confidences outside `[0, 1]`, a logical conclusion's array-path overlay key also read through an array-path premise elsewhere (`Overlay key "<key>" is written through an array path AND also read through an array path — the flat overlay key will not resolve`, `findOverlayMismatches`), and an inferential conclusion `?variable` unbound by all of its inference's premises (`Inference "<id>" conclusion variable "<variable>" is unbound by all premises`, `findUnboundVariables`). The runtime stays permissive about all of them — duplicates in particular are first/last-wins artifacts (a group's weight lookup takes the first same-id factor; a degenerate forward rule id-poisons its valid same-id twin out of the run), which is exactly why `validate` warns.
|
|
1079
|
+
|
|
1080
|
+
Prefer this at boundaries over the orchestrator's `validate: true` option (which throws `INVALID` per call); use the option when a throw is the right failure mode — for example definitions authored in code, where invalidity is a programmer error.
|
|
1081
|
+
|
|
1082
|
+
### Practices
|
|
1083
|
+
|
|
1084
|
+
- **Register every reasoner before the first `reason` call** — dispatch is a registry lookup; a miss throws `MISSING` regardless of `bail`.
|
|
1085
|
+
- **Build definitions with the value factories** — they default `name` to the id, omit absent optional keys (so outputs round-trip the exact-record validators), and keep call sites terse.
|
|
1086
|
+
- **Gate untrusted definitions twice** — `isDefinition` for shape at the boundary, `validate` for semantics; reserve the `validate: true` option for programmer-error contexts.
|
|
1087
|
+
- **Keep ids unique** — `validate` only warns on duplicates; the runtime resolves them first/last-wins, silently.
|
|
1088
|
+
- **Check `success` before trusting the payload** — errors accumulate without aborting, so a `value` / `conclusion` computed alongside errors is a partial answer.
|
|
1089
|
+
- **Read the `trace` when a result surprises you** — every skip, gate, derivation, and convergence is narrated step by step.
|
|
1090
|
+
- **Use `FieldPath` arrays for nested access** — `['address', 'city']` descends; a dotted string like `'address.city'` is one literal key, never split.
|
|
1091
|
+
- **Choose `bail` deliberately** — the default (`true`) rethrows a reasoner throw after the `error` emit; `bail: false` degrades it to an empty failure result for batch pipelines that must keep going.
|
|
1092
|
+
- **Reach for the helpers to derive, the workspace builders to accumulate** — a one-shot change is a pure helper call on plain data; a definition or subject built up across many steps (a builder UI, an MCP session) lives in a `createDefinitionBuilder` / `createSubjectBuilder` workspace.
|
|
1093
|
+
- **`build()` at the call site, always** — the engine takes only plain data; `reason.reason(subject.build(), draft.build())` makes the build step visible, and the payload is exactly what ran.
|
|
1094
|
+
- **Gate authoring verbs by `reasoning`** — managers are kind-free, so an off-kind mutation accumulates silently and never surfaces in `build()`; offer only the current kind's verbs on an authoring surface.
|
|
1095
|
+
- **Store `build()` output, not builders** — `JSON.stringify(draft.build())` out, `parseDefinition` back in, re-seed a fresh builder; the builder itself is a live workspace, not a payload.
|
|
1096
|
+
- **Mind what each `is…` guard narrows at the boundary** — `isDefinition` narrows the plain data; `isDefinitionBuilder` / `isSubjectBuilder` are the builder brand guards. A plain record carrying a `build` function is still data — only the brand makes a builder. A `Subject` is any plain record, so narrow one with `isRecord` from `@orkestrel/contract` — this package publishes no guard for it.
|
|
1097
|
+
- **Destroy when done** — `destroy()` releases the registry and the emitter (the builders cascade to their managers first); a destroyed instance throws `DESTROYED` on use (narrow with `isReasonError`).
|
|
1098
|
+
|
|
1099
|
+
## Tests
|
|
1100
|
+
|
|
1101
|
+
- [`tests/guides.test.ts`](../tests/guides.test.ts) — the `## Surface` ↔ `src/core` bijection (value + type exports), the `## Methods` ↔ interface-method bijection, and the equality gate: every `Summary` cell against its declaration's description paragraph, the titled `Create an orchestrator and score a subject` fence against the `@example` block of that title (pinned so the titled pair cannot be retired silently), and the README pitch against this guide's tagline. It also runs the flagship fences and asserts the values their comments claim.
|
|
1102
|
+
- [`tests/src/core/Reason.test.ts`](../tests/src/core/Reason.test.ts) — the orchestrator: dispatch, batch order, `bail` on and off, the `MISSING` / `INVALID` / `DESTROYED` codes, event sequences, idempotent `destroy`, build-outside equivalence (a builder's `build()` output reasons identically to inline plain data).
|
|
1103
|
+
- [`tests/src/core/builders/DefinitionBuilder.test.ts`](../tests/src/core/builders/DefinitionBuilder.test.ts) — the definition builder: mutation → `build` round-trips per manager, inert off-kind managers, per-manager event pins, manager + builder destroy semantics, brand-forge negatives, seed immutability.
|
|
1104
|
+
- [`tests/src/core/builders/SubjectBuilder.test.ts`](../tests/src/core/builders/SubjectBuilder.test.ts) — the subject builder: id defaulting + immutability + anonymous builds, batch `remove`, incoming-wins `merge`, deterministic `repeat`, `build` determinism, destroy semantics.
|
|
1105
|
+
- [`tests/src/core/helpers.test.ts`](../tests/src/core/helpers.test.ts) — `formatField`, `clamp` / `roundTo` / `matchesBounds` / `equalValues` / `sortByPriority` / `findDuplicates`, the fact and algebra machinery, and the capability-layer engine (`appendById` placement + `TARGET`, per-kind append / prepend / replace / remove, `merge*` / `clear*`, `parseDefinition`, the subject helpers).
|
|
1106
|
+
- [`tests/src/core/validators.test.ts`](../tests/src/core/validators.test.ts) — each guard accepts valid / rejects invalid + adversarial junk, exact-record semantics, `lazyOf` recursion containment.
|
|
1107
|
+
- [`tests/src/core/factories.test.ts`](../tests/src/core/factories.test.ts) — every value factory's output shape (override merging, key omission, `name` defaulting); every entity factory wires a working instance with custom `id`s through the options objects.
|
|
1108
|
+
- [`tests/src/core/operators/Evaluator.test.ts`](../tests/src/core/operators/Evaluator.test.ts) — every comparison (strictness, numeric requirements, the `any` / `none` asymmetry vs the pure-negation `outside`), `FieldPath` resolution, unknown-operator totality.
|
|
1109
|
+
- [`tests/src/core/operators/Transformer.test.ts`](../tests/src/core/operators/Transformer.test.ts) — per-operation operand defaults, divide-by-zero `NaN`, unary operations, `chain` folding.
|
|
1110
|
+
- [`tests/src/core/operators/Aggregator.test.ts`](../tests/src/core/operators/Aggregator.test.ts) — empty-input identities, weight-as-exponent `product`, zero-total-weight `average`, length-mismatch weight fallback.
|
|
1111
|
+
- [`tests/src/core/reasoners/QuantitativeReasoner.test.ts`](../tests/src/core/reasoners/QuantitativeReasoner.test.ts) — the factor pipeline, priorities, `strict` / `required`, source resolution + `parseNumber` coercion, base / bounds / precision stacking.
|
|
1112
|
+
- [`tests/src/core/reasoners/LogicalReasoner.test.ts`](../tests/src/core/reasoners/LogicalReasoner.test.ts) — forward fixpoint + derived overlays, backward proving + cycle safety, the connective truth tables.
|
|
1113
|
+
- [`tests/src/core/reasoners/SymbolicReasoner.test.ts`](../tests/src/core/reasoners/SymbolicReasoner.test.ts) — subject binding + overrides, isolation through invertible operations, rounded feed-forward, per-equation failure isolation.
|
|
1114
|
+
- [`tests/src/core/reasoners/InferentialReasoner.test.ts`](../tests/src/core/reasoners/InferentialReasoner.test.ts) — unification + relational joins, confidence products, dedupe, subject-fact injection, backward proof trees.
|
|
1115
|
+
- [`tests/src/core/integration.test.ts`](../tests/src/core/integration.test.ts) — cross-strategy scenarios through one orchestrator.
|
|
1116
|
+
|
|
1117
|
+
## See also
|
|
1118
|
+
|
|
1119
|
+
- [`contract.md`](contract.md) — the guards, combinators, and `parseNumber` coercion the validators and reasoners compose.
|
|
1120
|
+
- [`emitter.md`](emitter.md) — the typed emitter behind the orchestrator's observation surface.
|
|
1121
|
+
- [`AGENTS.md`](../AGENTS.md) — the rules.
|
|
1122
|
+
- [`README.md`](README.md) — the guides index.
|