@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,463 +0,0 @@
1
- // recognition.ts — Section 2 of the mind:
2
- // Address + Read over byte streams — decompose a query into its known forms.
3
- //
4
- // recognise — structural + canonical decomposition into every stored form
5
- // that leads somewhere (has a continuation edge or a halo).
6
- // segment — leaf-parent segmentation using the geometry's own groupings.
7
- import { rItem } from "./trace.js";
8
- import { canonResolve, foldTree, gistOf, latin1Key, perceive, resolve, } from "./primitives.js";
9
- import { atomIsHub, corpusN, leadsSomewhere } from "./traverse.js";
10
- import { chainReach, leafIdAt, leafIdRun } from "./canonical.js";
11
- import { isChunk } from "../sema.js";
12
- /** Decompose a byte stream into every stored form that leads somewhere
13
- * (has a continuation edge or a halo). Two complementary readings:
14
- *
15
- * • structural — walk the query's own perceived tree, naming each subtree
16
- * by findLeaf at the leaves and findBranch above. Catches every form
17
- * aligned to the query's segmentation.
18
- *
19
- * • canonical — re-derive the store's segmentation directly: at each byte,
20
- * the longest known leaf, chained into flat branches. Names forms the
21
- * query's own cut cannot, and records sub-leaf boundaries as `splits`.
22
- *
23
- * Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus. */
24
- export function recognise(ctx, bytes) {
25
- // Content-keyed memo — works for both single-turn respond() and multi-turn
26
- // respondTurn() (where the map persists across calls). ALWAYS consulted,
27
- // regardless of tracing — matching perceive()'s own memo, which carries no
28
- // trace gate at all. This memo is NOT an optional accelerator: recogniseImpl
29
- // walks the query's perceived tree via foldTree, whose subtree-resolution
30
- // fast path (see primitives.ts) skips invoking `visit` — and therefore
31
- // skips EMITTING SITES — for any subtree already cached in
32
- // ctx._resolvedSubtrees. A multi-turn conversation's stable-prefix fold
33
- // deliberately shares node OBJECTS across turns, so by the second call on
34
- // the exact same bytes, large swaths of the tree are already cached and
35
- // foldTree stops short of recursing into them — a second recogniseImpl
36
- // call on the SAME bytes is not idempotent; it silently finds FEWER sites
37
- // than the first (observed live: 31 sites → 5 on an immediate repeat
38
- // call). Skipping this memo "only while tracing" used to mean every
39
- // traced turn re-ran recogniseImpl from scratch at every one of the many
40
- // call sites that recognise the same query (cover, reason, articulate...),
41
- // each subsequent call silently more incomplete than the last — measurably
42
- // changing which mechanism grounds the answer, not just costing time. The
43
- // trace step must still fire on every call regardless (a cache hit is not
44
- // silent), so it is emitted here directly instead of only inside
45
- // recogniseImpl.
46
- if (ctx.recogniseMemo) {
47
- const key = latin1Key(bytes);
48
- const hit = ctx.recogniseMemo.get(key);
49
- if (hit !== undefined) {
50
- if (ctx.meter)
51
- ctx.meter.recogniseHits++;
52
- ctx.trace?.step("recognise", [rItem(bytes, "query")], hit.sites.map((s) => rItem(bytes.subarray(s.start, s.end), "form", s.payload, [
53
- s.start,
54
- s.end,
55
- ])), `decompose the query into ${hit.sites.length} learnt form(s) that ` +
56
- `lead somewhere (over ${hit.leaves.length} perceived leaves) [cached]`);
57
- return hit;
58
- }
59
- const fresh = recogniseImpl(ctx, bytes);
60
- ctx.recogniseMemo.set(key, fresh);
61
- return fresh;
62
- }
63
- return recogniseImpl(ctx, bytes);
64
- }
65
- function recogniseImpl(ctx, bytes) {
66
- if (ctx.meter) {
67
- ctx.meter.recognitions++;
68
- ctx.meter.recognisedBytes += bytes.length;
69
- }
70
- const store = ctx.store;
71
- const sites = [];
72
- const leaves = [];
73
- const splits = new Set();
74
- const starts = new Set();
75
- // The same cuts in ASCENDING order. The post-order walk below visits
76
- // leaf-parents left to right, so appending as they are added keeps this
77
- // sorted with no comparison — which is what lets the composite search find
78
- // its candidates by binary search instead of rescanning the whole set.
79
- const startList = [];
80
- if (bytes.length === 0)
81
- return { sites, leaves, splits, starts };
82
- // Span-resolve memo for THIS call: the structural pass (sub-runs inside
83
- // leaf-parents) and the canonical pass (leaf-id chains) probe overlapping
84
- // spans, and each resolve() is a full fold of the sub-span (fresh subarray
85
- // objects — the per-response perceive memo cannot see them). Keyed
86
- // numerically by (start, end); resolve is pure and the store is read-only
87
- // here, so a hit is exact.
88
- const spanIds = new Map();
89
- const resolveSpan = (start, end) => {
90
- const key = start * (bytes.length + 1) + end;
91
- let id = spanIds.get(key);
92
- if (id === undefined) {
93
- id = resolve(ctx, bytes.subarray(start, end));
94
- spanIds.set(key, id);
95
- }
96
- return id;
97
- };
98
- // Byte atoms (implicit negative-id single-byte leaves) are admitted as
99
- // recognised sites only while atoms can still DISCRIMINATE at this corpus
100
- // scale (see {@link atomIsHub}). On a small store a single-letter fact
101
- // ("a" → "A") is genuine learnt content and its site is essential; on a
102
- // large one every letter of every query would otherwise become a
103
- // "recognised form" — the bridge then finds junction connectors between
104
- // bare letters, cover follows edges hanging off them, and pure noise
105
- // ("qq8f3kz9…") grounds to an arbitrary learnt sentence instead of
106
- // silence. Atoms stay available as leaves (PASS-carried literals) and
107
- // through exact tier-0 resolution regardless.
108
- const atomsAreHubs = atomIsHub(ctx, corpusN(ctx));
109
- // Distinct probes (structural exact match, canon fallback, edge trims at
110
- // several offsets) can legitimately re-derive the SAME (start, end, id)
111
- // site from different tree nodes — a wide edge-trim search is exactly
112
- // this on purpose (see below). Duplicate site entries are not wrong
113
- // evidence, but they double the weight cover's derivation search gives
114
- // that span, distorting its cost model — the same span must count once.
115
- const seen = new Set();
116
- const emit = (start, end, id) => {
117
- if (id < 0 && atomsAreHubs)
118
- return;
119
- // A SITE MUST SPAN ONE RIVER WINDOW. Below W, byte overlap is chance,
120
- // not evidence — the principle identityBar already states ("below one
121
- // river window, byte overlap is chance") and the bridge's attestedQ
122
- // already applies ("spans shorter than W carry no window of their own").
123
- // No new constant.
124
- //
125
- // This REPLACES the false premise it used to share with fuse() and
126
- // tryChain: those gates asked "does this offset sit on a fold boundary?"
127
- // and read the answer from `starts`, which is exactly {0, W, 2W, …}
128
- // because riverFold groups fixed-arity — arithmetic, not evidence.
129
- //
130
- // Measured on the 17.9M-node store, over the sites of 7 probes (1 good,
131
- // 11 junk by hand-labelling, corrected for whole-query forms):
132
- // len >= W rejects "hi"(2) "of"(2) "is"(2) "di"(2) "the"(3),
133
- // admits "Eiffel Tower"(12) and both whole-query forms
134
- // len >= W-1 admits "the" — W-1 is the write side's straddle
135
- // neighbour for RETRIEVAL, never a claim about units
136
- // §2.7 saturation admits 11/11 junk: edgeAncestors on a site node
137
- // reaches 1..48 contexts, so dominates(ctx, N) needs
138
- // ctx > 162805 and never fires; every site reads DISC
139
- // rarity does not separate: "hi" has 1 container, "the" 572
140
- //
141
- // A span covering the WHOLE query is exempt: then it is not a fragment of
142
- // something longer, it is the question ("hi" asked on its own).
143
- if (atomsAreHubs && end - start < ctx.space.maxGroup &&
144
- !(start === 0 && end === bytes.length))
145
- return;
146
- const key = start + "," + end + "," + id;
147
- if (seen.has(key))
148
- return;
149
- seen.add(key);
150
- if (leadsSomewhere(ctx, id)) {
151
- sites.push({ start, end, payload: id });
152
- }
153
- };
154
- // ── structural: the query's own perceived tree ──────────────────────
155
- starts.add(0);
156
- startList.push(0);
157
- foldTree(ctx, perceive(ctx, bytes), 0, (n, start, end, node) => {
158
- if (n.kids === null) {
159
- leaves.push({ start, end, bytes: n.leaf ?? new Uint8Array(0), node });
160
- }
161
- if (node !== null)
162
- emit(start, end, node);
163
- // Canonical fallback: a subtree whose exact content-addressed lookup
164
- // missed may still be a stored form under the response's equivalence
165
- // (case, width, whitespace — whatever the injected canonicalizer says).
166
- // O(subtree bytes) per miss, memoised per response; a no-op when no
167
- // canonicalizer was injected or the store has no canon index. A raw
168
- // leaf (n.kids === null) is single-byte and handled by the byte-atom
169
- // path above instead — canon equivalence only applies to composites.
170
- else if (n.kids !== null) {
171
- const cid = canonResolve(ctx, bytes.subarray(start, end));
172
- if (cid !== null)
173
- emit(start, end, cid);
174
- // The edge-trim fallbacks below remove 1 byte from a side; the
175
- // remainder must still be a composite (>= 2 bytes, the same floor
176
- // n.kids !== null enforces above) rather than degenerate into
177
- // single-byte-atom territory, which atomIsHub already governs
178
- // separately.
179
- else if (end - start - 1 >= 2) {
180
- // The chunk's own boundary is drawn by content geometry, not by
181
- // any notion of "form" — it can include one edge byte the query's
182
- // fold happened to attach here that the trained span never had
183
- // (e.g. a separator from the preceding chunk). The core has no
184
- // idea what that byte means; it only knows resolve()/canonResolve
185
- // are self-verifying (hash-then-verify, same discipline as every
186
- // content lookup here), so a blind one-byte-shorter guess on
187
- // either edge costs nothing when wrong and is trustworthy when it
188
- // hits. Two extra probes, only on the already-failed miss path.
189
- const left = resolve(ctx, bytes.subarray(start + 1, end));
190
- if (left !== null)
191
- emit(start + 1, end, left);
192
- const right = resolve(ctx, bytes.subarray(start, end - 1));
193
- if (right !== null)
194
- emit(start, end - 1, right);
195
- // A misalignment wider than one byte (e.g. more than one edge
196
- // separator swallowed) is not itself geometry-quantized — the
197
- // WRITE side's canonical index (canonicalWindows) interns sliding
198
- // W−1/W-length windows over leaf ids at EVERY offset, not just
199
- // radix-aligned ones (see canonical.ts) — so the offset that
200
- // recovers a trained span can be anything, not a multiple of W.
201
- // What IS bounded is how far it's worth looking: chainReach(W)=W²,
202
- // the same reach the canonical pass (tryChain) trusts for a chain
203
- // rebuilt off the query's own fold. Every candidate offset is
204
- // gated by store.findBranch(leafIds) first — the SAME cheap,
205
- // fold-free existence check tryChain already uses — so the extra
206
- // resolve() fold (the real cost) is only paid when a branch could
207
- // plausibly exist there, not for every offset. The node itself is
208
- // also bounded to chunk-scale (end - start <= W²): widening this at
209
- // whole-query/root scale can rediscover a smaller subtree's own
210
- // content as a second, overlapping site the structural walk's own
211
- // finer recursion already emits correctly on its own — a duplicate
212
- // that downstream derivation can stitch into a wrong answer.
213
- const W = ctx.space.maxGroup;
214
- for (let k = 1; end - start <= W * W && k <= W * W && start + k < end - 1; k++) {
215
- const lIds = leafIdRun(ctx, bytes, start + k, end);
216
- if (lIds !== null && store.findBranch(lIds) !== null) {
217
- const eLeft = resolve(ctx, bytes.subarray(start + k, end));
218
- if (eLeft !== null)
219
- emit(start + k, end, eLeft);
220
- }
221
- const rIds = leafIdRun(ctx, bytes, start, end - k);
222
- if (rIds !== null && store.findBranch(rIds) !== null) {
223
- const eRight = resolve(ctx, bytes.subarray(start, end - k));
224
- if (eRight !== null)
225
- emit(start, end - k, eRight);
226
- }
227
- }
228
- // A REAL extra word at the left edge (a discourse connective like
229
- // "And " prepended to a follow-up turn — not boundary noise, actual
230
- // content the injected canonicalizer has no equivalence for) shows
231
- // up as a canon-miss too big for the chunk-scale search above: the
232
- // turn is its OWN segment, so it can be turn/segment-scale, not
233
- // chunk-scale. Widening the size bound itself reopens the root-scale
234
- // false-positive this module already fixed once (test/46); widening the
235
- // SEARCH instead does not, because every candidate is a cut the query's
236
- // OWN fold drew (`starts`, the same set the canonical pass privileges
237
- // with full chain reach) — fold EVIDENCE, never a blind guess.
238
- //
239
- // The candidates are the fold's own segment starts inside this span, in
240
- // order. They used to be probed at `start + k*W`, which assumed cuts
241
- // land on multiples of W; content-defined cuts do not, so that stride
242
- // tested offsets no segment ever began at and this search silently
243
- // never fired (test/44 pins it). Still bounded to W candidates, each
244
- // one O(1) from the sorted cut list before paying for a real
245
- // canonResolve fold — canonResolve, not resolve()/findBranch, because
246
- // the gap here is often exactly the kind of equivalence (case, in the
247
- // live trace) canon exists for, not an exact-content coincidence.
248
- // A deposit's ROOT is a whole-stream node, and a stream's ends are not
249
- // content cuts — so an embedded occurrence of a trained form reproduces
250
- // its SEGMENTS (which are offset-free) but never its root. What is
251
- // being looked for is therefore a suffix of this span that happens to be
252
- // a whole trained form, and its left edge can only be a cut the fold
253
- // itself drew. Candidates are taken from the RIGHT, nearest the end
254
- // first: the form ends where this node ends, so its start is near it.
255
- // Left-to-right was wrong — in test/44 the target's start is the 6th cut
256
- // from the end but the 12th from the beginning.
257
- //
258
- // `starts` is still filling (this runs inside the post-order walk), but
259
- // post-order guarantees every chunk BELOW this span is already in it —
260
- // exactly the set wanted. Bounded to chainReach(W) candidates, the same
261
- // reach the canonical pass trusts, so cost stays O(reach · span).
262
- let hi = startList.length; // first index past the last usable cut
263
- let lo = 0;
264
- while (lo < hi) {
265
- const mid = (lo + hi) >> 1;
266
- if (startList[mid] < end - 1)
267
- lo = mid + 1;
268
- else
269
- hi = mid;
270
- }
271
- const reach = chainReach(W);
272
- for (let k = 0; k < reach; k++) {
273
- const p = startList[lo - 1 - k];
274
- if (p === undefined || p <= start)
275
- break;
276
- const cid = canonResolve(ctx, bytes.subarray(p, end));
277
- if (cid !== null)
278
- emit(p, end, cid);
279
- }
280
- }
281
- }
282
- if (isChunk(n)) {
283
- starts.add(start);
284
- if (startList[startList.length - 1] !== start)
285
- startList.push(start);
286
- // Try every sub-span within this leaf-parent.
287
- const leafOffsets = [];
288
- let off = start;
289
- for (const k of n.kids) {
290
- leafOffsets.push(off);
291
- off += k.leaf?.length ?? 0;
292
- }
293
- // Sub-spans starting at i > 0 begin INSIDE the chunk, at an offset the
294
- // query's own fold did not itself choose as a boundary — the same
295
- // opportunistic byte-atom-chain risk `tryChain`'s `boundary` gate
296
- // guards below (see its comment). Only the chunk's own left edge
297
- // (i === 0, already registered in `starts` above) carries the fold's
298
- // evidence; interior sub-starts are exempt from the guard only while
299
- // atoms themselves still discriminate at this corpus scale.
300
- for (let i = 0; i < n.kids.length; i++) {
301
- if (i > 0 && atomsAreHubs)
302
- break;
303
- const subIds = [];
304
- for (let j = i; j < n.kids.length; j++) {
305
- const kj = n.kids[j];
306
- if (kj.kids !== null || !kj.leaf)
307
- break;
308
- const lid = store.findLeaf(kj.leaf);
309
- if (lid === null)
310
- break;
311
- subIds.push(lid);
312
- const branch = store.findBranch(subIds);
313
- if (branch === null)
314
- continue;
315
- const subEnd = leafOffsets[j] + (kj.leaf?.length ?? 0);
316
- const resolved = resolveSpan(leafOffsets[i], subEnd);
317
- if (resolved !== null)
318
- emit(leafOffsets[i], subEnd, resolved);
319
- }
320
- }
321
- }
322
- });
323
- // ── canonical: longest-known-leaf re-segmentation ──────────────────
324
- const W = ctx.space.maxGroup;
325
- const singleLeaf = new Array(bytes.length).fill(null);
326
- for (let p = 0; p < bytes.length; p++) {
327
- const id = leafIdAt(ctx, bytes, p);
328
- if (id !== null)
329
- singleLeaf[p] = { id, end: p + 1 };
330
- }
331
- const leafFrom = (p) => {
332
- if (p >= bytes.length)
333
- return null;
334
- return singleLeaf[p];
335
- };
336
- const chunkEnd = new Uint32Array(bytes.length);
337
- const sorted = [...starts].sort((a, b) => a - b);
338
- for (let si = 0; si < sorted.length; si++) {
339
- const chunkStart = sorted[si];
340
- const chunkLimit = si + 1 < sorted.length ? sorted[si + 1] : bytes.length;
341
- for (let p = chunkStart; p < chunkLimit; p++) {
342
- chunkEnd[p] = chunkLimit;
343
- }
344
- }
345
- // A chain rebuilt from a NON-boundary offset (the query's own perceived
346
- // cut, `starts`, never chose to segment here) is opportunistic: the same
347
- // byte-atom coincidence the hub guard above already exists for, just
348
- // spelled over 2+ leaves instead of 1. At small corpus scale that's fine
349
- // — coincidence is rare and every chain is real evidence (see `atomIsHub`).
350
- // Past the scale where atoms themselves stop discriminating, the same
351
- // uniform-expectation argument bounds a CHAIN'S commonality too: it is at
352
- // least as rare as its rarest atom, so a store where atoms are hubs makes
353
- // interior chain reconstructions no more trustworthy than the atoms they
354
- // are built from ("hi" resolving out of "W[hi]ch" is exactly this: two
355
- // hub-scale atoms, chained at an offset nothing in the query's own fold
356
- // selected). Chains that start ON a boundary carry the fold's own
357
- // evidence instead and are exempt.
358
- //
359
- // NOTE (2026-07-24): that last sentence is FALSE — `starts` is exactly
360
- // {0, W, 2W, …} (riverFold groups fixed-arity), so the exemption is
361
- // arithmetic, not evidence. Removing it wholesale was measured and
362
- // REVERTED: it also drops legitimate multi-byte chains (the 12-byte
363
- // "Eiffel Tower" site vanished with it). The premise is wrong but the
364
- // trust it stood in for is real; a replacement signal is still open work.
365
- // See bench/README.md.
366
- const tryChain = (p, maxIds, boundary) => {
367
- const first = leafFrom(p);
368
- if (!first)
369
- return;
370
- emit(p, first.end, first.id);
371
- const ids = [first.id];
372
- let pos = first.end;
373
- let prevId = null;
374
- for (let depth = 1; pos < bytes.length && ids.length <= maxIds; depth++) {
375
- const nx = leafFrom(pos);
376
- if (!nx)
377
- break;
378
- ids.push(nx.id);
379
- pos = nx.end;
380
- if (store.findBranch(ids) === null)
381
- continue;
382
- if (!boundary && atomsAreHubs)
383
- continue;
384
- const id = resolveSpan(p, pos);
385
- if (id === null || id === prevId)
386
- continue;
387
- prevId = id;
388
- emit(p, pos, id);
389
- }
390
- };
391
- for (let p = 0; p < bytes.length; p++) {
392
- if (starts.has(p)) {
393
- tryChain(p, chainReach(W), true); // boundary start — full reach
394
- }
395
- else {
396
- const limit = chunkEnd[p] + W;
397
- tryChain(p, Math.min(limit - p, chainReach(W)), false);
398
- }
399
- }
400
- // ── splits: a form boundary that does not fall on a leaf edge ────────
401
- const leafEdges = new Set([bytes.length]);
402
- for (const lf of leaves)
403
- leafEdges.add(lf.start);
404
- for (const s of sites) {
405
- if (!leafEdges.has(s.start))
406
- splits.add(s.start);
407
- if (!leafEdges.has(s.end))
408
- splits.add(s.end);
409
- }
410
- ctx.trace?.step("recognise", [rItem(bytes, "query")], sites.map((s) => rItem(bytes.subarray(s.start, s.end), "form", s.payload, [
411
- s.start,
412
- s.end,
413
- ])), `decompose the query into ${sites.length} learnt form(s) that lead somewhere` +
414
- ` (over ${leaves.length} perceived leaves)`);
415
- return { sites, leaves, splits, starts };
416
- }
417
- /** Segment bytes using the geometry's own groupings — leaf-parent
418
- * nodes from the perceived tree, with consecutive bare leaves merged
419
- * into one segment. Each segment's gist is perceived from its bytes
420
- * IN ISOLATION, so the same content has the same gist regardless of
421
- * where it appears. */
422
- export function segment(ctx, bytes) {
423
- const tree = perceive(ctx, bytes);
424
- const out = [];
425
- let pendingStart = -1;
426
- let pendingEnd = -1;
427
- const flush = () => {
428
- if (pendingStart >= 0 && pendingEnd > pendingStart) {
429
- out.push({
430
- start: pendingStart,
431
- end: pendingEnd,
432
- v: gistOf(ctx, bytes.subarray(pendingStart, pendingEnd)),
433
- });
434
- }
435
- pendingStart = -1;
436
- pendingEnd = -1;
437
- };
438
- const walk = (n, start) => {
439
- if (n.kids === null) {
440
- const end = start + (n.leaf?.length ?? 0);
441
- if (pendingStart < 0)
442
- pendingStart = start;
443
- pendingEnd = end;
444
- return end;
445
- }
446
- if (isChunk(n)) {
447
- flush();
448
- let end = start;
449
- for (const c of n.kids)
450
- end += c.leaf?.length ?? 0;
451
- out.push({ start, end, v: gistOf(ctx, bytes.subarray(start, end)) });
452
- return end;
453
- }
454
- flush();
455
- let pos = start;
456
- for (const c of n.kids)
457
- pos = walk(c, pos);
458
- return pos;
459
- };
460
- walk(tree, 0);
461
- flush();
462
- return out;
463
- }
@@ -1,23 +0,0 @@
1
- import type { MindContext } from "./types.js";
2
- /** The connector that belongs BETWEEN two adjacent results — the graded
3
- * junction ladder described in the module note above. Returns null when
4
- * the graph holds no evidence that the two ever ran together. */
5
- export declare function bridge(ctx: MindContext, left: Uint8Array, right: Uint8Array, interiorAllowance?: number): Promise<Uint8Array | null>;
6
- /** Join two spans with the learnt connector between them, when one exists —
7
- * the composition step every out-of-search assembly (multi-topic fusion,
8
- * CAST's substitution and comparison) shares. A miss joins the pieces BARE
9
- * and is never silent: it emits the same `bridgeMiss` trace step everywhere,
10
- * so a degraded join is visible in the rationale regardless of which
11
- * mechanism paid it. (The in-search connector splice in graph-search.ts is
12
- * the same concept inside the deduction, where the join is a costed rule.) */
13
- export declare function joinWithBridge(ctx: MindContext, left: Uint8Array, right: Uint8Array): Promise<Uint8Array>;
14
- /** The pivot a produced answer bridges through: the longest UNCONSUMED learnt
15
- * CONTEXT (a node bearing a continuation edge) whose bytes `answer` literally
16
- * contains. Candidates are gathered by resonating the answer's sub-regions
17
- * (breadth-first, leaves skipped, probes capped by branch count), then
18
- * confirmed by exact byte containment — a near-resonance alone never hops. */
19
- export declare function pivotInto(ctx: MindContext, answer: Uint8Array, consumed: ReadonlySet<number>): Promise<number | null>;
20
- export declare function meaningOf(ctx: MindContext, bytes: Uint8Array, anchors: ReadonlyArray<{
21
- name: string;
22
- form: Uint8Array;
23
- }>): Promise<string | null>;
Binary file
@@ -1,15 +0,0 @@
1
- import type { MindContext } from "./types.js";
2
- import type { DerivationStep } from "./graph-search.js";
3
- import type { RationaleItem } from "./rationale.js";
4
- export declare function rItem(bytes: Uint8Array, role?: string, node?: number, span?: [number, number]): RationaleItem;
5
- export declare function rNode(ctx: MindContext, id: number, role?: string, score?: number): RationaleItem;
6
- export declare function rDeriv(ctx: MindContext, it: DerivationStep["conclusion"], role?: string): RationaleItem;
7
- /** The standard FALL-THROUGH closer every self-gating mechanism ends with:
8
- * close the open scope with no outputs and the reason, and return null so
9
- * the caller can `return fail("…")` in one expression. `t` is the scope an
10
- * enclosing `ctx.trace?.enter(...)` returned (undefined when not tracing). */
11
- export declare function traceFail(t: {
12
- done(outputs: RationaleItem[], note?: string): void;
13
- } | undefined): (note: string) => null;
14
- export declare const MOVE_NOTE: Record<string, string>;
15
- export declare function traceDerivation(ctx: MindContext, steps: ReadonlyArray<DerivationStep>): void;
@@ -1,73 +0,0 @@
1
- // trace.ts — trace instrumentation + persistence (Section 9 of the mind).
2
- //
3
- // rItem, rNode, rDeriv — build RationaleItems from bytes/nodes/derivations
4
- // traceDerivation — trace a full derivation proof tree
5
- // MOVE_NOTE — human-readable names for each derivation move
6
- import { read } from "./primitives.js";
7
- import { decodeText } from "./rationale.js";
8
- export function rItem(bytes, role, node, span) {
9
- return {
10
- text: decodeText(bytes),
11
- role,
12
- node: node ?? undefined,
13
- span,
14
- };
15
- }
16
- export function rNode(ctx, id, role, score) {
17
- return {
18
- text: decodeText(read(ctx, id)),
19
- node: id,
20
- role,
21
- score,
22
- };
23
- }
24
- export function rDeriv(ctx, it, role) {
25
- const text = it.bytes
26
- ? decodeText(it.bytes)
27
- : it.node !== undefined
28
- ? decodeText(read(ctx, it.node))
29
- : it.kind === "cover"
30
- ? `cover@${it.span[0]}`
31
- : `[${it.span[0]},${it.span[1]})`;
32
- return { text, role: role ?? it.kind, node: it.node, span: it.span };
33
- }
34
- /** The standard FALL-THROUGH closer every self-gating mechanism ends with:
35
- * close the open scope with no outputs and the reason, and return null so
36
- * the caller can `return fail("…")` in one expression. `t` is the scope an
37
- * enclosing `ctx.trace?.enter(...)` returned (undefined when not tracing). */
38
- export function traceFail(t) {
39
- return (note) => {
40
- t?.done([], note);
41
- return null;
42
- };
43
- }
44
- export const MOVE_NOTE = {
45
- "follow-edge": "follow a learned continuation edge — 'what follows what'",
46
- "concept-hop": "jump a concept (halo) link — a synonym's edge",
47
- "voice": "emit the asker's own wording for this form (articulation)",
48
- "ground": "a chain reached its terminal answer",
49
- "splice-connector": "splice a learnt connector between two rewrites",
50
- "split": "cut a span at a sub-leaf form boundary so a form can be reached",
51
- "fuse": "fuse adjacent fragments toward a deeper learned form",
52
- "recompose": "recompose fused parts into a learned whole that leads on",
53
- "bridge": "advance the cover frontier across this span",
54
- "pool-vote": "pool independent regions' evidence for a shared anchor (sum, not shortest path)",
55
- "axiom": "a seed: a perceived leaf, recognised form, or computed result",
56
- "step": "a derivation step",
57
- };
58
- export function traceDerivation(ctx, steps) {
59
- const t = ctx.trace;
60
- if (!t)
61
- return;
62
- const indexOfOrder = new Map();
63
- for (const s of steps) {
64
- const note = MOVE_NOTE[s.move] ?? s.move;
65
- const deps = s.producers
66
- .map((o) => indexOfOrder.get(o))
67
- .filter((x) => x !== undefined);
68
- const premises = s.premises.map((p) => rDeriv(ctx, p));
69
- const conclusion = [rDeriv(ctx, s.conclusion)];
70
- const index = t.step(s.move, premises, conclusion, s.cost > 0 ? `${note} (cost ${s.cost})` : note, deps.length > 0 ? deps : undefined);
71
- indexOfOrder.set(s.order, index);
72
- }
73
- }