@hviana/sema 0.4.0 → 0.4.2

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 (154) hide show
  1. package/CONTRIBUTING.md +92 -10
  2. package/LICENSE.md +2 -2
  3. package/package.json +1 -1
  4. package/src/ingest-cache.ts +4 -0
  5. package/src/meter.ts +5 -0
  6. package/src/mind/attention.ts +18 -1
  7. package/src/mind/bridge.ts +292 -54
  8. package/src/mind/junction.ts +21 -7
  9. package/src/mind/match.ts +92 -1
  10. package/src/mind/mechanisms/cast.ts +12 -0
  11. package/src/mind/mechanisms/confluence.ts +30 -1
  12. package/src/mind/mechanisms/cover.ts +36 -4
  13. package/src/mind/mechanisms/recall.ts +21 -44
  14. package/src/mind/mind.ts +39 -2
  15. package/src/mind/pipeline-mechanism.ts +86 -4
  16. package/src/mind/pipeline.ts +1 -1
  17. package/src/mind/reasoning.ts +15 -8
  18. package/src/mind/recognition.ts +40 -0
  19. package/src/mind/resonance.ts +0 -0
  20. package/src/mind/traverse.ts +17 -15
  21. package/src/mind/types.ts +10 -0
  22. package/test/49-natural-units-synonym-bridge.test.mjs +56 -15
  23. package/dist/example/demo.d.ts +0 -1
  24. package/dist/example/demo.js +0 -39
  25. package/dist/example/train_base.d.ts +0 -87
  26. package/dist/example/train_base.js +0 -2252
  27. package/dist/src/alphabet.d.ts +0 -7
  28. package/dist/src/alphabet.js +0 -33
  29. package/dist/src/alu/src/alu.d.ts +0 -185
  30. package/dist/src/alu/src/alu.js +0 -440
  31. package/dist/src/alu/src/expr.d.ts +0 -61
  32. package/dist/src/alu/src/expr.js +0 -318
  33. package/dist/src/alu/src/index.d.ts +0 -11
  34. package/dist/src/alu/src/index.js +0 -19
  35. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  36. package/dist/src/alu/src/kernel-arith.js +0 -264
  37. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  38. package/dist/src/alu/src/kernel-bits.js +0 -152
  39. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  40. package/dist/src/alu/src/kernel-logic.js +0 -60
  41. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  42. package/dist/src/alu/src/kernel-nd.js +0 -208
  43. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  44. package/dist/src/alu/src/kernel-numeric.js +0 -366
  45. package/dist/src/alu/src/operation.d.ts +0 -168
  46. package/dist/src/alu/src/operation.js +0 -189
  47. package/dist/src/alu/src/parser.d.ts +0 -221
  48. package/dist/src/alu/src/parser.js +0 -577
  49. package/dist/src/alu/src/resonance.d.ts +0 -55
  50. package/dist/src/alu/src/resonance.js +0 -126
  51. package/dist/src/alu/src/text.d.ts +0 -31
  52. package/dist/src/alu/src/text.js +0 -73
  53. package/dist/src/alu/src/value.d.ts +0 -109
  54. package/dist/src/alu/src/value.js +0 -300
  55. package/dist/src/alu/test/alu.test.d.ts +0 -1
  56. package/dist/src/alu/test/alu.test.js +0 -764
  57. package/dist/src/bytes.d.ts +0 -14
  58. package/dist/src/bytes.js +0 -59
  59. package/dist/src/canon.d.ts +0 -26
  60. package/dist/src/canon.js +0 -57
  61. package/dist/src/config.d.ts +0 -111
  62. package/dist/src/config.js +0 -91
  63. package/dist/src/derive/src/deduction.d.ts +0 -136
  64. package/dist/src/derive/src/deduction.js +0 -159
  65. package/dist/src/derive/src/index.d.ts +0 -8
  66. package/dist/src/derive/src/index.js +0 -11
  67. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  68. package/dist/src/derive/src/priority-queue.js +0 -73
  69. package/dist/src/derive/src/rewrite.d.ts +0 -56
  70. package/dist/src/derive/src/rewrite.js +0 -100
  71. package/dist/src/derive/src/trie.d.ts +0 -90
  72. package/dist/src/derive/src/trie.js +0 -217
  73. package/dist/src/derive/test/derive.test.d.ts +0 -1
  74. package/dist/src/derive/test/derive.test.js +0 -122
  75. package/dist/src/extension.d.ts +0 -37
  76. package/dist/src/extension.js +0 -7
  77. package/dist/src/geometry.d.ts +0 -175
  78. package/dist/src/geometry.js +0 -823
  79. package/dist/src/index.d.ts +0 -17
  80. package/dist/src/index.js +0 -19
  81. package/dist/src/ingest-cache.d.ts +0 -41
  82. package/dist/src/ingest-cache.js +0 -161
  83. package/dist/src/meter.d.ts +0 -171
  84. package/dist/src/meter.js +0 -269
  85. package/dist/src/mind/articulation.d.ts +0 -6
  86. package/dist/src/mind/articulation.js +0 -99
  87. package/dist/src/mind/attention.d.ts +0 -414
  88. package/dist/src/mind/attention.js +0 -2065
  89. package/dist/src/mind/bridge.d.ts +0 -39
  90. package/dist/src/mind/bridge.js +0 -738
  91. package/dist/src/mind/canonical.d.ts +0 -34
  92. package/dist/src/mind/canonical.js +0 -93
  93. package/dist/src/mind/graph-search.d.ts +0 -294
  94. package/dist/src/mind/graph-search.js +0 -996
  95. package/dist/src/mind/index.d.ts +0 -9
  96. package/dist/src/mind/index.js +0 -5
  97. package/dist/src/mind/junction.d.ts +0 -134
  98. package/dist/src/mind/junction.js +0 -331
  99. package/dist/src/mind/learning.d.ts +0 -75
  100. package/dist/src/mind/learning.js +0 -270
  101. package/dist/src/mind/match.d.ts +0 -166
  102. package/dist/src/mind/match.js +0 -565
  103. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  104. package/dist/src/mind/mechanisms/alu.js +0 -36
  105. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  106. package/dist/src/mind/mechanisms/cast.js +0 -773
  107. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  108. package/dist/src/mind/mechanisms/confluence.js +0 -225
  109. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  110. package/dist/src/mind/mechanisms/cover.js +0 -203
  111. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  112. package/dist/src/mind/mechanisms/extraction.js +0 -300
  113. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  114. package/dist/src/mind/mechanisms/recall.js +0 -388
  115. package/dist/src/mind/mind.d.ts +0 -330
  116. package/dist/src/mind/mind.js +0 -594
  117. package/dist/src/mind/pipeline-mechanism.d.ts +0 -172
  118. package/dist/src/mind/pipeline-mechanism.js +0 -382
  119. package/dist/src/mind/pipeline.d.ts +0 -49
  120. package/dist/src/mind/pipeline.js +0 -275
  121. package/dist/src/mind/primitives.d.ts +0 -66
  122. package/dist/src/mind/primitives.js +0 -306
  123. package/dist/src/mind/rationale.d.ts +0 -139
  124. package/dist/src/mind/rationale.js +0 -163
  125. package/dist/src/mind/reasoning.d.ts +0 -40
  126. package/dist/src/mind/reasoning.js +0 -272
  127. package/dist/src/mind/recognition.d.ts +0 -20
  128. package/dist/src/mind/recognition.js +0 -463
  129. package/dist/src/mind/resonance.d.ts +0 -23
  130. package/dist/src/mind/resonance.js +0 -0
  131. package/dist/src/mind/trace.d.ts +0 -15
  132. package/dist/src/mind/trace.js +0 -73
  133. package/dist/src/mind/traverse.d.ts +0 -124
  134. package/dist/src/mind/traverse.js +0 -649
  135. package/dist/src/mind/types.d.ts +0 -323
  136. package/dist/src/mind/types.js +0 -130
  137. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  138. package/dist/src/rabitq-ivf/src/database.js +0 -201
  139. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  140. package/dist/src/rabitq-ivf/src/index.js +0 -4
  141. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  142. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  143. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  144. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  145. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  146. package/dist/src/rabitq-ivf/src/rabitq.js +0 -283
  147. package/dist/src/sema.d.ts +0 -31
  148. package/dist/src/sema.js +0 -63
  149. package/dist/src/store-sqlite.d.ts +0 -184
  150. package/dist/src/store-sqlite.js +0 -942
  151. package/dist/src/store.d.ts +0 -678
  152. package/dist/src/store.js +0 -1703
  153. package/dist/src/vec.d.ts +0 -31
  154. package/dist/src/vec.js +0 -109
@@ -1,565 +0,0 @@
1
- // match.ts — the ONE elementary operation behind every generalising mechanism:
2
- // MATCH a learned structure against bytes, then PROJECT along a learned
3
- // relation, gated by a derived threshold.
4
- //
5
- // Every grounding/generalisation mechanism in the mind is a configuration of
6
- // this single (matcher, direction, gate) operation:
7
- //
8
- // mechanism matcher direction gate
9
- // ─────────────────── ────────────────────────────── ───────────── ────────────────
10
- // cover follow-edge exact (content-addressed) forward —
11
- // concept hop halo sibling forward conceptThreshold
12
- // recall tier 0–1 identity / whole-query gist fwd/reverse identityBar
13
- // skill extraction locate() ladder (exact→halo→ read-out per-step gates
14
- // gist) on the exemplar's frames
15
- // CAST substitution alignGraded() (graded ladder: insert frame shapes
16
- // literal W-grams → halo sites)
17
- // CAST comparison analogyStrength() (halo, juxtapose significanceBar
18
- // direct or mutual-sibling)
19
- // multi-hop pivot byte containment forward —
20
- // articulation halo sibling substitute conceptThreshold
21
- //
22
- // This module holds the shared vocabulary those configurations are built
23
- // from — the MATCHERS (locate, alignRuns, alignGraded, analogyStrength) and
24
- // the PROJECTIONS (follow, conceptHop, reverseContext, project) — so each
25
- // mechanism file states only its configuration, never its own copy of the
26
- // machinery. The gates all live in geometry.ts (derived, never tuned).
27
- import { cosine } from "../vec.js";
28
- import { conceptThreshold, identityBar, significanceBar } from "../geometry.js";
29
- import { indexOf } from "../bytes.js";
30
- import { leafIdRun } from "./canonical.js";
31
- import { foldTree, gistOf, perceive, read, resolve } from "./primitives.js";
32
- import { argmaxCosine, chooseAmong, chooseNext, guidedFirst, hubBound, hubCap, } from "./traverse.js";
33
- import { recognise, segment } from "./recognition.js";
34
- // ═══════════════════════════════════════════════════════════════════════════
35
- // MATCHERS — locating learned structure in/against bytes, by graded strictness
36
- // ═══════════════════════════════════════════════════════════════════════════
37
- /** The graded LOCATE ladder: find `needle` in `haystack` starting at
38
- * `fromPos`, strictest matcher first, relaxing only when the stricter one
39
- * fails. This is the read-out matcher skill extraction locates exemplar
40
- * frames with.
41
- *
42
- * 1. exact — literal byte match (the fast path).
43
- * 2. halo — the needle's distributional role matches a recognised query
44
- * form (gate: conceptThreshold).
45
- * 3. gist — the needle's perceived gist matches a query segment
46
- * (gate: identityBar — scale-aware).
47
- *
48
- * Returns the absolute byte position, or −1. */
49
- export function locate(ctx, haystack, needle, fromPos, sites) {
50
- // 1. Exact match — fast, preserves backward compatibility.
51
- const exact = indexOf(haystack.subarray(fromPos), needle, 0);
52
- if (exact >= 0)
53
- return fromPos + exact;
54
- // 2. Halo-based: the frame bytes' distributional role matches a query form.
55
- if (sites && sites.length > 0) {
56
- const frameId = resolve(ctx, needle);
57
- if (frameId !== null) {
58
- const frameHalo = ctx.store.halo(frameId);
59
- if (frameHalo) {
60
- const bestSite = bestHaloMate(ctx, frameHalo, sites.filter((s) => s.start >= fromPos), (s) => ctx.store.halo(s.payload));
61
- if (bestSite !== null)
62
- return bestSite.item.start;
63
- }
64
- }
65
- }
66
- // 3. Gist resonance: the frame's perceived gist against query segments.
67
- const frameGist = gistOf(ctx, needle);
68
- const segments = segment(ctx, haystack.subarray(fromPos));
69
- // The gist tier claims the WHOLE needle appears as a segment — an
70
- // identity claim over `needle.length` bytes, so its bar is the
71
- // scale-aware {@link identityBar} (one river window of tolerated foreign
72
- // bytes), not the fixed estimator floor. For quantum-sized frames the
73
- // two coincide; for long needles the fixed bar accepted segments that
74
- // differed by whole windows.
75
- const bestSeg = argmaxCosine(frameGist, segments, (s) => s.v, identityBar(ctx.store.D, ctx.space.maxGroup, needle.length), true);
76
- if (bestSeg !== null)
77
- return fromPos + bestSeg.item.start;
78
- return -1;
79
- }
80
- /** The ALIGNED matcher: maximal literal matching runs between `query` and
81
- * `ct` (a learned context's bytes), by seed-and-extend over
82
- * `space.maxGroup`-sized n-gram seeds. Where locate() finds ONE position of
83
- * a short frame, this finds EVERY run two whole structures share — the
84
- * matcher CAST detects a woven query with. Returns non-overlapping runs
85
- * sorted by query position. */
86
- export function alignRuns(ctx, query, ct) {
87
- if (ctx.meter) {
88
- ctx.meter.alignments++;
89
- // The alignment family's honest unit: the seed index is O(|query|) but
90
- // the run extension is O(|query|·|ct|) in the worst case, and a weave
91
- // that starts scanning conversation-length contexts shows up HERE long
92
- // before it shows up in a call count.
93
- ctx.meter.alignCells += query.length * ct.length;
94
- }
95
- // MEASURED AND REFUTED — seeding at the write side's unit floor W−1 instead
96
- // of W. `canonicalWindows` interns both lengths precisely so a form
97
- // straddling a group boundary is reachable from either cut, and the runs
98
- // found here are exactly such remnants: test/29 C1's query shares only `ce `
99
- // with `Ice is cold` — three bytes, never seeded at W, so that structure
100
- // enters the weave carrying nothing but the scaffolding run every exemplar
101
- // shares. But this is a byte MATCHER between two streams, not an index, and
102
- // at W−1 the corpus is dense with spurious 3-byte agreements: the extra runs
103
- // reshuffle which point claims which span, and test/29 A2 loses its analog.
104
- // C1 does not pass either way. The store's unit floor does not transfer to
105
- // the aligner's seed length.
106
- const quantum = Math.min(ctx.space.maxGroup, ct.length);
107
- if (quantum < 1 || query.length < quantum)
108
- return [];
109
- const gram = (b, at) => {
110
- let s = "";
111
- for (let i = 0; i < quantum; i++)
112
- s += String.fromCharCode(b[at + i]);
113
- return s;
114
- };
115
- const seeds = new Map();
116
- for (let i = 0; i + quantum <= query.length; i++) {
117
- const k2 = gram(query, i);
118
- const bucket = seeds.get(k2);
119
- if (bucket === undefined)
120
- seeds.set(k2, [i]);
121
- else
122
- bucket.push(i);
123
- }
124
- const found = [];
125
- for (let j = 0; j + quantum <= ct.length; j++) {
126
- const bucket = seeds.get(gram(ct, j));
127
- if (bucket === undefined)
128
- continue;
129
- for (const i of bucket) {
130
- if (i > 0 && j > 0 && query[i - 1] === ct[j - 1])
131
- continue;
132
- let len = quantum;
133
- while (i + len < query.length && j + len < ct.length &&
134
- query[i + len] === ct[j + len])
135
- len++;
136
- found.push({ qs: i, qe: i + len, cs: j, len });
137
- }
138
- }
139
- found.sort((a, b) => b.len - a.len);
140
- const runs = [];
141
- for (const r of found) {
142
- const clash = runs.some((o) => (r.qs < o.qe && o.qs < r.qe) ||
143
- (r.cs < o.cs + (o.qe - o.qs) && o.cs < r.cs + r.len));
144
- if (!clash)
145
- runs.push({ qs: r.qs, qe: r.qe, cs: r.cs });
146
- }
147
- return runs.sort((a, b) => a.qs - b.qs);
148
- }
149
- /** The GRADED alignment matcher: extends literal W-gram alignment
150
- * ({@link alignRuns}) with halo-matched recognised sites in query regions
151
- * that have no literal coverage. Same ladder as {@link locate}: literal
152
- * first, then distributional role (halo-matched sites, gate:
153
- * conceptThreshold, enforced by {@link bestHaloMate}). Returns weighted
154
- * runs sorted by query position.
155
- *
156
- * `querySites` are the pre-computed recognition sites for the query
157
- * (optional — when absent, only literal alignment fires and graded degrades
158
- * to the original behaviour). Context sites are recognised internally. */
159
- export function alignGraded(ctx, query, contextBytes, querySites) {
160
- const lit = alignRuns(ctx, query, contextBytes);
161
- const out = lit.map((r) => ({ ...r, weight: 1 }));
162
- if (!querySites || querySites.length === 0)
163
- return out;
164
- // Mark query positions ALREADY covered by literal runs — halo fills gaps.
165
- // If literal coverage is already complete, skip the halo step entirely
166
- // (recognise is O(|ctx|·W) — wasted when every byte is accounted for).
167
- const covered = new Uint8Array(query.length);
168
- let gaps = false;
169
- for (const r of lit) {
170
- for (let i = r.qs; i < r.qe; i++)
171
- covered[i] = 1;
172
- }
173
- for (let i = 0; i < query.length; i++) {
174
- if (!covered[i]) {
175
- gaps = true;
176
- break;
177
- }
178
- }
179
- if (!gaps)
180
- return out;
181
- // Recognise sites in the exemplar context — structural positions for halo
182
- // matching. (Circular import with recognition.ts is safe: recognise() is
183
- // called lazily, never at module load — the same pattern `segment` uses.)
184
- const ctxSites = recognise(ctx, contextBytes).sites;
185
- if (ctxSites.length === 0)
186
- return out;
187
- // Context sites with halos, hoisted: the same set serves every query site.
188
- const ctxCands = ctxSites.filter((cs) => ctx.store.hasHalo(cs.payload));
189
- if (ctxCands.length === 0)
190
- return out;
191
- // Candidate halos, also hoisted (lazily, first query site that needs them):
192
- // bestHaloMate consults every candidate's halo PER QUERY SITE, and sites
193
- // share the candidate set — without this memo the same few dozen halos were
194
- // re-fetched thousands of times per response. Distinct payloads can repeat
195
- // across sites, hence the map by payload id.
196
- const ctxHalos = new Map();
197
- const ctxHaloOf = (cs) => {
198
- let h = ctxHalos.get(cs.payload);
199
- if (h === undefined) {
200
- h = ctx.store.halo(cs.payload);
201
- ctxHalos.set(cs.payload, h);
202
- }
203
- return h;
204
- };
205
- for (const qs of querySites) {
206
- // Only sites that overlap UNCOVERED query regions add new evidence.
207
- let touchesGap = false;
208
- for (let i = qs.start; i < qs.end; i++) {
209
- if (!covered[i]) {
210
- touchesGap = true;
211
- break;
212
- }
213
- }
214
- if (!touchesGap)
215
- continue;
216
- const qHalo = ctx.store.halo(qs.payload);
217
- if (!qHalo)
218
- continue;
219
- // bestHaloMate already gates at conceptThreshold — no second check needed.
220
- const match = bestHaloMate(ctx, qHalo, ctxCands, ctxHaloOf);
221
- if (match === null)
222
- continue;
223
- out.push({
224
- qs: qs.start,
225
- qe: qs.end,
226
- cs: match.item.start,
227
- weight: match.score,
228
- });
229
- }
230
- out.sort((a, b) => a.qs - b.qs);
231
- return out;
232
- }
233
- /** The IN-LIST halo matcher: the best halo-mate for `halo` among EXPLICIT
234
- * candidates, above the concept threshold — the list counterpart of
235
- * {@link haloSiblings}, which asks the halo INDEX for candidates instead.
236
- * Behind locate()'s halo step and articulation's voice matching; a third
237
- * "best halo among these" decision must come here, not inline. */
238
- export function bestHaloMate(ctx, halo, items, haloOf) {
239
- return argmaxCosine(halo, items, haloOf, conceptThreshold(ctx.store.D));
240
- }
241
- /** The HALO-SIBLING matcher: the nodes that keep the same distributional
242
- * company as `id`, nearest first — `resonateHalo` filtered to exclude the
243
- * node itself and everything below `bar` (default: the concept threshold).
244
- * `halo`, when the caller has already read the node's halo row, is reused
245
- * instead of refetched (one read per relation). Returns [] for a node with
246
- * no halo. The one sibling enumeration behind the concept hop, the
247
- * reasoning stage's synonym expansion, and the analogy matcher below. */
248
- const haloSiblingMemo = new WeakMap();
249
- export async function haloSiblings(ctx, id, halo, bar = conceptThreshold(ctx.store.D)) {
250
- // Per-response memo for the DEFAULT-ARGUMENT reading (the one the concept
251
- // hop, the bridge's synonym tier, and reasoning's synonym expansion all
252
- // use): the same node's siblings are asked for repeatedly within one
253
- // response (bridge pairs share sides), each a full halo-ANN query, and the
254
- // store is read-only while a response is in flight. Keyed by the response
255
- // lifecycle object (ctx.climbMemo — fresh per respond, nulled after).
256
- // Calls with an explicit halo or bar (analogyStrength's gated reading)
257
- // bypass the memo — their filter differs.
258
- const memoable = halo === undefined &&
259
- bar === conceptThreshold(ctx.store.D) && ctx.climbMemo !== null;
260
- let memo;
261
- if (memoable) {
262
- memo = haloSiblingMemo.get(ctx.climbMemo);
263
- if (memo === undefined) {
264
- haloSiblingMemo.set(ctx.climbMemo, memo = new Map());
265
- }
266
- const hit = memo.get(id);
267
- if (hit !== undefined)
268
- return hit;
269
- }
270
- const h = halo ?? ctx.store.halo(id);
271
- const out = h
272
- ? (await ctx.store.resonateHalo(h, ctx.cfg.haloQueryK))
273
- .filter((sib) => sib.id !== id && sib.score >= bar)
274
- : [];
275
- if (memo !== undefined)
276
- memo.set(id, out);
277
- return out;
278
- }
279
- export async function analogyStrength(ctx, a, b) {
280
- const ha = ctx.store.halo(a);
281
- const hb = ctx.store.halo(b);
282
- if (ha && hb) {
283
- const bar = significanceBar(ctx.store.D);
284
- const direct = cosine(ha, hb);
285
- if (direct >= bar)
286
- return { score: direct, halo: true };
287
- const sibsA = await haloSiblings(ctx, a, ha, bar);
288
- const sibsB = await haloSiblings(ctx, b, hb, bar);
289
- let best = 0;
290
- for (const x of sibsA) {
291
- if (x.id === b)
292
- continue;
293
- const y = sibsB.find((s) => s.id === x.id);
294
- if (y !== undefined) {
295
- best = Math.max(best, Math.min(x.score, y.score));
296
- }
297
- }
298
- if (best > 0)
299
- return { score: best, halo: true };
300
- }
301
- return { score: sharedFrameStrength(ctx, a, b), halo: false };
302
- }
303
- /** The STRUCTURAL analogy tier: two nodes are analogs when their byte
304
- * streams share a LEARNT frame — a content-addressed flat form of at least
305
- * one full river window (W bytes, the perception quantum) that occurs in
306
- * BOTH. This is what "playing the same role" means structurally: "Ice is
307
- * cold" and "Steel is hard" share the learnt " is " frame even though they
308
- * keep disjoint distributional company. Halos measure company by IDENTITY
309
- * (company signatures — see sema.ts), so unrelated-company analogs must be
310
- * validated by the frame itself, not by content leaking through halo
311
- * vectors. Strength is the shared learnt coverage of the SHORTER side —
312
- * a fraction, comparable to the cosine tiers above. Derived: the window
313
- * is maxGroup, the same quantum differsByOneWindow and canonicalChunkId
314
- * measure by; no tuned constants. */
315
- export function sharedFrameStrength(ctx, a, b) {
316
- const W = ctx.space.maxGroup;
317
- const A = read(ctx, a);
318
- const B = read(ctx, b);
319
- if (A.length < W || B.length < W)
320
- return 0;
321
- // Mark every byte of the shorter side covered by a learnt W-window that
322
- // also occurs in the longer side.
323
- const [s, l] = A.length <= B.length ? [A, B] : [B, A];
324
- const covered = new Uint8Array(s.length);
325
- for (let off = 0; off + W <= s.length; off++) {
326
- const win = s.subarray(off, off + W);
327
- // Learnt: the window resolves as a content-addressed flat form.
328
- const ids = leafIdRun(ctx, s, off, off + W);
329
- if (ids === null || ctx.store.findBranch(ids) === null)
330
- continue;
331
- if (indexOf(l, win, 0) < 0)
332
- continue;
333
- covered.fill(1, off, off + W);
334
- }
335
- let n = 0;
336
- for (let i = 0; i < s.length; i++)
337
- n += covered[i];
338
- return n >= W ? n / s.length : 0;
339
- }
340
- // ═══════════════════════════════════════════════════════════════════════════
341
- // PROJECTIONS — what a matched node is projected ALONG (the direction)
342
- // ═══════════════════════════════════════════════════════════════════════════
343
- /** FORWARD through a synonym: the continuation an edge-less node borrows from
344
- * a concept (halo) sibling — resonate the node's halo, take the first
345
- * sibling above the concept threshold that itself has a direct edge. */
346
- export async function conceptHop(ctx, id) {
347
- for (const sib of await haloSiblings(ctx, id)) {
348
- const hop = guidedFirst(ctx, sib.id);
349
- if (hop !== undefined)
350
- return hop;
351
- }
352
- return null;
353
- }
354
- /** FORWARD projection: follow continuation edges from a node to its fixpoint.
355
- * The first hop may cross a concept (halo) link — a synonym. The rest
356
- * follow direct edges. Convergence is intrinsic: the seen set guards
357
- * against cycles. `guide` disambiguates multi-continuation nodes by
358
- * resonance. */
359
- export async function follow(ctx, id, guide) {
360
- const seen = new Set([id]);
361
- // First hop: a direct edge, else a concept sibling's edge (the synonym).
362
- let next = chooseNext(ctx, id, guide);
363
- if (next === undefined) {
364
- const hop = await conceptHop(ctx, id);
365
- if (hop === null)
366
- return null;
367
- next = hop;
368
- }
369
- // Direct successors to the fixpoint. Only the FIXPOINT's bytes are
370
- // returned, so the walk tracks node ids and reads bytes exactly once at
371
- // the end — a K-hop chain used to pay K full reconstructions and discard
372
- // K−1 of them.
373
- while (!seen.has(next)) {
374
- seen.add(next);
375
- const fwd = chooseNext(ctx, next, guide);
376
- if (fwd === undefined || seen.has(fwd))
377
- break;
378
- next = fwd;
379
- }
380
- return read(ctx, next);
381
- }
382
- /** REVERSE projection: the context a learnt continuation follows, voiced as
383
- * bytes. A common continuation ("Yes.") follows MANY contexts; with a
384
- * `guide` the context whose gist resonates with the query wins (seat
385
- * symmetry) — without one, the most-corroborated context wins (poured halo
386
- * MASS, the direct measure of how many episodes established it), falling
387
- * back to first-learnt on equal mass. Among many predecessors RECIPROCAL
388
- * ones (mutual edges) are preferred when any exist (RC5). Callers that
389
- * HAVE a query gist must pass it, or they silently change disambiguation
390
- * regime.
391
- *
392
- * `rev`, when the caller has already materialised prevOf (one read per
393
- * relation — a hub's reverse fan-in is corpus-sized), is reused instead of
394
- * refetched. Returns null when there is no predecessor or the picked
395
- * context reads empty (a zero-length context is no grounding: an empty
396
- * Uint8Array is truthy, and returning it would flow a hollow "answer"
397
- * onward). */
398
- export function reverseContext(ctx, id, guide, rev) {
399
- // CAPPED default read: only the first √N predecessors are ever candidates
400
- // (hubCap below / in chooseAmong), so only they are read. hubBound ≥ 2
401
- // keeps the single-predecessor shortcut exact.
402
- const candidates = rev ?? ctx.store.prevFirst(id, hubBound(ctx));
403
- if (candidates.length === 0)
404
- return null;
405
- // RECIPROCAL PREFERENCE: among many predecessors, one that `id` also
406
- // continues TO (cand → id AND id → cand both learnt) is a mutually
407
- // established pairing — the strongest structural evidence a predecessor
408
- // can carry (bidirectional training deposits both directions of a genuine
409
- // pair). A bare predecessor is one episode's adjacency; guide-resonance
410
- // over bare predecessors favours whichever stored document merely
411
- // CONTAINS the query's bytes (the linear fold's cosine is byte overlap —
412
- // the observed "merci → unrelated French document" failure). One capped
413
- // forward read decides; when no reciprocal exists, behaviour is unchanged
414
- // — bare predecessors ARE the honest answer for a shared deposited
415
- // continuation (two questions → one answer; audited by 31-audit C1), and
416
- // this arm serves every mechanism's reverse projection, so abstaining
417
- // here starves far more than the one containment failure it would fix.
418
- let pool = candidates;
419
- if (candidates.length > 1) {
420
- const fwd = new Set(ctx.store.nextFirst(id, hubBound(ctx)));
421
- if (fwd.size > 0) {
422
- const mutual = candidates.filter((c) => fwd.has(c));
423
- if (mutual.length > 0)
424
- pool = mutual;
425
- }
426
- }
427
- const pick = pool.length === 1
428
- ? pool[0]
429
- : guide
430
- ? chooseAmong(ctx, pool, guide).id
431
- : pickByMass(ctx, pool);
432
- const g = read(ctx, pick);
433
- return g.length > 0 ? g : null;
434
- }
435
- /** The most-corroborated candidate by poured halo mass (first-seen wins a
436
- * tie). Capped at √N candidates by insertion order — the same hub bound
437
- * every fan-out walk uses. */
438
- function pickByMass(ctx, ids) {
439
- const capped = hubCap(ctx, ids);
440
- let best = capped[0];
441
- let bestMass = ctx.store.haloMass(best);
442
- for (let i = 1; i < capped.length; i++) {
443
- const mass = ctx.store.haloMass(capped[i]);
444
- if (mass > bestMass) {
445
- best = capped[i];
446
- bestMass = mass;
447
- }
448
- }
449
- return best;
450
- }
451
- /** THE projection: ground a matched node to answer bytes — FORWARD to its
452
- * continuation fixpoint (which may cross a concept hop), else REVERSE to
453
- * the context it follows. This is the direction ladder every mechanism's
454
- * final grounding step reduces to. */
455
- export async function project(ctx, id, guide) {
456
- const fc = await follow(ctx, id, guide);
457
- if (fc)
458
- return fc;
459
- return reverseContext(ctx, id, guide);
460
- }
461
- // ── The span-shape family ───────────────────────────────────────────────────
462
- //
463
- // "Is this answer drawn from this context?" has TWO formally distinct
464
- // readings, and the pair plus the anchor classifier built on them are SHARED
465
- // machinery — extraction proposes span-shaped exemplars with them, the
466
- // shared `Precomputed.spanShapedOf` container computes them, and fusion
467
- // (reasoning.ts) gates on the strict one. They lived inside
468
- // mechanisms/extraction.ts, so `pipeline-mechanism.ts` and `reasoning.ts`
469
- // both had to import back OUT of a specific mechanism — an inversion the
470
- // mechanism market forbids (AGENTS §2.6: the shared contract may not depend
471
- // on any one mechanism; §2.5: a shared matcher belongs to this family, never
472
- // to a mechanism's private helpers). Deleting extraction must not break the
473
- // shared container, so they live here.
474
- //
475
- // • isSpanShaped — the OPEN reading (sparse in-order embedding).
476
- // • containsSpan — the STRICT reading (contiguous run or resolved node).
477
- // • skillExemplar — classify one anchor into (context, answer) using them.
478
- //
479
- // The two readings are NOT interchangeable; AGENTS §2.5 pins the distinction
480
- // and each function's own doc states what breaks if it is substituted.
481
- /** Check whether an anchor is a span-shaped skill exemplar: it represents a
482
- * fact whose context and answer together form a span-in-context pattern.
483
- * If the anchor has a nextOf continuation, that is the answer and the anchor
484
- * itself is the context. Otherwise the anchor's prevOf parents provide
485
- * candidate contexts, and the longest one whose span is span-shaped wins. */
486
- export async function skillExemplar(ctx, anchor, guide) {
487
- if (ctx.store.hasNext(anchor)) {
488
- const contextBytes = read(ctx, anchor);
489
- const answerBytes = await follow(ctx, anchor, guide);
490
- if (answerBytes !== null && isSpanShaped(ctx, contextBytes, answerBytes)) {
491
- return { contextBytes, answerBytes };
492
- }
493
- return null;
494
- }
495
- const answerBytes = read(ctx, anchor);
496
- // Candidate contexts, capped at the hub bound (a common answer's reverse
497
- // fan-in is corpus-sized).
498
- const capped = ctx.store.prevFirst(anchor, hubBound(ctx));
499
- const spanShaped = [];
500
- for (const p of capped) {
501
- const ctxB = read(ctx, p);
502
- if (ctxB.length > 0 && isSpanShaped(ctx, ctxB, answerBytes)) {
503
- spanShaped.push({ id: p, bytes: ctxB });
504
- }
505
- }
506
- if (spanShaped.length === 0)
507
- return null;
508
- // Among span-shaped contexts, the longest wins (the smallest spanning frame
509
- // heuristic's dual: more frame to locate in the query); the query gist,
510
- // when given, breaks LENGTH TIES via chooseAmong — the same reverse-regime
511
- // disambiguator every context pick uses, whose gist cache spares the
512
- // re-fold this block once paid per tied candidate. Same strict first-seen
513
- // tie-break as the hand loop it replaces.
514
- const maxLen = Math.max(...spanShaped.map((s) => s.bytes.length));
515
- const longest = spanShaped.filter((s) => s.bytes.length === maxLen);
516
- let contextBytes = longest[0].bytes;
517
- if (guide && longest.length > 1) {
518
- const pick = chooseAmong(ctx, longest.map((s) => s.id), guide).id;
519
- contextBytes = longest.find((s) => s.id === pick).bytes;
520
- }
521
- return { contextBytes, answerBytes };
522
- }
523
- /** Whether the answer is a SPARSE subsequence of the context (bytes in
524
- * order, arbitrary gaps) — the OPEN span-shape reading (see the section
525
- * note above). This is what lets extraction validate a MULTI-PIECE
526
- * exemplar whose answer is stitched from several context runs — but it is
527
- * deliberately permissive, so it must never be used as evidence that one
528
- * span was "drawn from" another (see {@link containsSpan} for that).
529
- *
530
- * There is deliberately NO containsSpan pre-check here: strict containment
531
- * IMPLIES the subsequence embedding (a contiguous run, or a resolved node —
532
- * whose content-addressed identity means its bytes occur contiguously — is
533
- * an in-order embedding with zero gaps), so the scan below decides alone,
534
- * with the same truth value. The old pre-check re-perceived the context
535
- * (a full river fold) per CANDIDATE in skillExemplar's √N-capped loop —
536
- * pure cost, no discrimination. */
537
- export function isSpanShaped(_ctx, context, answer) {
538
- let ai = 0;
539
- for (let ci = 0; ci < context.length && ai < answer.length; ci++) {
540
- if (context[ci] === answer[ai])
541
- ai++;
542
- }
543
- return ai === answer.length;
544
- }
545
- /** STRICT containment: the answer's resolved node appears in the context's
546
- * folded tree, or the answer occurs as one CONTIGUOUS byte run of the
547
- * context. This is real evidence the answer was drawn from the context.
548
- * Fusion gates on this — the sparse-subsequence reading of
549
- * {@link isSpanShaped} is trivially satisfied by short answers over long
550
- * queries ("cold" is a gap-tolerant subsequence of most sentences holding
551
- * c…o…l…d in order), and gating fusion on it silently starved multi-topic
552
- * queries of their further points of attention. */
553
- export function containsSpan(ctx, context, answer) {
554
- const ansId = resolve(ctx, answer);
555
- if (ansId !== null) {
556
- let found = false;
557
- foldTree(ctx, perceive(ctx, context), 0, (_n, _s, _e, node) => {
558
- if (node === ansId)
559
- found = true;
560
- });
561
- if (found)
562
- return true;
563
- }
564
- return indexOf(context, answer, 0) >= 0;
565
- }
@@ -1,4 +0,0 @@
1
- import type { Alu } from "../../alu/src/alu.js";
2
- import type { PipelineMechanism } from "../pipeline-mechanism.js";
3
- /** Wrap the ALU as a {@link PipelineMechanism}. */
4
- export declare function aluToMechanism(alu: Alu): PipelineMechanism;
@@ -1,36 +0,0 @@
1
- // mechanisms/alu.ts — the ALU wrapped as an ordinary PipelineMechanism.
2
- //
3
- // The ALU is a self-contained sublibrary (src/alu) that knows nothing about
4
- // the pipeline; this adapter is the whole coupling. Its `parse` populates
5
- // `pre.computed` before the grounding loop; the cover mechanism handles
6
- // masking (see mechanisms/cover.ts). The ALU's own trace steps
7
- // (`evalComputation`) are emitted inside its `parse()`. A user extension
8
- // joins the same way — see MindOptions.mechanismFactories.
9
- import { STEP } from "../graph-search.js";
10
- import { unexplainedLabel } from "../rationale.js";
11
- /** Wrap the ALU as a {@link PipelineMechanism}. */
12
- export function aluToMechanism(alu) {
13
- return {
14
- name: "alu",
15
- // Not a cover derivation: cover.ts composes an answer by walking
16
- // recognised query STRUCTURE; the ALU evaluates a recognised expression
17
- // to its authoritative result and hands the bytes back untouched. It
18
- // shares cover's near-zero floor (computation always wins, masked into
19
- // cover's own search — see mechanisms/cover.ts), but the candidate this
20
- // produces is not one of cover's derivations, so it carries its own
21
- // honest label, the same way extract/cast/recall each carry theirs.
22
- provenance: "alu",
23
- parse: (query) => alu.parse(query),
24
- async floor(_ctx, _query, pre, _worthRunning) {
25
- return pre.computed.length > 0 ? 0 : null;
26
- },
27
- async run(_ctx, query, pre) {
28
- return pre.computed.map((u) => ({
29
- bytes: u.bytes,
30
- accounted: [[u.i, u.j]],
31
- moves: STEP,
32
- unexplained: unexplainedLabel(query, [[u.i, u.j]]),
33
- }));
34
- },
35
- };
36
- }