@hviana/sema 0.2.6 → 0.2.8

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 (133) hide show
  1. package/dist/example/demo.d.ts +1 -0
  2. package/dist/example/demo.js +39 -0
  3. package/dist/example/train_base.d.ts +87 -0
  4. package/dist/example/train_base.js +2252 -0
  5. package/dist/src/alphabet.d.ts +7 -0
  6. package/dist/src/alphabet.js +33 -0
  7. package/dist/src/alu/src/alu.d.ts +185 -0
  8. package/dist/src/alu/src/alu.js +440 -0
  9. package/dist/src/alu/src/expr.d.ts +61 -0
  10. package/dist/src/alu/src/expr.js +318 -0
  11. package/dist/src/alu/src/index.d.ts +11 -0
  12. package/dist/src/alu/src/index.js +19 -0
  13. package/dist/src/alu/src/kernel-arith.d.ts +16 -0
  14. package/dist/src/alu/src/kernel-arith.js +264 -0
  15. package/dist/src/alu/src/kernel-bits.d.ts +19 -0
  16. package/dist/src/alu/src/kernel-bits.js +152 -0
  17. package/dist/src/alu/src/kernel-logic.d.ts +4 -0
  18. package/dist/src/alu/src/kernel-logic.js +60 -0
  19. package/dist/src/alu/src/kernel-nd.d.ts +3 -0
  20. package/dist/src/alu/src/kernel-nd.js +208 -0
  21. package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
  22. package/dist/src/alu/src/kernel-numeric.js +366 -0
  23. package/dist/src/alu/src/operation.d.ts +168 -0
  24. package/dist/src/alu/src/operation.js +189 -0
  25. package/dist/src/alu/src/parser.d.ts +221 -0
  26. package/dist/src/alu/src/parser.js +577 -0
  27. package/dist/src/alu/src/resonance.d.ts +55 -0
  28. package/dist/src/alu/src/resonance.js +126 -0
  29. package/dist/src/alu/src/text.d.ts +31 -0
  30. package/dist/src/alu/src/text.js +73 -0
  31. package/dist/src/alu/src/value.d.ts +109 -0
  32. package/dist/src/alu/src/value.js +300 -0
  33. package/dist/src/alu/test/alu.test.d.ts +1 -0
  34. package/dist/src/alu/test/alu.test.js +764 -0
  35. package/dist/src/bytes.d.ts +14 -0
  36. package/dist/src/bytes.js +59 -0
  37. package/dist/src/canon.d.ts +26 -0
  38. package/dist/src/canon.js +57 -0
  39. package/dist/src/config.d.ts +111 -0
  40. package/dist/src/config.js +91 -0
  41. package/dist/src/derive/src/deduction.d.ts +125 -0
  42. package/dist/src/derive/src/deduction.js +155 -0
  43. package/dist/src/derive/src/index.d.ts +7 -0
  44. package/dist/src/derive/src/index.js +11 -0
  45. package/dist/src/derive/src/priority-queue.d.ts +20 -0
  46. package/dist/src/derive/src/priority-queue.js +73 -0
  47. package/dist/src/derive/src/rewrite.d.ts +56 -0
  48. package/dist/src/derive/src/rewrite.js +100 -0
  49. package/dist/src/derive/src/trie.d.ts +90 -0
  50. package/dist/src/derive/src/trie.js +217 -0
  51. package/dist/src/derive/test/derive.test.d.ts +1 -0
  52. package/dist/src/derive/test/derive.test.js +122 -0
  53. package/dist/src/extension.d.ts +37 -0
  54. package/dist/src/extension.js +7 -0
  55. package/dist/src/geometry.d.ts +202 -0
  56. package/dist/src/geometry.js +575 -0
  57. package/dist/src/index.d.ts +16 -0
  58. package/dist/src/index.js +18 -0
  59. package/dist/src/ingest-cache.d.ts +41 -0
  60. package/dist/src/ingest-cache.js +161 -0
  61. package/dist/src/mind/articulation.d.ts +6 -0
  62. package/dist/src/mind/articulation.js +99 -0
  63. package/dist/src/mind/attention.d.ts +405 -0
  64. package/dist/src/mind/attention.js +1829 -0
  65. package/dist/src/mind/bridge.d.ts +30 -0
  66. package/dist/src/mind/bridge.js +569 -0
  67. package/dist/src/mind/canonical.d.ts +29 -0
  68. package/dist/src/mind/canonical.js +88 -0
  69. package/dist/src/mind/graph-search.d.ts +285 -0
  70. package/dist/src/mind/graph-search.js +956 -0
  71. package/dist/src/mind/index.d.ts +7 -0
  72. package/dist/src/mind/index.js +5 -0
  73. package/dist/src/mind/junction.d.ts +124 -0
  74. package/dist/src/mind/junction.js +317 -0
  75. package/dist/src/mind/learning.d.ts +47 -0
  76. package/dist/src/mind/learning.js +248 -0
  77. package/dist/src/mind/match.d.ts +126 -0
  78. package/dist/src/mind/match.js +441 -0
  79. package/dist/src/mind/mechanisms/alu.d.ts +4 -0
  80. package/dist/src/mind/mechanisms/alu.js +36 -0
  81. package/dist/src/mind/mechanisms/cast.d.ts +89 -0
  82. package/dist/src/mind/mechanisms/cast.js +716 -0
  83. package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
  84. package/dist/src/mind/mechanisms/confluence.js +225 -0
  85. package/dist/src/mind/mechanisms/cover.d.ts +6 -0
  86. package/dist/src/mind/mechanisms/cover.js +203 -0
  87. package/dist/src/mind/mechanisms/extraction.d.ts +67 -0
  88. package/dist/src/mind/mechanisms/extraction.js +387 -0
  89. package/dist/src/mind/mechanisms/recall.d.ts +13 -0
  90. package/dist/src/mind/mechanisms/recall.js +325 -0
  91. package/dist/src/mind/mind.d.ts +276 -0
  92. package/dist/src/mind/mind.js +572 -0
  93. package/dist/src/mind/pipeline-mechanism.d.ts +142 -0
  94. package/dist/src/mind/pipeline-mechanism.js +213 -0
  95. package/dist/src/mind/pipeline.d.ts +20 -0
  96. package/dist/src/mind/pipeline.js +217 -0
  97. package/dist/src/mind/primitives.d.ts +70 -0
  98. package/dist/src/mind/primitives.js +287 -0
  99. package/dist/src/mind/rationale.d.ts +139 -0
  100. package/dist/src/mind/rationale.js +163 -0
  101. package/dist/src/mind/reasoning.d.ts +34 -0
  102. package/dist/src/mind/reasoning.js +240 -0
  103. package/dist/src/mind/recognition.d.ts +20 -0
  104. package/dist/src/mind/recognition.js +390 -0
  105. package/dist/src/mind/resonance.d.ts +23 -0
  106. package/dist/src/mind/resonance.js +0 -0
  107. package/dist/src/mind/trace.d.ts +15 -0
  108. package/dist/src/mind/trace.js +73 -0
  109. package/dist/src/mind/traverse.d.ts +113 -0
  110. package/dist/src/mind/traverse.js +568 -0
  111. package/dist/src/mind/types.d.ts +289 -0
  112. package/dist/src/mind/types.js +130 -0
  113. package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
  114. package/dist/src/rabitq-ivf/src/database.js +201 -0
  115. package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
  116. package/dist/src/rabitq-ivf/src/index.js +4 -0
  117. package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
  118. package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
  119. package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
  120. package/dist/src/rabitq-ivf/src/prng.js +36 -0
  121. package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
  122. package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
  123. package/dist/src/sema.d.ts +31 -0
  124. package/dist/src/sema.js +63 -0
  125. package/dist/src/store-sqlite.d.ts +184 -0
  126. package/dist/src/store-sqlite.js +942 -0
  127. package/dist/src/store.d.ts +663 -0
  128. package/dist/src/store.js +1618 -0
  129. package/dist/src/vec.d.ts +31 -0
  130. package/dist/src/vec.js +109 -0
  131. package/package.json +1 -1
  132. package/src/mind/attention.ts +178 -63
  133. package/test/51-structural-resonance-ladder.test.mjs +6 -3
@@ -0,0 +1,30 @@
1
+ import type { MindContext } from "./types.js";
2
+ /** One accepted substitution: query span [qs,qe) stands in for the
3
+ * candidate context's span — recorded for the rationale trace. */
4
+ interface Substitution {
5
+ qs: number;
6
+ qe: number;
7
+ cs: number;
8
+ ce: number;
9
+ }
10
+ /** A bridged grounding proposal: the trained context to ground, the query
11
+ * spans its alignment accounts for, and the substitutions that closed it. */
12
+ export interface BridgeHit {
13
+ id: number;
14
+ accounted: Array<[number, number]>;
15
+ subs: Substitution[];
16
+ }
17
+ /** True when some query byte-range left UNACCOUNTED by `spans` contains a
18
+ * STORED window — content the store has seen that the proposed reading
19
+ * simply ignores. The IGNORED-KNOWN principle: a span may be dismissed
20
+ * only when the store itself has never seen it; known content the
21
+ * alignment failed to account for is grounds for refusal, while genuinely
22
+ * novel spans (an untrained word, stray punctuation) remain tolerable.
23
+ * Shared by the substitution bridge's own acceptance and CAST's
24
+ * frame-tier comparison gate (cast.ts). Pure attestation — no
25
+ * similarity, no constants. */
26
+ export declare function dismissedKnownContent(ctx: MindContext, query: Uint8Array, spans: ReadonlyArray<readonly [number, number]>): boolean;
27
+ /** Recall's corroborated-substitution bridge — see the module comment.
28
+ * Returns the best bridged grounding proposal, or null. */
29
+ export declare function substitutionBridge(ctx: MindContext, query: Uint8Array, proposed?: ReadonlyArray<number>): Promise<BridgeHit | null>;
30
+ export {};
@@ -0,0 +1,569 @@
1
+ // bridge.ts — corroborated-substitution grounding (recall's last tier before
2
+ // refusal).
3
+ //
4
+ // THE GAP (test/49): a query phrased through a near-synonym of a trained
5
+ // word ("Name the biggest planet." against a corpus that only ever says
6
+ // "largest planet") reaches nothing — recognition finds no form, whole-query
7
+ // resonance stays below the reach bar — even though the fact is trained and
8
+ // the synonym pairing is corroborated across the corpus. Words are never
9
+ // independently addressable nodes (deposit interns whole streams plus W-1/W
10
+ // leaf windows; a word mid-sentence falls between those scales), so no halo
11
+ // ever links "biggest" to "largest" — and the write side cannot cheaply
12
+ // mint such nodes without polluting the shared indexes (measured: an
13
+ // earlier write-side attempt regressed 24 unrelated tests).
14
+ //
15
+ // THE MECHANISM — read-only, refusal-path-only. When recall is about to
16
+ // refuse, the query's own content-addressed windows (the W-byte leaf-id
17
+ // flat branches indexSubSpans already interns at every byte offset) are
18
+ // probed against the store; the rarest ones anchor a climb (edgeAncestors —
19
+ // the same climb consensus voting uses) to the trained contexts that
20
+ // contain them. Each candidate context is ALIGNED to the query byte-for-
21
+ // byte around the anchor, leaving mismatched spans; a mismatch grounds as a
22
+ // SUBSTITUTION only under two derived gates:
23
+ //
24
+ // • CORROBORATION — the query-side span is itself corpus-attested: every
25
+ // W-window inside it resolves as a stored flat form, at least one of
26
+ // them reused across ≥ 2 containers (the same "≥ 2 structural parents"
27
+ // bar propagateSuffixes gates suffix inheritance with). An untrained
28
+ // word ("deadliest") has no stored windows and can never substitute.
29
+ // • GEOMETRIC IDENTITY — the two spans' own perceived gists must clear
30
+ // conceptThreshold(D), the same "same concept" bar haloSiblings and
31
+ // articulation already gate on. This is what separates a synonym pair
32
+ // the fold geometry genuinely identifies ("biggest"~"largest", sharing
33
+ // most of their bytes and their role) from an arbitrary co-frame word.
34
+ //
35
+ // A candidate context is accepted when its aligned-plus-substituted spans
36
+ // DOMINATE the query (the same half-dominance predicate used throughout)
37
+ // and every unexplained gap stays within one perception window W (the same
38
+ // single-window tolerance identityBar prices). The accepted context is
39
+ // then grounded exactly like any recall hit — project() through its learnt
40
+ // edges — so the answer is a trained continuation, never synthesized bytes.
41
+ //
42
+ // COST: nothing on any answering path — the bridge runs only where the
43
+ // alternative was silence. There it pays O(|query|) content-hash probes
44
+ // (the propagateSuffixes trick), at most W anchor climbs and hubBound
45
+ // candidate reads, and one O(|query|·|candidate|)-bounded alignment each —
46
+ // all capped by existing derived bounds (W, chainReach, hubBound).
47
+ //
48
+ // FIXED WRONG-ANSWER GAP (found and closed 2026-07-20): a proper-noun swap
49
+ // could pass both derived gates above and voice a WRONG fact. Live case:
50
+ // "The capital of France is" (a prefix-completion probe) bridged through a
51
+ // substitution reading "of Fra[nce]" as "of Spain si[nce]" into "Madrid has
52
+ // been the capital of Spain since 1561...", because the TRUE France fact
53
+ // ("The capital of France is Paris.") is a terminal statement with no
54
+ // outgoing edge and is therefore never admitted as a bridge candidate (§
55
+ // candidate admission above) — so no competing evidence for "France" was
56
+ // ever collected, and the Spain candidate's own text satisfied frame
57
+ // unanimity vacuously (nothing to disagree with).
58
+ // REFUTED FIX 1 — require unanimous()'s frame-consensus scan to find a
59
+ // genuine corroborating occurrence (not vacuous-true on zero evidence):
60
+ // breaks test/49 — "biggest"~"largest" is corroborated ONLY by the very
61
+ // candidate proposing the substitution in that miniature corpus (no
62
+ // OTHER trained pair pairs either word with "planet"); requiring
63
+ // external evidence makes that legitimate case fail too.
64
+ // REFUTED FIX 2 — exclude the candidate's own bytes from being its own
65
+ // corroborating witness (same idea, scoped to self-reference): same
66
+ // failure, same reason — self-witness is ALL the evidence test/49 has.
67
+ // REFUTED FIX 3 — require the CANDIDATE-side substituted span to also
68
+ // clear the ≥2-container reuse bar (attestedSpan, symmetric with the
69
+ // query-side attestedQ): does not discriminate — "Spain" is reused
70
+ // across at least as many trained contexts as "France" is, so it
71
+ // passes trivially.
72
+ // THE ACTUAL FIX — RAW BALANCE (see the substitution loop below): the raw
73
+ // mismatch (BEFORE expansion absorbs any matched flanking bytes) must
74
+ // be roughly length-balanced on both sides — dominates(min(uLen,cLen),
75
+ // max(uLen,cLen)), the SAME "part*2 > whole" bar used throughout the
76
+ // codebase, no new constant. Measured on both cases: the legitimate
77
+ // "biggest"~"largest" substitution's raw diff is "big"/"lar" (3/3
78
+ // bytes, perfectly balanced — expansion then absorbs the shared "gest"
79
+ // suffix, identical on both sides, to reach an attestable span). The
80
+ // wrong "France"~"Spain" substitution's raw diff was "Fra"/"Spai" (3/8
81
+ // bytes) — the align sweep's greedy search had found a coincidental
82
+ // "nce " match years later inside "since", so 3 bytes of query content
83
+ // were standing in for 8 bytes of candidate content. That asymmetry is
84
+ // exactly what a real lexical/morphological synonym never has and an
85
+ // arbitrary sentence divergence always does; expansion (which only
86
+ // grows both sides by IDENTICAL absorbed bytes) can never repair a raw
87
+ // imbalance, so gating on the RAW gap is the correct point of attack.
88
+ // Verified: real-store repro now falls through to an honest echo of
89
+ // the true trained fact instead of the wrong Spain continuation; the
90
+ // boiling-point and lowercase-France bridge wins are unaffected; full
91
+ // suite green (358/358).
92
+ import { cosine } from "../vec.js";
93
+ import { conceptThreshold, dominates } from "../geometry.js";
94
+ import { bytesEqual, indexOf } from "../bytes.js";
95
+ import { foldTree, perceive } from "./primitives.js";
96
+ import { chainReach, leafIdRun } from "./canonical.js";
97
+ import { corpusN, edgeAncestors, hubBound } from "./traverse.js";
98
+ import { rItem, rNode } from "./trace.js";
99
+ /** True when some query byte-range left UNACCOUNTED by `spans` contains a
100
+ * STORED window — content the store has seen that the proposed reading
101
+ * simply ignores. The IGNORED-KNOWN principle: a span may be dismissed
102
+ * only when the store itself has never seen it; known content the
103
+ * alignment failed to account for is grounds for refusal, while genuinely
104
+ * novel spans (an untrained word, stray punctuation) remain tolerable.
105
+ * Shared by the substitution bridge's own acceptance and CAST's
106
+ * frame-tier comparison gate (cast.ts). Pure attestation — no
107
+ * similarity, no constants. */
108
+ export function dismissedKnownContent(ctx, query, spans) {
109
+ const W = ctx.space.maxGroup;
110
+ const sorted = [...spans].sort((a, b) => a[0] - b[0]);
111
+ let cursor = 0;
112
+ for (const [s, e] of [...sorted, [query.length, query.length]]) {
113
+ for (let o = cursor; o + W <= s; o++) {
114
+ const ids = leafIdRun(ctx, query, o, o + W);
115
+ if (ids !== null && ctx.store.findBranch(ids) !== null)
116
+ return true;
117
+ }
118
+ cursor = Math.max(cursor, e);
119
+ }
120
+ return false;
121
+ }
122
+ /** Extend a seed match (query offset qo ↔ candidate offset co) to its
123
+ * maximal common run, then walk outward in both directions collecting
124
+ * further common runs of at least W bytes across bounded mismatch gaps
125
+ * (each side ≤ chainReach). Returns the matched query spans and the
126
+ * mismatch pairs between consecutive runs. */
127
+ function align(ctx, q, c, qo, co) {
128
+ const W = ctx.space.maxGroup;
129
+ const reachCap = chainReach(W);
130
+ // Maximal run around the seed.
131
+ let qs = qo, ss = co;
132
+ while (qs > 0 && ss > 0 && q[qs - 1] === c[ss - 1]) {
133
+ qs--;
134
+ ss--;
135
+ }
136
+ let qe = qo, se = co;
137
+ while (qe < q.length && se < c.length && q[qe] === c[se]) {
138
+ qe++;
139
+ se++;
140
+ }
141
+ const matched = [[qs, qe]];
142
+ const gaps = [];
143
+ // The next common run of ≥ W bytes past (qi, si), with each side's gap
144
+ // bounded by chainReach; smallest total gap wins (nearest continuation).
145
+ const runLenAt = (qi, si) => {
146
+ let n = 0;
147
+ while (qi + n < q.length && si + n < c.length && q[qi + n] === c[si + n]) {
148
+ n++;
149
+ }
150
+ return n;
151
+ };
152
+ // RIGHT sweep.
153
+ let qi = qe, si = se;
154
+ for (;;) {
155
+ let found = false;
156
+ for (let total = 1; total <= 2 * reachCap && !found; total++) {
157
+ for (let gq = 0; gq <= Math.min(total, reachCap); gq++) {
158
+ const gs = total - gq;
159
+ if (gs > reachCap)
160
+ continue;
161
+ if (qi + gq >= q.length || si + gs >= c.length)
162
+ continue;
163
+ const n = runLenAt(qi + gq, si + gs);
164
+ if (n >= W || qi + gq + n === q.length) {
165
+ if (n === 0)
166
+ continue;
167
+ if (gq > 0 || gs > 0) {
168
+ gaps.push({ qs: qi, qe: qi + gq, cs: si, ce: si + gs });
169
+ }
170
+ matched.push([qi + gq, qi + gq + n]);
171
+ qi = qi + gq + n;
172
+ si = si + gs + n;
173
+ found = true;
174
+ break;
175
+ }
176
+ }
177
+ }
178
+ if (!found)
179
+ break;
180
+ }
181
+ // LEFT sweep (mirror).
182
+ qi = qs;
183
+ si = ss;
184
+ for (;;) {
185
+ let found = false;
186
+ for (let total = 1; total <= 2 * reachCap && !found; total++) {
187
+ for (let gq = 0; gq <= Math.min(total, reachCap); gq++) {
188
+ const gs = total - gq;
189
+ if (gs > reachCap)
190
+ continue;
191
+ if (qi - gq <= 0 || si - gs <= 0)
192
+ continue;
193
+ // Run ENDING at (qi - gq, si - gs).
194
+ let n = 0;
195
+ while (n < qi - gq && n < si - gs &&
196
+ q[qi - gq - 1 - n] === c[si - gs - 1 - n]) {
197
+ n++;
198
+ }
199
+ if (n >= W || n === qi - gq) {
200
+ if (n === 0)
201
+ continue;
202
+ if (gq > 0 || gs > 0) {
203
+ gaps.push({ qs: qi - gq, qe: qi, cs: si - gs, ce: si });
204
+ }
205
+ matched.push([qi - gq - n, qi - gq]);
206
+ qi = qi - gq - n;
207
+ si = si - gs - n;
208
+ found = true;
209
+ break;
210
+ }
211
+ }
212
+ }
213
+ if (!found)
214
+ break;
215
+ }
216
+ return { matched, gaps };
217
+ }
218
+ /** Recall's corroborated-substitution bridge — see the module comment.
219
+ * Returns the best bridged grounding proposal, or null. */
220
+ export async function substitutionBridge(ctx, query, proposed = []) {
221
+ const W = ctx.space.maxGroup;
222
+ if (query.length < 2 * W)
223
+ return null;
224
+ const bound = hubBound(ctx);
225
+ const N = corpusN(ctx);
226
+ const bar = conceptThreshold(ctx.store.D);
227
+ const reachCap = chainReach(W);
228
+ // 1. The query's stored windows, rarest first (fewest containers — the
229
+ // most discriminative anchors; hub-clamped like every fan-out read).
230
+ // The scan doubles as the ONE store probe of every query window: the
231
+ // per-offset stored/reused facts it establishes serve every later
232
+ // attestation and ignored-known check as plain array reads (the same
233
+ // probes repeated per candidate dominated the refusal-path cost).
234
+ const nWin = Math.max(0, query.length - W + 1);
235
+ const winStored = new Uint8Array(nWin);
236
+ const winReused = new Uint8Array(nWin);
237
+ const anchors = [];
238
+ for (let o = 0; o + W <= query.length; o++) {
239
+ const ids = leafIdRun(ctx, query, o, o + W);
240
+ if (ids === null)
241
+ continue;
242
+ const id = ctx.store.findBranch(ids);
243
+ if (id === null)
244
+ continue;
245
+ winStored[o] = 1;
246
+ const rarity = ctx.store.containersSlice(id, 0, bound + 1).length;
247
+ if (rarity >= 2)
248
+ winReused[o] = 1;
249
+ if (rarity === 0)
250
+ continue;
251
+ anchors.push({ off: o, id, rarity });
252
+ }
253
+ if (anchors.length === 0)
254
+ return null;
255
+ // CORROBORATION (see the module-level doc) over the precomputed window
256
+ // facts: the query span [qs,qe) attests when every full W-window inside
257
+ // it is a stored flat form and at least one is reused across ≥ 2
258
+ // containers. Spans shorter than W carry no window of their own and can
259
+ // never substitute.
260
+ const attestedQ = (qs, qe) => {
261
+ if (qe - qs < W)
262
+ return false;
263
+ let reused = false;
264
+ for (let o = qs; o + W <= qe; o++) {
265
+ if (!winStored[o])
266
+ return false;
267
+ if (winReused[o])
268
+ reused = true;
269
+ }
270
+ return reused;
271
+ };
272
+ // dismissedKnownContent (see above) over the same precomputed facts.
273
+ const dismissedKnownQ = (spans) => {
274
+ const sorted = [...spans].sort((a, b) => a[0] - b[0]);
275
+ let cursor = 0;
276
+ for (const [s, e] of [...sorted, [query.length, query.length]]) {
277
+ for (let o = cursor; o + W <= s; o++)
278
+ if (winStored[o])
279
+ return true;
280
+ cursor = Math.max(cursor, e);
281
+ }
282
+ return false;
283
+ };
284
+ anchors.sort((a, b) => a.rarity - b.rarity);
285
+ // Up to W anchors, at least one window apart — the quantum's own count.
286
+ const picked = [];
287
+ for (const a of anchors) {
288
+ if (picked.length >= W)
289
+ break;
290
+ if (picked.some((p) => Math.abs(p.off - a.off) < W))
291
+ continue;
292
+ picked.push(a);
293
+ }
294
+ // 2. Candidate trained contexts. Two proposal channels, one verifier:
295
+ // (a) the caller's PROPOSED hits — recall's whole-query resonance
296
+ // ranking, the retrieval structure built to surface near-paraphrase
297
+ // forms the window climb cannot single out at corpus scale; (b) each
298
+ // picked anchor climbed to its edge-bearing ancestors (the same climb
299
+ // consensus voting uses). Both are only ever PROPOSALS — every
300
+ // candidate passes the same byte-exact alignment and gates below.
301
+ const seen = new Set();
302
+ const candidates = [];
303
+ // Proposal channel — carries its caller's own bound (recall's resonance
304
+ // k), so it neither consumes the climb's hub budget (on a small corpus
305
+ // √N is a handful and the proposals would crowd the climb out entirely)
306
+ // nor lets per-candidate byte work grow past that bound. A proposal may
307
+ // be a FLAT content twin whose continuation edge lives on the
308
+ // fold-shaped deposit node with the same bytes — the same twin split
309
+ // canonResolve bridges by re-folding (primitives.ts) — but the re-fold
310
+ // (a full perceive of the candidate's bytes) is paid only for proposals
311
+ // that could align at all: alignment can only seed at a picked anchor
312
+ // window occurring literally in the candidate (measured: unconditional
313
+ // re-folds multiplied the refusal-path latency several-fold).
314
+ for (const sid of proposed) {
315
+ if (seen.has(sid))
316
+ continue;
317
+ seen.add(sid);
318
+ const tb = ctx.store.bytes(sid);
319
+ if (tb.length === 0)
320
+ continue;
321
+ if (!picked.some((a) => indexOf(tb, query.subarray(a.off, a.off + W), 0) >= 0))
322
+ continue;
323
+ let use = sid;
324
+ if (!ctx.store.hasNext(use)) {
325
+ const folded = foldTree(ctx, perceive(ctx, tb), 0).node;
326
+ if (folded === null || folded === sid || !ctx.store.hasNext(folded)) {
327
+ continue;
328
+ }
329
+ use = folded;
330
+ if (seen.has(use))
331
+ continue;
332
+ seen.add(use);
333
+ }
334
+ candidates.push(use);
335
+ }
336
+ // Climb channel — edge-bearing ancestors only, decided by the indexed
337
+ // O(1) hasNext; no byte is read here (the climb visits hundreds of
338
+ // roots, and reading each was measured to dominate the refusal path).
339
+ const proposedCount = candidates.length;
340
+ for (const a of picked) {
341
+ const reach = edgeAncestors(ctx, a.id, N);
342
+ for (const sid of reach.roots) {
343
+ if (candidates.length - proposedCount >= bound)
344
+ break;
345
+ if (seen.has(sid))
346
+ continue;
347
+ seen.add(sid);
348
+ if (!ctx.store.hasNext(sid))
349
+ continue;
350
+ candidates.push(sid);
351
+ }
352
+ if (candidates.length - proposedCount >= bound)
353
+ break;
354
+ }
355
+ // 3. Align each candidate; gate its mismatches; keep the best.
356
+ const allBytes = new Map();
357
+ for (const sid of candidates)
358
+ allBytes.set(sid, ctx.store.bytes(sid));
359
+ // FRAME UNANIMITY: a substitution U → C inside the frame (Lf, Rf) is
360
+ // groundable only when the collected candidates — the store's own sample
361
+ // of contexts sharing the query's content — are unanimous about the
362
+ // filler: every occurrence of Lf…Rf across them holds either U (the
363
+ // query's own word, corroboration) or C. A THIRD distinct filler means
364
+ // the frame is a VALUE SLOT ("was born in _" held Germany, Poland,
365
+ // England, Serbia — observed live), and picking one value would assert
366
+ // knowledge the store does not have. Consensus of the store's own
367
+ // instances, no similarity judgement, no tuned constant.
368
+ // Requires a genuine CORROBORATING sighting, not merely the absence of a
369
+ // conflicting one: scanning only the handful of resonance/climb-proposed
370
+ // candidates means the frame can easily occur NOWHERE else among them
371
+ // (observed live: "of Fra[nce]" -> "of Spain si[nce]" passed vacuously —
372
+ // the frame "tal …nce " never recurred among the collected candidates at
373
+ // all, so there was no consensus, only an absence of disagreement, yet
374
+ // the substitution was accepted). "Unanimous" must mean the store's own
375
+ // instances agree, which requires at least one instance to consult.
376
+ const unanimous = (u, c, lf, rf) => {
377
+ for (const bytes of allBytes.values()) {
378
+ let from = 0;
379
+ for (;;) {
380
+ const i = indexOf(bytes, lf, from);
381
+ if (i < 0)
382
+ break;
383
+ from = i + 1;
384
+ const start = i + lf.length;
385
+ const j = indexOf(bytes.subarray(start, start + reachCap + rf.length), rf, 0);
386
+ if (j < 0)
387
+ continue;
388
+ const filler = bytes.subarray(start, start + j);
389
+ if (filler.length === 0)
390
+ continue;
391
+ if (!bytesEqual(filler, u) && !bytesEqual(filler, c))
392
+ return false;
393
+ }
394
+ }
395
+ return true;
396
+ };
397
+ // (A candidate need NOT contain the query's rarest window literally: the
398
+ // rarest window may sit INSIDE the very word being substituted (observed
399
+ // live: "chemical symbol for water" whose rarest window "l sy" spans
400
+ // "symbol" — the trained formula-question can never contain it). A
401
+ // candidate that instead dodges the query's known content by writing it
402
+ // off as gaps is refused by dismissedKnownContent below, which subsumes
403
+ // the old rarest-window containment gate: rare windows inside an accepted
404
+ // substitution are accounted for; rare windows outside the accepted spans
405
+ // force refusal (the Matrix-synopsis junk stays dead by exactly that
406
+ // check — verified live).
407
+ let best = null;
408
+ let bestAccounted = 0;
409
+ for (const sid of candidates) {
410
+ const cBytes = allBytes.get(sid);
411
+ if (cBytes.length === 0)
412
+ continue;
413
+ // Seed at the rarest picked anchor that literally occurs in this
414
+ // candidate.
415
+ let seed = null;
416
+ for (const a of picked) {
417
+ const co = indexOf(cBytes, query.subarray(a.off, a.off + W), 0);
418
+ if (co >= 0) {
419
+ seed = { qo: a.off, co };
420
+ break;
421
+ }
422
+ }
423
+ if (seed === null)
424
+ continue;
425
+ const { matched, gaps } = align(ctx, query, cBytes, seed.qo, seed.co);
426
+ // Gate each mismatch: a corroborated, geometrically-identified
427
+ // substitution counts as accounted; anything else stays a gap.
428
+ //
429
+ // A raw mismatch is the MINIMAL byte diff ("big" ↔ "lar" inside
430
+ // biggest/largest), usually below the scale at which either side is a
431
+ // corpus unit. The true unit is found by EXPANSION: absorb flanking
432
+ // bytes from the adjacent matched runs (equal on both sides by
433
+ // construction, so both spans grow identically) until the query side
434
+ // attests and the pair clears the concept bar — smallest expansion
435
+ // first, capped at chainReach like the mismatch itself. Absorbed
436
+ // bytes were already matched, so coverage is unchanged.
437
+ const subs = [];
438
+ let ok = true;
439
+ for (const g of gaps) {
440
+ const uLen = g.qe - g.qs, cLen = g.ce - g.cs;
441
+ if (uLen === 0 || cLen === 0 || uLen > reachCap || cLen > reachCap) {
442
+ // Pure insertion/deletion or over-long mismatch. Query-side: one
443
+ // perception window (the identityBar tolerance). Candidate-side:
444
+ // one chain reach (W², the two-level composite bound) — a genuine
445
+ // paraphrase inserts inflection-scale material ("does water boil"
446
+ // ↔ "should water be boiled"), while a divergent candidate jumps
447
+ // hundreds of bytes between the query's frames.
448
+ if (uLen > W || cLen > reachCap)
449
+ ok = false;
450
+ continue;
451
+ }
452
+ // RAW BALANCE gate (closes the OPEN GAP above): the two sides of a
453
+ // genuine lexical substitution swap comparable amounts of content —
454
+ // "big"/"lar" (3/3 bytes, before expansion absorbs the shared "gest"
455
+ // suffix to reach an attestable "biggest"/"largest"). A candidate
456
+ // whose two sentences simply diverge into unrelated continuations
457
+ // produces a LOPSIDED raw mismatch instead — the live wrong answer's
458
+ // raw gap was "Fra"/"Spai" widened to (3,8) by the align sweep
459
+ // finding a coincidental "nce " match years later in "since" — 3
460
+ // bytes of query content standing in for 8 bytes of candidate
461
+ // content is not a word swap, it is two different sentences that
462
+ // happen to share a few letters. Uses the SAME dominates() bar
463
+ // (part*2 > whole) applied throughout the codebase, symmetrically:
464
+ // the smaller raw side must be more than half the larger. Applies
465
+ // to the RAW gap, before expansion — expansion only ever grows both
466
+ // sides by IDENTICAL absorbed bytes, so it cannot fix an imbalance
467
+ // that was already there.
468
+ let accepted = false;
469
+ const balanced = dominates(Math.min(uLen, cLen), Math.max(uLen, cLen));
470
+ const maxExtra = balanced ? reachCap - Math.max(uLen, cLen) : -1;
471
+ outer: for (let extra = 0; extra <= maxExtra; extra++) {
472
+ for (let a = 0; a <= extra; a++) {
473
+ const b = extra - a;
474
+ const qs2 = g.qs - a, qe2 = g.qe + b;
475
+ const cs2 = g.cs - a, ce2 = g.ce + b;
476
+ if (qs2 < 0 || qe2 > query.length)
477
+ continue;
478
+ if (cs2 < 0 || ce2 > cBytes.length)
479
+ continue;
480
+ // INTERIOR gate: a substitution must sit INSIDE matched
481
+ // structure — at least one full window of matched bytes must
482
+ // remain adjacent on BOTH sides after absorption. Every junk
483
+ // substitution observed live sat at the query's edge, with only
484
+ // terminal punctuation beyond it ("…born in [England].",
485
+ // "…capital of [Zamunda]?"): an edge mismatch is the query
486
+ // trailing off into different content, not a word standing in a
487
+ // shared frame.
488
+ const leftOk = matched.some(([s, e]) => e >= qs2 && qs2 - s >= W);
489
+ const rightOk = matched.some(([s, e]) => s <= qe2 && e - qe2 >= W);
490
+ if (!leftOk || !rightOk)
491
+ continue;
492
+ if (!attestedQ(qs2, qe2))
493
+ continue;
494
+ const u = query.subarray(qs2, qe2);
495
+ const cSpan = cBytes.subarray(cs2, ce2);
496
+ if (cosine(perceive(ctx, u).v, perceive(ctx, cSpan).v) < bar) {
497
+ continue;
498
+ }
499
+ if (!unanimous(u, cSpan, query.subarray(qs2 - W, qs2), query.subarray(qe2, qe2 + W)))
500
+ continue;
501
+ subs.push({ qs: qs2, qe: qe2, cs: cs2, ce: ce2 });
502
+ accepted = true;
503
+ break outer;
504
+ }
505
+ }
506
+ if (!accepted && (uLen > W || cLen > reachCap))
507
+ ok = false;
508
+ }
509
+ // A candidate with ZERO gaps needs no substitution and might seem like
510
+ // the strongest possible bridge, but accepting it here is a trap: this
511
+ // mechanism runs only where recall's own resonance/echo tiers already
512
+ // declined to ground a same-shape, zero-substitution match — usually
513
+ // because the query is a strict byte-PREFIX of several candidates
514
+ // (many trained "The capital of X is Y." facts share the query "The
515
+ // capital of France is" as a substring once the true France fact is
516
+ // filtered out for lacking a continuation edge) and nothing here
517
+ // corroborates picking one candidate's completion over another's
518
+ // (observed live: prefix-completion bridged to an unrelated "London"
519
+ // trivia distractor over the true France fact, which precedes it in
520
+ // resonance rank but has no outgoing edge to bridge through). This
521
+ // mechanism exists to explain SUBSTITUTIONS; a query needing none is
522
+ // recall's job, not the bridge's.
523
+ if (!ok || subs.length === 0)
524
+ continue;
525
+ // Coverage: matched runs plus accepted substitutions must dominate the
526
+ // query, every interior gap already proved ≤ W above, and the EDGES
527
+ // must be explained to the same one-window tolerance — the same "at
528
+ // most one river window of foreign content" identityBar prices. The
529
+ // live junk this closes: alignments that matched a query's scaffolding
530
+ // and one substitution but left the query's whole trailing content
531
+ // ("…planet orbiting our sun.", 24 bytes) unexplained, yet still
532
+ // half-dominated the byte count.
533
+ const spans = [
534
+ ...matched,
535
+ ...subs.map((s) => [s.qs, s.qe]),
536
+ ].sort((x, y) => x[0] - y[0]);
537
+ let covered = 0;
538
+ let reachEnd = 0;
539
+ for (const [s, e] of spans) {
540
+ if (e <= reachEnd)
541
+ continue;
542
+ covered += e - Math.max(s, reachEnd);
543
+ reachEnd = Math.max(reachEnd, e);
544
+ }
545
+ if (spans[0][0] > W || query.length - reachEnd > W)
546
+ continue;
547
+ if (!dominates(covered, query.length))
548
+ continue;
549
+ // KNOWN content may never be dismissed — see dismissedKnownContent
550
+ // (the live case: "what is the capital of france" aligning into a
551
+ // Matrix synopsis by writing off "ance" — a stored window of the
552
+ // trained "France" — as a gap, while genuinely novel spans like
553
+ // test/49's untrained "Name" remain tolerable).
554
+ if (dismissedKnownQ(spans))
555
+ continue;
556
+ if (covered > bestAccounted) {
557
+ bestAccounted = covered;
558
+ best = { id: sid, accounted: spans, subs };
559
+ }
560
+ }
561
+ if (best !== null) {
562
+ ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [
563
+ rNode(ctx, best.id, "bridged-context"),
564
+ ...best.subs.map((s) => rItem(query.subarray(s.qs, s.qe), "substituted")),
565
+ ], `a trained context accounts for the query up to ${best.subs.length} ` +
566
+ `corroborated substitution(s) — grounding through its learnt edges`);
567
+ }
568
+ return best;
569
+ }
@@ -0,0 +1,29 @@
1
+ import type { MindContext } from "./types.js";
2
+ /** The two sliding-window lengths the WRITE side interns over a stream's leaf
3
+ * ids: W−1 and W (the river's grouping quantum and its off-by-one neighbour,
4
+ * so a form straddling a group boundary is reachable from either cut). */
5
+ export declare function canonicalWindows(W: number): [number, number];
6
+ /** The READ side's chain reach: how many leaf ids a canonical chain may grow
7
+ * to from one position — W², the deepest two-level composite the write side's
8
+ * windows can spell. */
9
+ export declare function chainReach(W: number): number;
10
+ /** The id of the single-byte leaf at position `p`, or null when that byte was
11
+ * never interned. */
12
+ export declare function leafIdAt(ctx: MindContext, bytes: Uint8Array, p: number): number | null;
13
+ /** The leaf ids of the window `[from, to)`, or null the moment ANY byte in it
14
+ * is unknown — the all-or-nothing read {@link canonicalChunkId} anchors on. */
15
+ export declare function leafIdRun(ctx: MindContext, bytes: Uint8Array, from: number, to: number): number[] | null;
16
+ /** The leaf ids of the LONGEST KNOWN PREFIX of `bytes` — stops at the first
17
+ * unknown byte and returns what it has (possibly empty). The caller decides
18
+ * what a partial prefix means (deposit only interns the whole-stream flat
19
+ * branch when the prefix covers everything). */
20
+ export declare function leafIdPrefix(ctx: MindContext, bytes: Uint8Array): number[];
21
+ /** The canonical W-window node ids of a byte stream, offset → id — the
22
+ * CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
23
+ * two deposits share IS the same node (hash-consing paid the comparison at
24
+ * write time). The read side of the {@link canonicalWindows} contract asked
25
+ * at every offset: a window over unknown bytes, or one never interned as a
26
+ * flat branch, has no identity and is skipped. Confluence's meet and CAST's
27
+ * frame detection both read shared/common content through this — never
28
+ * through a byte scan. */
29
+ export declare function windowIds(ctx: MindContext, bytes: Uint8Array): Map<number, number>;