@hviana/sema 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/package.json +1 -1
  2. package/src/geometry.ts +14 -30
  3. package/src/mind/mechanisms/cast.ts +12 -7
  4. package/dist/example/demo.d.ts +0 -1
  5. package/dist/example/demo.js +0 -39
  6. package/dist/example/train_base.d.ts +0 -87
  7. package/dist/example/train_base.js +0 -2252
  8. package/dist/src/alphabet.d.ts +0 -7
  9. package/dist/src/alphabet.js +0 -33
  10. package/dist/src/alu/src/alu.d.ts +0 -185
  11. package/dist/src/alu/src/alu.js +0 -440
  12. package/dist/src/alu/src/expr.d.ts +0 -61
  13. package/dist/src/alu/src/expr.js +0 -318
  14. package/dist/src/alu/src/index.d.ts +0 -11
  15. package/dist/src/alu/src/index.js +0 -19
  16. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  17. package/dist/src/alu/src/kernel-arith.js +0 -264
  18. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  19. package/dist/src/alu/src/kernel-bits.js +0 -152
  20. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  21. package/dist/src/alu/src/kernel-logic.js +0 -60
  22. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  23. package/dist/src/alu/src/kernel-nd.js +0 -208
  24. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  25. package/dist/src/alu/src/kernel-numeric.js +0 -366
  26. package/dist/src/alu/src/operation.d.ts +0 -168
  27. package/dist/src/alu/src/operation.js +0 -189
  28. package/dist/src/alu/src/parser.d.ts +0 -221
  29. package/dist/src/alu/src/parser.js +0 -577
  30. package/dist/src/alu/src/resonance.d.ts +0 -55
  31. package/dist/src/alu/src/resonance.js +0 -126
  32. package/dist/src/alu/src/text.d.ts +0 -31
  33. package/dist/src/alu/src/text.js +0 -73
  34. package/dist/src/alu/src/value.d.ts +0 -109
  35. package/dist/src/alu/src/value.js +0 -300
  36. package/dist/src/alu/test/alu.test.d.ts +0 -1
  37. package/dist/src/alu/test/alu.test.js +0 -764
  38. package/dist/src/bytes.d.ts +0 -14
  39. package/dist/src/bytes.js +0 -59
  40. package/dist/src/canon.d.ts +0 -26
  41. package/dist/src/canon.js +0 -57
  42. package/dist/src/config.d.ts +0 -111
  43. package/dist/src/config.js +0 -91
  44. package/dist/src/derive/src/deduction.d.ts +0 -125
  45. package/dist/src/derive/src/deduction.js +0 -155
  46. package/dist/src/derive/src/index.d.ts +0 -7
  47. package/dist/src/derive/src/index.js +0 -11
  48. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  49. package/dist/src/derive/src/priority-queue.js +0 -73
  50. package/dist/src/derive/src/rewrite.d.ts +0 -56
  51. package/dist/src/derive/src/rewrite.js +0 -100
  52. package/dist/src/derive/src/trie.d.ts +0 -90
  53. package/dist/src/derive/src/trie.js +0 -217
  54. package/dist/src/derive/test/derive.test.d.ts +0 -1
  55. package/dist/src/derive/test/derive.test.js +0 -122
  56. package/dist/src/extension.d.ts +0 -37
  57. package/dist/src/extension.js +0 -7
  58. package/dist/src/geometry.d.ts +0 -148
  59. package/dist/src/geometry.js +0 -529
  60. package/dist/src/index.d.ts +0 -16
  61. package/dist/src/index.js +0 -18
  62. package/dist/src/ingest-cache.d.ts +0 -41
  63. package/dist/src/ingest-cache.js +0 -161
  64. package/dist/src/mind/articulation.d.ts +0 -6
  65. package/dist/src/mind/articulation.js +0 -99
  66. package/dist/src/mind/attention.d.ts +0 -72
  67. package/dist/src/mind/attention.js +0 -898
  68. package/dist/src/mind/canonical.d.ts +0 -29
  69. package/dist/src/mind/canonical.js +0 -88
  70. package/dist/src/mind/graph-search.d.ts +0 -270
  71. package/dist/src/mind/graph-search.js +0 -847
  72. package/dist/src/mind/index.d.ts +0 -5
  73. package/dist/src/mind/index.js +0 -5
  74. package/dist/src/mind/junction.d.ts +0 -95
  75. package/dist/src/mind/junction.js +0 -262
  76. package/dist/src/mind/learning.d.ts +0 -47
  77. package/dist/src/mind/learning.js +0 -233
  78. package/dist/src/mind/match.d.ts +0 -113
  79. package/dist/src/mind/match.js +0 -446
  80. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  81. package/dist/src/mind/mechanisms/alu.js +0 -29
  82. package/dist/src/mind/mechanisms/cast.d.ts +0 -35
  83. package/dist/src/mind/mechanisms/cast.js +0 -461
  84. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  85. package/dist/src/mind/mechanisms/confluence.js +0 -225
  86. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  87. package/dist/src/mind/mechanisms/cover.js +0 -179
  88. package/dist/src/mind/mechanisms/extraction.d.ts +0 -67
  89. package/dist/src/mind/mechanisms/extraction.js +0 -342
  90. package/dist/src/mind/mechanisms/recall.d.ts +0 -13
  91. package/dist/src/mind/mechanisms/recall.js +0 -235
  92. package/dist/src/mind/mind.d.ts +0 -273
  93. package/dist/src/mind/mind.js +0 -562
  94. package/dist/src/mind/pipeline-mechanism.d.ts +0 -142
  95. package/dist/src/mind/pipeline-mechanism.js +0 -213
  96. package/dist/src/mind/pipeline.d.ts +0 -20
  97. package/dist/src/mind/pipeline.js +0 -185
  98. package/dist/src/mind/primitives.d.ts +0 -63
  99. package/dist/src/mind/primitives.js +0 -251
  100. package/dist/src/mind/rationale.d.ts +0 -134
  101. package/dist/src/mind/rationale.js +0 -162
  102. package/dist/src/mind/reasoning.d.ts +0 -15
  103. package/dist/src/mind/reasoning.js +0 -162
  104. package/dist/src/mind/recognition.d.ts +0 -20
  105. package/dist/src/mind/recognition.js +0 -246
  106. package/dist/src/mind/resonance.d.ts +0 -23
  107. package/dist/src/mind/resonance.js +0 -0
  108. package/dist/src/mind/trace.d.ts +0 -15
  109. package/dist/src/mind/trace.js +0 -73
  110. package/dist/src/mind/traverse.d.ts +0 -113
  111. package/dist/src/mind/traverse.js +0 -488
  112. package/dist/src/mind/types.d.ts +0 -186
  113. package/dist/src/mind/types.js +0 -84
  114. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  115. package/dist/src/rabitq-ivf/src/database.js +0 -201
  116. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  117. package/dist/src/rabitq-ivf/src/index.js +0 -4
  118. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  119. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  120. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  121. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  122. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  123. package/dist/src/rabitq-ivf/src/rabitq.js +0 -283
  124. package/dist/src/sema.d.ts +0 -31
  125. package/dist/src/sema.js +0 -63
  126. package/dist/src/store-sqlite.d.ts +0 -184
  127. package/dist/src/store-sqlite.js +0 -942
  128. package/dist/src/store.d.ts +0 -654
  129. package/dist/src/store.js +0 -1612
  130. package/dist/src/vec.d.ts +0 -31
  131. package/dist/src/vec.js +0 -109
@@ -1,461 +0,0 @@
1
- // counterfactual.ts — Counterfactual Transfer / CAST (Section 4 of the mind).
2
- //
3
- // When a query weaves together byte-string evidence from multiple independently-
4
- // learnt structures (disjoint run alignments, literal or distributional), CAST
5
- // attempts to transfer structure between them — substitution, redirection, or
6
- // analogical comparison — producing a counterfactual answer that goes beyond what
7
- // the ordinary cover-and-extract pipeline can reach.
8
- //
9
- // CAST is a configuration of the elementary match-and-project operation
10
- // (match.ts): matcher = alignGraded (literal W-gram runs + halo-matched pre.rec.sites),
11
- // gate = the frame gate below + analogyStrength, projection = insert / project /
12
- // juxtapose.
13
- import { read } from "../primitives.js";
14
- import { argmaxBy, hubBound } from "../traverse.js";
15
- import { analogyStrength, follow, project, reverseContext, } from "../match.js";
16
- import { joinWithBridge } from "../resonance.js";
17
- import { CONCEPT, STEP } from "../graph-search.js";
18
- import { concat2, indexOf } from "../../bytes.js";
19
- import { dominates } from "../../geometry.js";
20
- import { unexplainedLabel } from "../rationale.js";
21
- import { rItem, rNode } from "../trace.js";
22
- // ── CAST gates ────────────────────────────────────────────────────────────
23
- //
24
- // The frame gate has TWO components, both derived from the weave itself:
25
- //
26
- // 1. MIN WEAVE — the same 2 as the precondition `points.length < 2` (CAST
27
- // needs at least two aligned structures to form a weave). Frame requires
28
- // evidence BEYOND the minimum pair — a third structure agreeing — so the
29
- // depth gate is `depth > MIN_WEAVE`. One definition, two uses.
30
- //
31
- // 2. HALF-DOMINANCE — `dominates(framed, len)` (the same test
32
- // collectRegions, liftAnswer, and confluence's filler gate all use): a
33
- // span more than half scaffolding no longer discriminates its own content.
34
- // The per-byte test `dominates(depth[i], aligned)` classifies a byte as
35
- // frame; the per-run test `dominates(framedCount, runLen)` decides
36
- // whether the run is usable.
37
- //
38
- // Both are derived from structural quantities (aligned points, run length),
39
- // never tuned. The constants below are the weave's own shape, not thresholds.
40
- //
41
- // DO NOT replace the frame gates with the structural IDF (reachOf +
42
- // dominates): it was tried and empirically REFUTED (17-intelligence's
43
- // reorder probe). CAST's frame is WEAVE-LOCAL — "what the aligned
44
- // structures share among THEMSELVES" — while the IDF is corpus-global; a
45
- // phrase common to the aligned exemplars (" describe it", "the importance
46
- // of") is frame here even when it reaches only a corpus minority, and
47
- // treating it as content lets the substitution branch fire on reordered
48
- // single-fact queries. The two commonality notions coincide often, but
49
- // neither derives the other.
50
- /** The minimum number of aligned structures to form a weave — the same 2 that
51
- * gates CAST entry (`points.length < 2`). Frame requires MORE than this
52
- * minimum: `depth > MIN_WEAVE` means at least three structures agree on a
53
- * byte, so no byte is frame when only the minimum pair exists. */
54
- const MIN_WEAVE = 2;
55
- /** CAST's own entry gates, checked once here and reused by
56
- /** The main CAST entry point. Given a query and its pre-computed pre.rec.sites,
57
- * determine whether the query weaves together multiple independent learnt
58
- * structures (by graded alignment — literal first, then halo-matched pre.rec.sites).
59
- * If so, attempt substitution, redirection, AND analogical comparison —
60
- * each schema is tried independently and every one that fires yields its
61
- * OWN candidate; think's grounding decider (which already compares weights
62
- * across mechanisms) picks among them, so CAST no longer needs an internal
63
- * priority order.
64
- *
65
- * `climb`, when given, is {@link castFloor}'s own climb result — reused
66
- * instead of re-running climbAttentionAll (see the note on {@link
67
- * CastFloor}). Its gates (`query.length`, `edgeSourceCount`,
68
- * `ranked.length < 2`) MUST stay in sync with castFloor's — one is the
69
- * other's admissible lower bound, checked before this runs.
70
- *
71
- * Returns the array of {@link CastResult}s that fired (possibly empty). */
72
- export async function counterfactualTransfer(ctx, query, pre) {
73
- const quantum = ctx.space.maxGroup;
74
- if (query.length < 2 * quantum || ctx.store.edgeSourceCount() === 0) {
75
- return [];
76
- }
77
- const { roots, ranked } = await pre.attention();
78
- if (ranked.length < 2)
79
- return [];
80
- const weave = await pre.weave();
81
- const points = weave.points;
82
- const depth = weave.depth;
83
- const aligned = points.length;
84
- if (aligned < 2)
85
- return [];
86
- // ── Frame gate (half-dominance, weave-local) ─────────────────────────
87
- // A byte is FRAME when more than MIN_WEAVE aligned structures cover it
88
- // AND those structures are a majority of all aligned structures.
89
- // Per-byte: frame(i) ⇔ depth[i] > MIN_WEAVE ∧ dominates(depth[i], aligned)
90
- // Per-run: usable(r) ⇔ ¬dominates(framedCount, runLen)
91
- const isFrame = (i) => depth[i] > MIN_WEAVE && dominates(depth[i], aligned);
92
- const framedCount = (qs, qe) => {
93
- let n = 0;
94
- for (let i = qs; i < qe; i++)
95
- if (isFrame(i))
96
- n++;
97
- return n;
98
- };
99
- const usable = (qs, qe) => !dominates(framedCount(qs, qe), qe - qs);
100
- // The weave's DOMINANT is its principal STRUCTURE — the aligned point
101
- // explaining the most query bytes — not the climb's top-ranked TOPIC.
102
- // The two used to coincide (approximate votes from a query's novel spans
103
- // boosted whichever exemplar shared its frame), but the contrastive
104
- // margin ranks the query's own exact site first, and CAST's schemas all
105
- // orient around the frame-bearing structure: the substitution/redirection
106
- // seat is displaced IN the dominant, and comparison seats the analogs by
107
- // the contexts that establish their roles. Coverage is weave-local and
108
- // derived (sum of aligned run lengths); ties keep the ranked order.
109
- let dominant = points[0];
110
- let domCover = -1;
111
- for (const p of points) {
112
- let cover = 0;
113
- for (const r of p.runs)
114
- cover += r.qe - r.qs;
115
- if (cover > domCover) {
116
- domCover = cover;
117
- dominant = p;
118
- }
119
- }
120
- const isRoot = (id) => roots.some((r) => r.anchor === id);
121
- // The weave must touch a COMMITTED point of attention: the dominant
122
- // structure itself, or another aligned point the climb committed to.
123
- if (!points.some((p) => isRoot(p.anchor)))
124
- return [];
125
- const woven = points.some((p) => p.runs.some((r) => !pre.rec.sites.some((s) => r.qs >= s.start && r.qe <= s.end)));
126
- if (!woven)
127
- return [];
128
- const t = ctx.trace?.enter("counterfactual", [
129
- rItem(query, "query"),
130
- ]);
131
- // Each schema tried below RECORDS its candidate (when it fires) rather than
132
- // returning immediately — every schema that succeeds contributes its own
133
- // candidate, and the grounding decider's own weight comparison (not CAST's
134
- // former internal priority) picks among them.
135
- //
136
- // `accounted` is SCHEMA-SPECIFIC, not the whole weave's alignment: a
137
- // schema only actually TRANSFERS BETWEEN the two points its own logic
138
- // names (substitution: the filled subject + the displaced seat;
139
- // redirection: the displaced seat + the named substitute; comparison:
140
- // the dominant + its analog) — a THIRD point the weave happened to align
141
- // but this schema never touched contributes nothing to what THIS answer
142
- // explains. Pricing every schema against the SAME "every kept point's
143
- // every run" span would let the cheapest schema win on move-cost alone
144
- // regardless of which one actually used more of the query; pricing it
145
- // against only a fragment of even its OWN two points (e.g. one run
146
- // instead of the point's full aligned evidence) is just as wrong the
147
- // other way — it starves an otherwise-correct schema of credit for
148
- // evidence it legitimately relied on. Each call site below passes the
149
- // full run set of exactly the points ITS OWN transfer used — no more,
150
- // no less.
151
- const runSpans = (p) => p.runs.map((r) => [r.qs, r.qe]);
152
- const results = [];
153
- const record = (answer, note, used, moves, accounted) => {
154
- if (answer === null)
155
- return;
156
- ctx.trace?.step("castSchema", [rItem(query, "query")], [rItem(answer, "answer")], note);
157
- results.push({
158
- bytes: answer,
159
- used: used ?? new Set(),
160
- accounted,
161
- moves,
162
- unexplained: unexplainedLabel(query, accounted),
163
- });
164
- };
165
- ctx.trace?.step("alignStructures", [rItem(query, "query")], points.map((p) => rNode(ctx, p.anchor, "structure", p.vote)), "the independent learnt structures the query weaves, by graded alignment");
166
- const lastRun = (p) => p.runs[p.runs.length - 1];
167
- const qv = pre.guide;
168
- // ── SUBSTITUTION ──────────────────────────────────────────────────
169
- const fillerOf = (s) => {
170
- const r = s.runs[0];
171
- return r.cs < quantum
172
- ? s.ctx.subarray(0, r.cs + (r.qe - r.qs))
173
- : query.subarray(r.qs, r.qe);
174
- };
175
- const beforeOf = (p, r) => argmaxBy(points.filter((s) => s !== p && lastRun(s).qe <= r.qs &&
176
- s.runs[0].cs < quantum &&
177
- usable(s.runs[0].qs, s.runs[0].qe)), (s) => lastRun(s).qs, -Infinity, true)?.item;
178
- const displacement = points
179
- .map((p) => {
180
- const r = p.runs[0];
181
- if (r.cs < quantum || !usable(r.qs, r.qe)) {
182
- return null;
183
- }
184
- const before = beforeOf(p, r);
185
- if (before === undefined)
186
- return null;
187
- if (r.cs > fillerOf(before).length + quantum)
188
- return null;
189
- return { p, before, depth: p.ctx.length - r.cs };
190
- })
191
- .filter((c) => c !== null);
192
- const picked = argmaxBy(displacement, (c) => c.depth, -Infinity, true);
193
- const proj = picked?.item.p ?? null;
194
- const subj = picked?.item.before ?? null;
195
- if (proj !== null && subj !== null) {
196
- const seat = proj.runs[0];
197
- const filler = fillerOf(subj);
198
- const tail = proj.ctx.subarray(seat.cs);
199
- let answer = await joinWithBridge(ctx, filler, tail);
200
- const fwd = await follow(ctx, proj.anchor, qv);
201
- if (fwd !== null && indexOf(answer, fwd, 0) < 0) {
202
- answer = concat2(answer, fwd);
203
- }
204
- ctx.trace?.step("projectCounterfactual", [
205
- rItem(filler, "filler", subj.anchor),
206
- rNode(ctx, proj.anchor, "displaced-structure"),
207
- ], [rItem(answer, "projection")], "transfer the displaced structure onto the subject filler (seat substitution)");
208
- record(answer, "counterfactual substitution — the subject fills the analog's seat", new Set([subj.anchor, proj.anchor]),
209
- // The acts performed: one seat INSERT projection + one edge FOLLOW.
210
- STEP + STEP,
211
- // What substitution actually READ: the two points it transfers
212
- // between — the subject filling the seat, and the displaced
213
- // structure whose seat it fills — not every OTHER point the weave
214
- // happened to align (a third, unrelated point in the same weave
215
- // contributes nothing to what substitution itself explains).
216
- [...runSpans(subj), ...runSpans(proj)]);
217
- }
218
- // ── REDIRECTION ────────────────────────────────────────────────────
219
- const last = points.reduce((a, b) => lastRun(b).qs > lastRun(a).qs ? b : a);
220
- // Displacement test, capped at the hub bound: a hub anchor can carry a
221
- // corpus-sized fan-out, and each continuation costs a full byte
222
- // reconstruction plus an O(|query|·|bytes|) scan. The first √N edges (the
223
- // same insertion-order convention chooseNext caps by) decide; past a hub's
224
- // cap the test reads "none of the established continuations appears".
225
- const domNext = ctx.store.nextFirst(dominant.anchor, hubBound(ctx));
226
- const displaced = domNext
227
- .every((n) => indexOf(query, read(ctx, n), 0) < 0);
228
- if (last !== dominant &&
229
- last.runs[0].cs === 0 && displaced &&
230
- usable(last.runs[0].qs, last.runs[0].qe)) {
231
- const g = await project(ctx, last.anchor, qv);
232
- if (g !== null) {
233
- ctx.trace?.step("projectCounterfactual", [
234
- rNode(ctx, dominant.anchor, "displaced-structure"),
235
- rNode(ctx, last.anchor, "substitute"),
236
- ], [rItem(g, "projection")], "the substitute's own fact replaces the displaced structure's answer");
237
- record(g, "counterfactual redirection — the named substitute's fact is followed", new Set([dominant.anchor, last.anchor]),
238
- // One forward projection across the substitute's own fact.
239
- STEP,
240
- // What redirection READ: the displaced structure's own recognized
241
- // seat (still explained — this schema RECOGNIZES it as the slot
242
- // being overridden, it just doesn't answer from it) plus the named
243
- // substitute's own aligned run — not every OTHER point the weave
244
- // happened to align.
245
- [...runSpans(dominant), ...runSpans(last)]);
246
- }
247
- }
248
- // ── COMPARISON ─────────────────────────────────────────────────────
249
- // Collect every qualifying non-dominant point as a candidate analog.
250
- // When a point's own anchor is structurally at the wrong level
251
- // (e.g. a long exemplar sentence whose halo does not resemble the
252
- // dominant's), its nextOf targets often point to the right level — the
253
- // person / concept the exemplar is about. Trying both prevents a
254
- // seed-dependent failure where the climb ranks an exemplar above a
255
- // person node and the person node is excluded from points by run-
256
- // overlap trimming.
257
- // The seat that establishes a candidate's role: the REVERSE projection
258
- // (the context it follows), voiced by the query gist — falling back to the
259
- // candidate's own bytes when it follows nothing. DELIBERATE STRENGTHENING
260
- // over the pre-refactor code: reverseContext also returns null when the
261
- // picked context READS EMPTY (a dangling id degrades to zero bytes), so a
262
- // corrupted-store read now falls back here instead of voicing a hollow
263
- // seat into the comparison — the same "empty bytes are no grounding"
264
- // invariant project() has always enforced.
265
- // A node that only ever CONTINUES — an edge SOURCE with no predecessor —
266
- // is a learnt CONTEXT (a stored question-shaped frame), not an entity.
267
- // Voicing it verbatim answers a question with a question (the observed
268
- // cast echo: two stored questions concatenated as an "answer"). The
269
- // context's role is established by what it LEADS TO, so its seat is its
270
- // own continuation — the fact — with the reverse projection and the raw
271
- // bytes as the ordinary fallbacks for genuine entities.
272
- const seatOfNode = async (id, fallback) => {
273
- if (ctx.store.prevCount(id) === 0 && ctx.store.hasNext(id)) {
274
- const fwd = await follow(ctx, id, qv);
275
- if (fwd !== null)
276
- return fwd;
277
- }
278
- return reverseContext(ctx, id, qv) ?? fallback;
279
- };
280
- const seatOf = (p) => seatOfNode(p.anchor, p.ctx);
281
- const analogs = [];
282
- for (const p of points) {
283
- if (p === dominant)
284
- continue;
285
- // Push the point's own anchor only when its context fits within
286
- // the query (the seat sentence must not dominate the comparison).
287
- if (p.ctx.length <= query.length &&
288
- indexOf(dominant.ctx, p.ctx, 0) < 0 &&
289
- indexOf(p.ctx, dominant.ctx, 0) < 0 &&
290
- indexOf(query, p.ctx, 0) < 0) {
291
- analogs.push({ anchor: p.anchor, point: p });
292
- }
293
- // Reach through to the point's continuation targets regardless
294
- // of the point's own context length: when the point is a leaf
295
- // (exemplar sentence), its nextOf is the hub (person / concept)
296
- // that makes a genuine cross-domain analog, and the hub's own
297
- // (shorter) context will be the seat.
298
- // Capped like every fan-out: a hub anchor's full continuation list is
299
- // corpus-sized, and each candidate costs a read plus O(|query|·|bytes|)
300
- // scans — only the first √N (insertion order, the same convention
301
- // chooseNext caps by) are reachable as analogs.
302
- for (const nid of ctx.store.nextFirst(p.anchor, hubBound(ctx))) {
303
- const nctx = read(ctx, nid);
304
- if (nctx.length > query.length ||
305
- indexOf(dominant.ctx, nctx, 0) >= 0 ||
306
- indexOf(nctx, dominant.ctx, 0) >= 0 ||
307
- indexOf(query, nctx, 0) >= 0)
308
- continue;
309
- analogs.push({ anchor: nid, point: null });
310
- }
311
- }
312
- let bestAnalog = null;
313
- let bestSim = 0;
314
- for (const c of analogs) {
315
- const sim = await analogyStrength(ctx, dominant.anchor, c.anchor);
316
- ctx.trace?.step("tryAnalog", [
317
- rNode(ctx, dominant.anchor, "dominant"),
318
- rNode(ctx, c.anchor, "candidate", sim),
319
- ], [], `analogy strength ${sim.toFixed(4)}`);
320
- if (sim > bestSim) {
321
- bestSim = sim;
322
- bestAnalog = c;
323
- }
324
- }
325
- // When every candidate fails the similarity gates (halo company — now
326
- // deterministic signatures, see sema.ts — and the shared-frame tier),
327
- // fall back to a candidate that is a genuine structural hub (edges in
328
- // BOTH directions). A hub node — a person, concept, or category — is
329
- // the kind of thing that makes sense to compare across domains. A leaf
330
- // value (extracted span, terminal answer) has edges in at most one
331
- // direction and comparing it would preempt the extraction pipeline,
332
- // which is the right mechanism for those. A fallback comparison carries
333
- // NO similarity evidence — it stays honest only because the grounding
334
- // decider weighs it against mechanisms that explain more of the query
335
- // (extraction accounts its whole located envelope; see extraction.ts).
336
- //
337
- // WHICH hub: not the first in `analogs` order — that order flows from the
338
- // vote ranking, which flows from approximate resonance, which is seed-
339
- // dependent. Pick by evidence instead: combined edge support (prevCount +
340
- // fan-out), tie-broken by poured halo MASS (episode corroboration — the
341
- // direct distributional evidence), then by LOWEST node id. The id order
342
- // is a property of the corpus, not of the seed — but note ids are SIGNED:
343
- // byte leaves occupy −256…−1, so "lowest id" is creation order only among
344
- // multi-byte nodes and byte-value order among leaves. Either way it is
345
- // deterministic, which is all the final tie-break must be.
346
- if (bestAnalog === null && analogs.length > 0) {
347
- let hubSupport = -1;
348
- let hubMass = -1;
349
- const fanClamp = hubBound(ctx) + 1;
350
- for (const c of analogs) {
351
- // Evidence clamped at the hub bound: beyond √N + 1 the exact fan-out
352
- // no longer discriminates (every mega-hub ties at the clamp), and
353
- // counting it exactly would require the corpus-sized read.
354
- const fanOut = ctx.store.nextFirst(c.anchor, fanClamp).length;
355
- if (fanOut === 0)
356
- continue;
357
- const support = ctx.store.prevCount(c.anchor);
358
- if (support === 0)
359
- continue;
360
- const total = support + fanOut;
361
- if (total < hubSupport)
362
- continue;
363
- const mass = ctx.store.haloMass(c.anchor);
364
- if (total > hubSupport ||
365
- mass > hubMass ||
366
- (mass === hubMass && bestAnalog !== null &&
367
- c.anchor < bestAnalog.anchor)) {
368
- hubSupport = total;
369
- hubMass = mass;
370
- bestAnalog = c;
371
- }
372
- }
373
- if (bestAnalog !== null) {
374
- ctx.trace?.step("tryAnalog", [], [rNode(ctx, bestAnalog.anchor, "fallback", hubSupport)], "no candidate passed the similarity gates — using the best-supported structural hub");
375
- }
376
- }
377
- ctx.trace?.step("tryAnalog", [], bestAnalog !== null ? [rNode(ctx, bestAnalog.anchor, "best", bestSim)] : [], bestAnalog !== null
378
- ? `best analog with strength ${bestSim.toFixed(4)}`
379
- : `no analog candidate passed (${analogs.length} checked)`);
380
- // COMPARISON gate — analogical comparison seats the dominant against ONE
381
- // analog, so it presupposes the query is ABOUT a single thing. When the
382
- // consensus climb instead committed to MULTIPLE independent points of
383
- // attention (`roots.length > 1`), the query names independent topics to
384
- // FUSE — the reasoner's fuseAttention already combines them — not analogs
385
- // to compare. Firing here would juxtapose two co-scaffolded but unrelated
386
- // records (each sharing only the corpus preamble), out-accounting the
387
- // honest thin multi-root grounding with a frame echo. Derived from the
388
- // climb's own forest, never tuned; substitution/redirection stay
389
- // unaffected — they orient around a displaced seat, not a whole-topic
390
- // analogy.
391
- if (bestAnalog !== null &&
392
- dominant.ctx.length <= query.length &&
393
- roots.length <= 1) {
394
- ctx.trace?.step("validateAnalogy", [
395
- rNode(ctx, dominant.anchor, "analog", bestSim),
396
- rNode(ctx, bestAnalog.anchor, "analog", bestSim),
397
- ], [], "the two structures keep distributional company beyond chance — genuine analogs");
398
- const a = await seatOf(dominant);
399
- const b = bestAnalog.point !== null
400
- ? await seatOf(bestAnalog.point)
401
- : await seatOfNode(bestAnalog.anchor, read(ctx, bestAnalog.anchor));
402
- const answer = await joinWithBridge(ctx, a, b);
403
- record(answer, "analogical comparison — each analog voiced by the context that establishes its role", new Set([dominant.anchor, bestAnalog.anchor]),
404
- // A halo-mediated act (the analogy gate) plus two seat projections.
405
- CONCEPT + STEP + STEP,
406
- // What comparison READ: the dominant's own aligned runs, plus the
407
- // analog's aligned runs when it was itself an aligned point (a nextOf
408
- // descendant was never aligned to the query directly, so it
409
- // contributes no accounted span — its seat is graph-reached, not
410
- // query-matched).
411
- [
412
- ...runSpans(dominant),
413
- ...(bestAnalog.point !== null ? runSpans(bestAnalog.point) : []),
414
- ]);
415
- }
416
- t?.done(results.map((r) => rItem(r.bytes, "answer")), results.length > 0
417
- ? `${results.length} counterfactual schema(s) fired — the grounding decider weighs them`
418
- : "no counterfactual weave — the ordinary pipeline decides");
419
- return results;
420
- }
421
- export const castMechanism = {
422
- name: "cast",
423
- provenance: "cast",
424
- async floor(_ctx, query, pre, worthRunning) {
425
- const W = _ctx.space.maxGroup;
426
- // Cheap checks first — no pre-computation needed.
427
- if (query.length < 2 * W || _ctx.store.edgeSourceCount() === 0)
428
- return null;
429
- // CAST's floor, when it exists, is ALWAYS exactly 2*STEP — the climb and
430
- // the weave only decide whether it exists (2*STEP) or not (null), they
431
- // never tighten the number itself. So if 2*STEP already can't beat
432
- // whatever incumbent has already won this response (cover runs first —
433
- // see defaultMechanisms), no analysis can change the outcome: RETURN THE
434
- // BOUND uninvested (still admissible) and let the pipeline's own check
435
- // prune run() with the truthful "cannot beat incumbent" note. This is
436
- // the SAME admissible-floor economy worthRunning applies to run(),
437
- // applied to floor()'s own investment — uniformly, whatever mechanism
438
- // supplied the incumbent (an extension's computed result is not
439
- // special-cased; any sufficiently cheap incumbent prunes the same way).
440
- if (!worthRunning(2 * STEP))
441
- return 2 * STEP;
442
- // Now first-touch the shared analyses (climb, then the weave built on
443
- // it). If another mechanism already triggered either, this awaits the
444
- // cached result; otherwise it's computed once here and reused in run().
445
- if ((await pre.attention()).ranked.length < 2)
446
- return null;
447
- if ((await pre.weave()).points.length < 2)
448
- return null;
449
- return 2 * STEP;
450
- },
451
- async run(ctx, query, pre) {
452
- const casts = await counterfactualTransfer(ctx, query, pre);
453
- return casts.map((c) => ({
454
- bytes: c.bytes,
455
- accounted: c.accounted,
456
- moves: c.moves,
457
- used: c.used,
458
- unexplained: c.unexplained,
459
- }));
460
- },
461
- };
@@ -1,24 +0,0 @@
1
- import type { MindContext } from "../types.js";
2
- import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
3
- /** A join answer plus its elementary evidence for think's grounding decider:
4
- * `accounted` — the query spans whose votes carried the two constraint
5
- * streams; `moves` — the ladder cost of the acts performed (two constraint
6
- * matches and one meet, STEP each). `used` carries the pair's exemplar
7
- * anchors so the reasoning stage does not re-speak them. */
8
- export interface JoinResult {
9
- bytes: Uint8Array;
10
- used: ReadonlySet<number>;
11
- accounted: Array<[number, number]>;
12
- moves: number;
13
- /** A human-readable label for the query bytes the meet left unexplained —
14
- * purely diagnostic, never priced. */
15
- unexplained: string;
16
- }
17
- /** The main confluence entry point. Given a query, detect whether it weaves
18
- * two or more INDEPENDENT constraints (ranked anchors supported by disjoint
19
- * query spans), intersect the constraints' evidence by content-addressed
20
- * identity, and return the discriminative content the streams share — the
21
- * entity that satisfies all constraints at once. Null when the query is
22
- * not conjunctive or nothing lies in the intersection. */
23
- export declare function confluenceJoin(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<JoinResult | null>;
24
- export declare const confluenceMechanism: PipelineMechanism;