@hviana/sema 0.5.2 → 0.5.4

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 (157) hide show
  1. package/AGENTS.md +115 -53
  2. package/HOW_IT_WORKS.md +291 -184
  3. package/jsr.json +6 -0
  4. package/package.json +1 -1
  5. package/src/mind/bridge.ts +10 -104
  6. package/src/mind/match.ts +497 -7
  7. package/src/mind/{prefix-completion.ts → mechanisms/prefix-completion.ts} +66 -92
  8. package/src/mind/mechanisms/recall.ts +38 -129
  9. package/src/mind/mechanisms/reference.ts +343 -0
  10. package/src/mind/mind.ts +12 -8
  11. package/src/mind/pipeline-mechanism.ts +120 -3
  12. package/src/mind/pipeline.ts +16 -2
  13. package/src/mind/traverse.ts +92 -1
  14. package/src/store.ts +13 -4
  15. package/test/33-multi-candidate.test.mjs +21 -11
  16. package/test/70-prefix-completion.test.mjs +1 -1
  17. package/test/72-prefix-candidate-supply.test.mjs +7 -9
  18. package/test/74-prefix-trap-not-sprung-early.test.mjs +1 -1
  19. package/test/76-reference-binding.test.mjs +543 -0
  20. package/dist/example/demo.d.ts +0 -1
  21. package/dist/example/demo.js +0 -39
  22. package/dist/example/train_base.d.ts +0 -93
  23. package/dist/example/train_base.js +0 -2269
  24. package/dist/src/alphabet.d.ts +0 -7
  25. package/dist/src/alphabet.js +0 -33
  26. package/dist/src/alu/src/alu.d.ts +0 -185
  27. package/dist/src/alu/src/alu.js +0 -440
  28. package/dist/src/alu/src/expr.d.ts +0 -61
  29. package/dist/src/alu/src/expr.js +0 -318
  30. package/dist/src/alu/src/index.d.ts +0 -11
  31. package/dist/src/alu/src/index.js +0 -19
  32. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  33. package/dist/src/alu/src/kernel-arith.js +0 -264
  34. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  35. package/dist/src/alu/src/kernel-bits.js +0 -152
  36. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  37. package/dist/src/alu/src/kernel-logic.js +0 -60
  38. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  39. package/dist/src/alu/src/kernel-nd.js +0 -208
  40. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  41. package/dist/src/alu/src/kernel-numeric.js +0 -366
  42. package/dist/src/alu/src/operation.d.ts +0 -168
  43. package/dist/src/alu/src/operation.js +0 -189
  44. package/dist/src/alu/src/parser.d.ts +0 -221
  45. package/dist/src/alu/src/parser.js +0 -577
  46. package/dist/src/alu/src/resonance.d.ts +0 -55
  47. package/dist/src/alu/src/resonance.js +0 -126
  48. package/dist/src/alu/src/text.d.ts +0 -31
  49. package/dist/src/alu/src/text.js +0 -73
  50. package/dist/src/alu/src/value.d.ts +0 -109
  51. package/dist/src/alu/src/value.js +0 -300
  52. package/dist/src/alu/test/alu.test.d.ts +0 -1
  53. package/dist/src/alu/test/alu.test.js +0 -764
  54. package/dist/src/bytes.d.ts +0 -14
  55. package/dist/src/bytes.js +0 -59
  56. package/dist/src/canon.d.ts +0 -45
  57. package/dist/src/canon.js +0 -85
  58. package/dist/src/config.d.ts +0 -111
  59. package/dist/src/config.js +0 -91
  60. package/dist/src/derive/src/deduction.d.ts +0 -136
  61. package/dist/src/derive/src/deduction.js +0 -159
  62. package/dist/src/derive/src/index.d.ts +0 -8
  63. package/dist/src/derive/src/index.js +0 -11
  64. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  65. package/dist/src/derive/src/priority-queue.js +0 -73
  66. package/dist/src/derive/src/rewrite.d.ts +0 -56
  67. package/dist/src/derive/src/rewrite.js +0 -100
  68. package/dist/src/derive/src/trie.d.ts +0 -90
  69. package/dist/src/derive/src/trie.js +0 -217
  70. package/dist/src/derive/test/derive.test.d.ts +0 -1
  71. package/dist/src/derive/test/derive.test.js +0 -122
  72. package/dist/src/extension.d.ts +0 -37
  73. package/dist/src/extension.js +0 -7
  74. package/dist/src/geometry.d.ts +0 -233
  75. package/dist/src/geometry.js +0 -1089
  76. package/dist/src/index.d.ts +0 -17
  77. package/dist/src/index.js +0 -19
  78. package/dist/src/ingest-cache.d.ts +0 -41
  79. package/dist/src/ingest-cache.js +0 -165
  80. package/dist/src/meter.d.ts +0 -176
  81. package/dist/src/meter.js +0 -274
  82. package/dist/src/mind/articulation.d.ts +0 -6
  83. package/dist/src/mind/articulation.js +0 -99
  84. package/dist/src/mind/attention.d.ts +0 -430
  85. package/dist/src/mind/attention.js +0 -2418
  86. package/dist/src/mind/bridge.d.ts +0 -39
  87. package/dist/src/mind/bridge.js +0 -998
  88. package/dist/src/mind/canonical.d.ts +0 -34
  89. package/dist/src/mind/canonical.js +0 -93
  90. package/dist/src/mind/frame-filler.d.ts +0 -15
  91. package/dist/src/mind/frame-filler.js +0 -535
  92. package/dist/src/mind/graph-search.d.ts +0 -294
  93. package/dist/src/mind/graph-search.js +0 -996
  94. package/dist/src/mind/index.d.ts +0 -9
  95. package/dist/src/mind/index.js +0 -5
  96. package/dist/src/mind/junction.d.ts +0 -137
  97. package/dist/src/mind/junction.js +0 -358
  98. package/dist/src/mind/learning.d.ts +0 -75
  99. package/dist/src/mind/learning.js +0 -265
  100. package/dist/src/mind/match.d.ts +0 -192
  101. package/dist/src/mind/match.js +0 -666
  102. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  103. package/dist/src/mind/mechanisms/alu.js +0 -36
  104. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  105. package/dist/src/mind/mechanisms/cast.js +0 -1186
  106. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  107. package/dist/src/mind/mechanisms/confluence.js +0 -271
  108. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  109. package/dist/src/mind/mechanisms/cover.js +0 -232
  110. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  111. package/dist/src/mind/mechanisms/extraction.js +0 -327
  112. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  113. package/dist/src/mind/mechanisms/recall.js +0 -558
  114. package/dist/src/mind/mind.d.ts +0 -386
  115. package/dist/src/mind/mind.js +0 -754
  116. package/dist/src/mind/pipeline-mechanism.d.ts +0 -183
  117. package/dist/src/mind/pipeline-mechanism.js +0 -521
  118. package/dist/src/mind/pipeline.d.ts +0 -49
  119. package/dist/src/mind/pipeline.js +0 -331
  120. package/dist/src/mind/prefix-completion.d.ts +0 -59
  121. package/dist/src/mind/prefix-completion.js +0 -270
  122. package/dist/src/mind/primitives.d.ts +0 -85
  123. package/dist/src/mind/primitives.js +0 -333
  124. package/dist/src/mind/rationale.d.ts +0 -139
  125. package/dist/src/mind/rationale.js +0 -163
  126. package/dist/src/mind/reasoning.d.ts +0 -42
  127. package/dist/src/mind/reasoning.js +0 -305
  128. package/dist/src/mind/recognition.d.ts +0 -20
  129. package/dist/src/mind/recognition.js +0 -696
  130. package/dist/src/mind/resonance.d.ts +0 -35
  131. package/dist/src/mind/resonance.js +0 -365
  132. package/dist/src/mind/trace.d.ts +0 -15
  133. package/dist/src/mind/trace.js +0 -73
  134. package/dist/src/mind/traverse.d.ts +0 -158
  135. package/dist/src/mind/traverse.js +0 -716
  136. package/dist/src/mind/types.d.ts +0 -418
  137. package/dist/src/mind/types.js +0 -179
  138. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  139. package/dist/src/rabitq-ivf/src/database.js +0 -201
  140. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  141. package/dist/src/rabitq-ivf/src/index.js +0 -4
  142. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  143. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  144. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  145. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  146. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  147. package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
  148. package/dist/src/sema.d.ts +0 -41
  149. package/dist/src/sema.js +0 -77
  150. package/dist/src/store-sqlite.d.ts +0 -184
  151. package/dist/src/store-sqlite.js +0 -942
  152. package/dist/src/store.d.ts +0 -762
  153. package/dist/src/store.js +0 -1823
  154. package/dist/src/vec.d.ts +0 -31
  155. package/dist/src/vec.js +0 -109
  156. package/src/mind/frame-filler.ts +0 -604
  157. package/test/69-frame-filler.test.mjs +0 -115
@@ -1,998 +0,0 @@
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
- // • GRADED IDENTITY — lexical geometry is tried first at
30
- // conceptThreshold(D). Differently-spelled forms fall through to VSA
31
- // company: their stored W-window occurrences ascend to learned episodes,
32
- // whose bundled halos must clear significanceBar(D), the same
33
- // distributional-evidence bar used by analogyStrength.
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
45
- // 2·recallQueryK candidate reads, and one
46
- // O(|query|·|candidate|)-bounded alignment each.
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, significanceBar } from "../geometry.js";
94
- import { bytesEqual, indexOf } from "../bytes.js";
95
- import { foldTree, perceive, read } from "./primitives.js";
96
- import { chainReach, leafIdRun } from "./canonical.js";
97
- import { allWindowsAreScaffolding, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "./traverse.js";
98
- import { rItem, rNode } from "./trace.js";
99
- import { junctionContainersFrom } from "./junction.js";
100
- import { spanHalo } from "./match.js";
101
- /** True when some query byte-range left UNACCOUNTED by `spans` contains a
102
- * STORED window — content the store has seen that the proposed reading
103
- * simply ignores. The IGNORED-KNOWN principle: a span may be dismissed
104
- * only when the store itself has never seen it; known content the
105
- * alignment failed to account for is grounds for refusal, while genuinely
106
- * novel spans (an untrained word, stray punctuation) remain tolerable.
107
- * Shared by the substitution bridge's own acceptance and CAST's
108
- * frame-tier comparison gate (cast.ts). Pure attestation — no
109
- * similarity, no constants. */
110
- export function dismissedKnownContent(ctx, query, spans) {
111
- const W = ctx.space.maxGroup;
112
- const sorted = [...spans].sort((a, b) => a[0] - b[0]);
113
- let cursor = 0;
114
- for (const [s, e] of [...sorted, [query.length, query.length]]) {
115
- for (let o = cursor; o + W <= s; o++) {
116
- const ids = leafIdRun(ctx, query, o, o + W);
117
- if (ids !== null && ctx.store.findBranch(ids) !== null)
118
- return true;
119
- }
120
- cursor = Math.max(cursor, e);
121
- }
122
- return false;
123
- }
124
- /** Extend a seed match (query offset qo ↔ candidate offset co) to its
125
- * maximal common run, then walk outward in both directions collecting
126
- * further common runs of at least W bytes across bounded mismatch gaps
127
- * (each side ≤ chainReach). Returns the matched query spans and the
128
- * mismatch pairs between consecutive runs. */
129
- function align(ctx, q, c, qo, co) {
130
- const W = ctx.space.maxGroup;
131
- const reachCap = chainReach(W);
132
- // Maximal run around the seed.
133
- let qs = qo, ss = co;
134
- while (qs > 0 && ss > 0 && q[qs - 1] === c[ss - 1]) {
135
- qs--;
136
- ss--;
137
- }
138
- let qe = qo, se = co;
139
- while (qe < q.length && se < c.length && q[qe] === c[se]) {
140
- qe++;
141
- se++;
142
- }
143
- const matched = [[qs, qe]];
144
- const gaps = [];
145
- // The next common run of ≥ W bytes past (qi, si), with each side's gap
146
- // bounded by chainReach; smallest total gap wins (nearest continuation).
147
- const runLenAt = (qi, si) => {
148
- let n = 0;
149
- while (qi + n < q.length && si + n < c.length && q[qi + n] === c[si + n]) {
150
- n++;
151
- }
152
- return n;
153
- };
154
- // RIGHT sweep.
155
- let qi = qe, si = se;
156
- for (;;) {
157
- let found = false;
158
- for (let total = 1; total <= 2 * reachCap && !found; total++) {
159
- for (let gq = 0; gq <= Math.min(total, reachCap); gq++) {
160
- const gs = total - gq;
161
- if (gs > reachCap)
162
- continue;
163
- if (qi + gq >= q.length || si + gs >= c.length)
164
- continue;
165
- const n = runLenAt(qi + gq, si + gs);
166
- if (n >= W || qi + gq + n === q.length) {
167
- if (n === 0)
168
- continue;
169
- if (gq > 0 || gs > 0) {
170
- gaps.push({ qs: qi, qe: qi + gq, cs: si, ce: si + gs });
171
- }
172
- matched.push([qi + gq, qi + gq + n]);
173
- qi = qi + gq + n;
174
- si = si + gs + n;
175
- found = true;
176
- break;
177
- }
178
- }
179
- }
180
- if (!found)
181
- break;
182
- }
183
- // LEFT sweep (mirror).
184
- qi = qs;
185
- si = ss;
186
- for (;;) {
187
- let found = false;
188
- for (let total = 1; total <= 2 * reachCap && !found; total++) {
189
- for (let gq = 0; gq <= Math.min(total, reachCap); gq++) {
190
- const gs = total - gq;
191
- if (gs > reachCap)
192
- continue;
193
- if (qi - gq <= 0 || si - gs <= 0)
194
- continue;
195
- // Run ENDING at (qi - gq, si - gs).
196
- let n = 0;
197
- while (n < qi - gq && n < si - gs &&
198
- q[qi - gq - 1 - n] === c[si - gs - 1 - n]) {
199
- n++;
200
- }
201
- if (n >= W || n === qi - gq) {
202
- if (n === 0)
203
- continue;
204
- if (gq > 0 || gs > 0) {
205
- gaps.push({ qs: qi - gq, qe: qi, cs: si - gs, ce: si });
206
- }
207
- matched.push([qi - gq - n, qi - gq]);
208
- qi = qi - gq - n;
209
- si = si - gs - n;
210
- found = true;
211
- break;
212
- }
213
- }
214
- }
215
- if (!found)
216
- break;
217
- }
218
- return { matched, gaps };
219
- }
220
- /** Recall's corroborated-substitution bridge — see the module comment.
221
- * Returns the best bridged grounding proposal, or null. */
222
- /** `proposed` is a THUNK, not a list: the bridge's own cheap gates (the
223
- * two-quantum query floor and the O(|query|) stored-window anchor scan)
224
- * decide whether ANY candidate can be aligned, and they need no proposals
225
- * to do it. Resolving the caller's proposals eagerly meant recall paid its
226
- * exhaustive whole-index resonance — the most expensive single act on the
227
- * refusal path — for every query, including the ones whose windows the
228
- * store has never seen and which the anchor scan rejects outright. Same
229
- * investment discipline the mechanism floors follow (AGENTS §2.6): never
230
- * compute a shared analysis just to discard it. */
231
- export async function substitutionBridge(ctx, query, proposed = async () => []) {
232
- const meter = ctx.meter;
233
- return meter
234
- ? meter.time("substitutionBridge", () => bridgeImpl(ctx, query, proposed))
235
- : bridgeImpl(ctx, query, proposed);
236
- }
237
- async function bridgeImpl(ctx, query, proposed) {
238
- const W = ctx.space.maxGroup;
239
- if (query.length < 2 * W)
240
- return null;
241
- const bound = hubBound(ctx);
242
- const N = corpusN(ctx);
243
- const marketScale = ctx.cfg.recallQueryK * W;
244
- const candidateCap = N <= marketScale ** 3 ? bound : 2 * ctx.cfg.recallQueryK;
245
- const bar = conceptThreshold(ctx.store.D);
246
- const synonymBar = significanceBar(ctx.store.D);
247
- const reachCap = chainReach(W);
248
- const diagnostics = ctx.trace
249
- ? {
250
- anchors: 0,
251
- picked: 0,
252
- proposed: 0,
253
- structuralProposed: 0,
254
- proposedGrounded: 0,
255
- synonymChecks: 0,
256
- bestSynonym: 0,
257
- climbed: 0,
258
- phraseScale: 0,
259
- seeded: 0,
260
- aligned: 0,
261
- structurallyValid: 0,
262
- coverageValid: 0,
263
- identityValid: 0,
264
- knownContentValid: 0,
265
- bestCovered: 0,
266
- bestRank: -1,
267
- closest: [],
268
- }
269
- : null;
270
- // PHRASE-SCALE CANDIDATE CAP — the same |content|·W bound the weave
271
- // (pipeline-mechanism.ts), the cross-region junction ladder's
272
- // `maxInterior`, and structural resonance's `maxSiblingBytes` all apply,
273
- // for the same reason and now at the one remaining place that read
274
- // candidate contexts WHOLE.
275
- //
276
- // The bridge accepts a candidate only when the query is DOMINATED by its
277
- // matched runs plus substitutions, with at most one window W of slack at
278
- // each edge and at most one chain reach (W²) per interior gap — so the
279
- // candidate region an accepted alignment can ever consume is bounded by
280
- // |query|·W. Content beyond that cannot participate in any alignment
281
- // this function would accept; reading it is pure cost. And a candidate
282
- // an order of magnitude past the query is not a paraphrase of it: it is a
283
- // document or a whole conversation that merely quotes a phrase, and
284
- // grounding through ITS learnt edge voices that document's continuation,
285
- // not a phrase answer.
286
- //
287
- // Measured on the 17.7M-node / 325K-context store: uncapped, the refusal
288
- // path materialised up to ~1 MB of candidate bytes per query (up to √N
289
- // proposals plus √N climbed contexts, each read in full), and the frame-
290
- // unanimity scan — which walks EVERY collected candidate's bytes, inside
291
- // the per-gap expansion loop — paid that volume back tens of times per
292
- // substitution. Recall's run() was 0.7–2.6 s per refusing query.
293
- const capBytes = query.length * W;
294
- /** A candidate's bytes, phrase-scale capped: null when it exceeds the cap
295
- * (read one byte past it, so "too long" is decided without materialising
296
- * the rest) or has no content. */
297
- const candidateBytes = (sid) => {
298
- const b = read(ctx, sid, capBytes + 1);
299
- return b.length === 0 || b.length > capBytes ? null : b;
300
- };
301
- // 1. The query's stored windows, rarest first (fewest containers — the
302
- // most discriminative anchors; hub-clamped like every fan-out read).
303
- // The scan doubles as the ONE store probe of every query window: the
304
- // per-offset stored/reused facts it establishes serve every later
305
- // attestation and ignored-known check as plain array reads (the same
306
- // probes repeated per candidate dominated the refusal-path cost).
307
- const nWin = Math.max(0, query.length - W + 1);
308
- const winStored = new Uint8Array(nWin);
309
- const winReused = new Uint8Array(nWin);
310
- const anchors = [];
311
- for (let o = 0; o + W <= query.length; o++) {
312
- const ids = leafIdRun(ctx, query, o, o + W);
313
- if (ids === null)
314
- continue;
315
- const id = ctx.store.findBranch(ids);
316
- if (id === null)
317
- continue;
318
- winStored[o] = 1;
319
- const rarity = ctx.store.containersSlice(id, 0, bound + 1).length;
320
- if (rarity >= 2)
321
- winReused[o] = 1;
322
- if (rarity === 0)
323
- continue;
324
- anchors.push({ off: o, id, rarity });
325
- }
326
- if (diagnostics)
327
- diagnostics.anchors = anchors.length;
328
- if (anchors.length === 0) {
329
- ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [], "no stored query window can anchor a corroborated substitution", undefined, diagnostics);
330
- return null;
331
- }
332
- // NO DISCRIMINATING LITERAL EVIDENCE — abstain (§2.13). A bridge grounds
333
- // through the literal spans it did NOT substitute; those anchors are the
334
- // whole of its evidence. When every one of them is SATURATED — containment
335
- // clamped at the √N hub bound, i.e. the window is corpus-global scaffolding
336
- // — the query's unsubstituted part discriminates nothing, and the single
337
- // substituted span is carrying the entire semantic load. That is not a
338
- // corroborated bridge; it is a template match, and it FABRICATES.
339
- //
340
- // Measured on the trained store (hubBound 571). "What is the capital of"
341
- // has 19 anchors, ALL saturated ("What":572, "hat ":572, "at i":572 …), and
342
- // bridged to an unrelated trained context about an integral, voiced
343
- // confidently. Every query the bridge answers CORRECTLY has at least one
344
- // unsaturated anchor, by a wide margin and with no near miss:
345
- // "Who is the author of Hamlet?" → "let?":12, "How do you say 'thank you'
346
- // in French?" → "y 't":3, "…largest planet…" → "tem?":31, "What is the
347
- // capital of France?" → "f Fr":114. The honest-silence probes sit on the
348
- // same side as the correct ones ("Zamu":3), so this gate is not what makes
349
- // them silent and cannot be credited for them.
350
- //
351
- // This introduces NO new threshold: `bound` is the same √N reading of "hub"
352
- // the anchor scan already clamps its own containment read to (§2.2, §2.7).
353
- if (allWindowsAreScaffolding(ctx, query)) {
354
- ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [], "every query window that could anchor is corpus-global scaffolding — " +
355
- "no literal evidence to corroborate a substitution", undefined, diagnostics);
356
- return null;
357
- }
358
- // CORROBORATION (see the module-level doc) over the precomputed window
359
- // facts: the query span [qs,qe) attests when every full W-window inside
360
- // it is a stored flat form and at least one is reused across ≥ 2
361
- // containers. Spans shorter than W carry no window of their own and can
362
- // never substitute.
363
- const attestedQ = (qs, qe) => {
364
- if (qe - qs < W)
365
- return false;
366
- let reused = false;
367
- for (let o = qs; o + W <= qe; o++) {
368
- if (!winStored[o])
369
- return false;
370
- if (winReused[o])
371
- reused = true;
372
- }
373
- return reused;
374
- };
375
- // dismissedKnownContent (see above) over the same precomputed facts.
376
- const dismissedKnownQ = (spans) => {
377
- const sorted = [...spans].sort((a, b) => a[0] - b[0]);
378
- let cursor = 0;
379
- for (const [s, e] of [...sorted, [query.length, query.length]]) {
380
- for (let o = cursor; o + W <= s; o++)
381
- if (winStored[o])
382
- return true;
383
- cursor = Math.max(cursor, e);
384
- }
385
- return false;
386
- };
387
- // ── EXPLAINED SPANS — the scaffolding judgement, corpus-global ──────────
388
- //
389
- // The question every gap poses is "may the two forms differ HERE without
390
- // differing in what they SAY?", and that is the discriminative-vs-
391
- // scaffolding question AGENTS §2.7 names, over the CORPUS-GLOBAL
392
- // population. It already has one definition — `dominates(reachOf(...), N)`,
393
- // the same gate confluence's filler test uses ("scaffolding never binds").
394
- // Nothing new is derived here; the bar is read, not invented.
395
- //
396
- // A span is explained when EITHER
397
- // • it is sub-quantum (< W) — typographic glue, the tolerance identityBar
398
- // already prices ("below one river window, byte overlap is chance"); or
399
- // • every full W-window inside it is COMMON by the store's own climb:
400
- // the ascent SATURATES (the window sits in more places than √N — the
401
- // climb's own definition of non-discriminative), or it resolves to a
402
- // majority of the corpus's contexts. "the process of ", " is the ".
403
- //
404
- // THE READING MATTERS, not just the population (AGENTS §2.7). This
405
- // deliberately does NOT go through `reachOf`, which maps BOTH "saturated"
406
- // and "reaches nothing" to Infinity. For IDF weighting those are the same
407
- // thing (no usable identity evidence); for THIS question they are
408
- // opposites — a window reaching nothing is novel content, the most
409
- // discriminative material there is, and reading it as Infinity would call
410
- // it scaffolding. Measured: with `reachOf`, "Is water wet?" was answered
411
- // with "No, heavy water is not wet." — "heav"/"eavy" occur once, reach no
412
- // edge-bearing ancestor, and were written off as filler. So an
413
- // empty-rooted window is NEVER explained, and neither is an untrained one
414
- // (the same principle attestedQ applies to the query side).
415
- const reachMemo = sharedReachMemo(ctx);
416
- const explainedSpan = (bytes, from, to) => {
417
- if (to - from < W)
418
- return true;
419
- const common = (start, end) => {
420
- if (end - start < W)
421
- return false;
422
- for (let o = start; o + W <= end; o++) {
423
- const ids = leafIdRun(ctx, bytes, o, o + W);
424
- if (ids === null)
425
- return false;
426
- const wid = ctx.store.findBranch(ids);
427
- if (wid === null)
428
- return false;
429
- const r = edgeAncestors(ctx, wid, N, reachMemo);
430
- if (r.saturated)
431
- continue; // in too many places to discriminate
432
- if (r.roots.length === 0)
433
- return false; // reaches nothing: novel content
434
- if (!dominates(r.contextsReached, N))
435
- return false;
436
- }
437
- return true;
438
- };
439
- if (common(from, to))
440
- return true;
441
- // Alignment may attach the shared delimiter to either side of an inserted
442
- // phrase. Up to W-1 boundary bytes are below the fold's identity scale;
443
- // classify the phrase by a full-window interior core when one exists.
444
- // This does not erase a short discriminative insertion: "heavy" still
445
- // leaves the full `heav`/`eavy` windows for the corpus-global test.
446
- for (let left = 0; left < W; left++) {
447
- for (let right = 0; right < W; right++) {
448
- if (left + right === 0 || left + right >= W)
449
- continue;
450
- if (common(from + left, to - right))
451
- return true;
452
- }
453
- }
454
- return false;
455
- };
456
- anchors.sort((a, b) => a.rarity - b.rarity);
457
- // Up to W anchors, at least one window apart — the quantum's own count.
458
- const picked = [];
459
- for (const a of anchors) {
460
- if (picked.length >= W)
461
- break;
462
- if (picked.some((p) => Math.abs(p.off - a.off) < W))
463
- continue;
464
- picked.push(a);
465
- }
466
- if (diagnostics)
467
- diagnostics.picked = picked.length;
468
- // 2. Candidate trained contexts. Two proposal channels, one verifier:
469
- // (a) the caller's PROPOSED hits — recall's whole-query resonance
470
- // ranking, the retrieval structure built to surface near-paraphrase
471
- // forms the window climb cannot single out at corpus scale; (b) each
472
- // picked anchor climbed to its edge-bearing ancestors (the same climb
473
- // consensus voting uses). Both are only ever PROPOSALS — every
474
- // candidate passes the same byte-exact alignment and gates below.
475
- const seen = new Set();
476
- const candidates = [];
477
- // Exact co-occurrence proposes contexts the whole-form ANN can miss when a
478
- // short insertion shifts every later fold boundary. The byte alignment below
479
- // remains the decider. All pairs share one candidateCap·W junction
480
- // allowance, ordered
481
- // by their rarest side and then span: a rare content window joined to a
482
- // distant frame boundary discriminates a whole question better than two
483
- // neighbouring rare windows inside the same word.
484
- if (query.length <= 2 * reachCap) {
485
- const pairs = [];
486
- for (let i = 0; i < picked.length; i++) {
487
- for (let j = i + 1; j < picked.length; j++) {
488
- pairs.push([picked[i], picked[j]]);
489
- }
490
- }
491
- pairs.sort((a, b) => Math.min(a[0].rarity, a[1].rarity) -
492
- Math.min(b[0].rarity, b[1].rarity) ||
493
- Math.abs(b[0].off - b[1].off) - Math.abs(a[0].off - a[1].off) ||
494
- a[0].rarity + a[1].rarity - b[0].rarity - b[1].rarity);
495
- const structuralBudget = {
496
- n: chainReach(W) * W * ctx.cfg.recallQueryK,
497
- };
498
- for (const [left, right] of pairs.slice(0, W)) {
499
- const found = junctionContainersFrom(ctx, query.subarray(left.off, left.off + W), query.subarray(right.off, right.off + W), capBytes, [left.id], [right.id], structuralBudget, true);
500
- for (const hit of found) {
501
- if (candidates.length >= candidateCap)
502
- break;
503
- if (seen.has(hit.id) || !ctx.store.hasNext(hit.id))
504
- continue;
505
- seen.add(hit.id);
506
- candidates.push(hit.id);
507
- if (diagnostics)
508
- diagnostics.structuralProposed++;
509
- }
510
- }
511
- }
512
- // Once exact structural proposals fill the shared cap, no caller proposal
513
- // can enter the verifier. Do not evaluate the lazy ANN thunk merely to
514
- // discard every result at the loop's first guard.
515
- const proposedIds = candidates.length < candidateCap ? await proposed() : [];
516
- if (diagnostics)
517
- diagnostics.proposed = proposedIds.length;
518
- for (const sid of proposedIds) {
519
- if (candidates.length >= candidateCap)
520
- break;
521
- if (seen.has(sid))
522
- continue;
523
- seen.add(sid);
524
- const tb = candidateBytes(sid);
525
- if (tb === null)
526
- continue;
527
- if (!picked.some((a) => indexOf(tb, query.subarray(a.off, a.off + W), 0) >= 0))
528
- continue;
529
- let use = sid;
530
- if (!ctx.store.hasNext(use)) {
531
- const folded = foldTree(ctx, perceive(ctx, tb), 0).node;
532
- if (folded === null || folded === sid || !ctx.store.hasNext(folded)) {
533
- continue;
534
- }
535
- use = folded;
536
- if (seen.has(use))
537
- continue;
538
- seen.add(use);
539
- }
540
- candidates.push(use);
541
- if (diagnostics)
542
- diagnostics.proposedGrounded++;
543
- }
544
- // Proposal channel — carries its caller's own bound (recall's resonance
545
- // k), sharing the 2·recallQueryK candidate allowance
546
- // with the structural and climb channels. A proposal may
547
- // be a FLAT content twin whose continuation edge lives on the
548
- // fold-shaped deposit node with the same bytes — the same twin split
549
- // canonResolve bridges by re-folding (primitives.ts) — but the re-fold
550
- // (a full perceive of the candidate's bytes) is paid only for proposals
551
- // that could align at all: alignment can only seed at a picked anchor
552
- // window occurring literally in the candidate (measured: unconditional
553
- // re-folds multiplied the refusal-path latency several-fold).
554
- // FIRST TOUCH of the caller's proposals — past every gate that could have
555
- // refused without them (see substitutionBridge's doc).
556
- // Climb channel — edge-bearing ancestors only, decided by the indexed
557
- // O(1) hasNext; no byte is read here (the climb visits hundreds of
558
- // roots, and reading each was measured to dominate the refusal path).
559
- for (const a of picked) {
560
- const reach = edgeAncestors(ctx, a.id, N, reachMemo);
561
- for (const sid of reach.roots) {
562
- if (candidates.length >= candidateCap)
563
- break;
564
- if (seen.has(sid))
565
- continue;
566
- seen.add(sid);
567
- if (!ctx.store.hasNext(sid))
568
- continue;
569
- candidates.push(sid);
570
- if (diagnostics)
571
- diagnostics.climbed++;
572
- }
573
- if (candidates.length >= candidateCap)
574
- break;
575
- }
576
- // 3. Align each candidate; gate its mismatches; keep the best.
577
- // Over-cap candidates are dropped here rather than earlier: the climb
578
- // channel deliberately reads no bytes while collecting (the climb visits
579
- // hundreds of roots), so this is where its proposals are first sized.
580
- //
581
- // Candidate bytes are read LAZILY — on first access during the seed
582
- // check — not eagerly for every collected id. Most climb-proposed
583
- // candidates fail the seed check and never reach the expensive identity
584
- // and frame-consensus gates.
585
- //
586
- // Frame unanimity is different: once any candidate reaches that gate, it
587
- // must be evaluated against the COMPLETE collected candidate population,
588
- // not only the prefix whose bytes happened to be loaded earlier. The full
589
- // phrase-scale population is therefore materialised once, lazily, on the
590
- // first unanimous() call and reused afterward.
591
- //
592
- // Null results are memoised too, so an empty or over-cap candidate is never
593
- // read repeatedly by the candidate loop and the population materialiser.
594
- const candidateByteMemo = new Map();
595
- /** Read one candidate at most once. Returns null when it exceeds the
596
- * phrase-scale cap or has no content. */
597
- const bytesOfCandidate = (sid) => {
598
- const cached = candidateByteMemo.get(sid);
599
- if (cached !== undefined)
600
- return cached;
601
- const b = candidateBytes(sid);
602
- candidateByteMemo.set(sid, b);
603
- return b;
604
- };
605
- let framePopulation = null;
606
- /** Return the complete phrase-scale candidate population.
607
- *
608
- * This is intentionally lazy: queries that never reach frame unanimity
609
- * keep the cheap per-candidate seed path. Once required, every candidate
610
- * is bounded by candidateBytes(), loaded at most once, and all subsequent
611
- * unanimity checks observe the same order-independent population.
612
- */
613
- const ensureFramePopulation = () => {
614
- if (framePopulation !== null) {
615
- return framePopulation;
616
- }
617
- const complete = new Map();
618
- for (const sid of candidates) {
619
- const bytes = bytesOfCandidate(sid);
620
- if (bytes !== null) {
621
- complete.set(sid, bytes);
622
- }
623
- }
624
- framePopulation = complete;
625
- if (diagnostics) {
626
- diagnostics.phraseScale = complete.size;
627
- }
628
- return complete;
629
- };
630
- // FRAME UNANIMITY: a substitution U → C inside the frame (Lf, Rf) is
631
- // groundable only when the collected candidates — the store's own sample
632
- // of contexts sharing the query's content — are unanimous about the
633
- // filler: every occurrence of Lf…Rf across them holds either U (the
634
- // query's own word, corroboration) or C. A THIRD distinct filler means
635
- // the frame is a VALUE SLOT ("was born in _" held Germany, Poland,
636
- // England, Serbia — observed live), and picking one value would assert
637
- // knowledge the store does not have. Consensus of the store's own
638
- // instances, no similarity judgement, no tuned constant.
639
- // Requires a genuine CORROBORATING sighting, not merely the absence of a
640
- // conflicting one: scanning only the handful of resonance/climb-proposed
641
- // candidates means the frame can easily occur NOWHERE else among them
642
- // (observed live: "of Fra[nce]" -> "of Spain si[nce]" passed vacuously —
643
- // the frame "tal …nce " never recurred among the collected candidates at
644
- // all, so there was no consensus, only an absence of disagreement, yet
645
- // the substitution was accepted). "Unanimous" must mean the store's own
646
- // instances agree, which requires at least one instance to consult.
647
- const unanimous = (u, c, lf, rf) => {
648
- const population = ensureFramePopulation();
649
- for (const bytes of population.values()) {
650
- let from = 0;
651
- for (;;) {
652
- const i = indexOf(bytes, lf, from);
653
- if (i < 0)
654
- break;
655
- from = i + 1;
656
- const start = i + lf.length;
657
- const j = indexOf(bytes.subarray(start, start + reachCap + rf.length), rf, 0);
658
- if (j < 0)
659
- continue;
660
- const filler = bytes.subarray(start, start + j);
661
- if (filler.length === 0)
662
- continue;
663
- if (!bytesEqual(filler, u) && !bytesEqual(filler, c))
664
- return false;
665
- }
666
- }
667
- return true;
668
- };
669
- // (A candidate need NOT contain the query's rarest window literally: the
670
- // rarest window may sit INSIDE the very word being substituted (observed
671
- // live: "chemical symbol for water" whose rarest window "l sy" spans
672
- // "symbol" — the trained formula-question can never contain it). A
673
- // candidate that instead dodges the query's known content by writing it
674
- // off as gaps is refused by dismissedKnownContent below, which subsumes
675
- // the old rarest-window containment gate: rare windows inside an accepted
676
- // substitution are accounted for; rare windows outside the accepted spans
677
- // force refusal (the Matrix-synopsis junk stays dead by exactly that
678
- // check — verified live).
679
- let best = null;
680
- let bestAccounted = 0;
681
- const queryHaloMemo = new Map();
682
- const candidateHaloMemo = new Map();
683
- for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
684
- const sid = candidates[candidateIndex];
685
- // Read bytes lazily — most climb-proposed candidates have no picked
686
- // anchor window and will never pass the seed check below, so their
687
- // bytes are never read at all.
688
- const cBytes = bytesOfCandidate(sid);
689
- if (cBytes === null)
690
- continue;
691
- // Seed at the rarest picked anchor that literally occurs in this
692
- // candidate.
693
- let seed = null;
694
- for (const a of picked) {
695
- const co = indexOf(cBytes, query.subarray(a.off, a.off + W), 0);
696
- if (co >= 0) {
697
- seed = { qo: a.off, co };
698
- break;
699
- }
700
- }
701
- if (seed === null)
702
- continue;
703
- if (diagnostics)
704
- diagnostics.seeded++;
705
- const { matched, gaps } = align(ctx, query, cBytes, seed.qo, seed.co);
706
- if (diagnostics)
707
- diagnostics.aligned++;
708
- // Investment gate: even treating every two-sided mismatch as a valid
709
- // synonym, can this alignment satisfy the bridge's final coverage rule?
710
- // Distributional span composition performs bounded ancestor climbs; never
711
- // pay for it on a candidate arithmetic already proves cannot win.
712
- let matchStart = query.length;
713
- let matchEnd = 0;
714
- let potential = 0;
715
- for (const [s, e] of matched) {
716
- matchStart = Math.min(matchStart, s);
717
- matchEnd = Math.max(matchEnd, e);
718
- potential += e - s;
719
- }
720
- for (const g of gaps) {
721
- if (g.qe > g.qs && g.ce > g.cs)
722
- potential += g.qe - g.qs;
723
- }
724
- if (matchStart > W || query.length - matchEnd > W ||
725
- !dominates(potential, query.length))
726
- continue;
727
- // Gate each mismatch: a corroborated, geometrically-identified
728
- // substitution counts as accounted; anything else stays a gap.
729
- //
730
- // A raw mismatch is the MINIMAL byte diff ("big" ↔ "lar" inside
731
- // biggest/largest), usually below the scale at which either side is a
732
- // corpus unit. The true unit is found by EXPANSION: absorb flanking
733
- // bytes from the adjacent matched runs (equal on both sides by
734
- // construction, so both spans grow identically) until the query side
735
- // attests and the pair clears the concept bar — smallest expansion
736
- // first, capped at chainReach like the mismatch itself. Absorbed
737
- // bytes were already matched, so coverage is unchanged.
738
- const subs = [];
739
- let ok = true;
740
- for (const g of gaps) {
741
- const uLen = g.qe - g.qs, cLen = g.ce - g.cs;
742
- if (uLen === 0 || cLen === 0 || uLen > reachCap || cLen > reachCap) {
743
- // Pure insertion/deletion or over-long mismatch. Query-side: one
744
- // perception window (the identityBar tolerance). Candidate-side:
745
- // one chain reach (W², the two-level composite bound) — a genuine
746
- // paraphrase inserts inflection-scale material ("does water boil"
747
- // ↔ "should water be boiled"), while a divergent candidate jumps
748
- // hundreds of bytes between the query's frames.
749
- if (uLen > W || cLen > reachCap)
750
- ok = false;
751
- continue;
752
- }
753
- // RAW BALANCE gate (closes the OPEN GAP above): the two sides of a
754
- // genuine lexical substitution swap comparable amounts of content —
755
- // "big"/"lar" (3/3 bytes, before expansion absorbs the shared "gest"
756
- // suffix to reach an attestable "biggest"/"largest"). A candidate
757
- // whose two sentences simply diverge into unrelated continuations
758
- // produces a LOPSIDED raw mismatch instead — the live wrong answer's
759
- // raw gap was "Fra"/"Spai" widened to (3,8) by the align sweep
760
- // finding a coincidental "nce " match years later in "since" — 3
761
- // bytes of query content standing in for 8 bytes of candidate
762
- // content is not a word swap, it is two different sentences that
763
- // happen to share a few letters. Uses the SAME dominates() bar
764
- // (part*2 > whole) applied throughout the codebase, symmetrically:
765
- // the smaller raw side must be more than half the larger. Applies
766
- // to the RAW gap for GEOMETRIC identity, before expansion — expansion
767
- // only ever grows both sides by IDENTICAL absorbed bytes, so it cannot
768
- // fix an imbalance that was already there. Distributional synonym
769
- // evidence is exempt: two phrases may occupy the same role at very
770
- // different lengths.
771
- let accepted = false;
772
- const balanced = dominates(Math.min(uLen, cLen), Math.max(uLen, cLen));
773
- const maxExtra = reachCap - Math.max(uLen, cLen);
774
- outer: for (let extra = 0; extra <= maxExtra; extra++) {
775
- for (let a = 0; a <= extra; a++) {
776
- const b = extra - a;
777
- const qs2 = g.qs - a, qe2 = g.qe + b;
778
- const cs2 = g.cs - a, ce2 = g.ce + b;
779
- if (qs2 < 0 || qe2 > query.length)
780
- continue;
781
- if (cs2 < 0 || ce2 > cBytes.length)
782
- continue;
783
- // INTERIOR gate: a substitution must sit INSIDE matched
784
- // structure — at least one full window of matched bytes must
785
- // remain adjacent on BOTH sides after absorption. Every junk
786
- // substitution observed live sat at the query's edge, with only
787
- // terminal punctuation beyond it ("…born in [England].",
788
- // "…capital of [Zamunda]?"): an edge mismatch is the query
789
- // trailing off into different content, not a word standing in a
790
- // shared frame.
791
- const leftOk = matched.some(([s, e]) => e >= qs2 && qs2 - s >= W);
792
- const rightOk = matched.some(([s, e]) => s <= qe2 && e - qe2 >= W);
793
- if (!leftOk || !rightOk)
794
- continue;
795
- if (!attestedQ(qs2, qe2))
796
- continue;
797
- const u = query.subarray(qs2, qe2);
798
- const cSpan = cBytes.subarray(cs2, ce2);
799
- const geometric = cosine(perceive(ctx, u).v, perceive(ctx, cSpan).v);
800
- const qKey = `${qs2}:${qe2}`;
801
- let qHalo = queryHaloMemo.get(qKey);
802
- if (qHalo === undefined) {
803
- qHalo = spanHalo(ctx, query, qs2, qe2);
804
- queryHaloMemo.set(qKey, qHalo);
805
- }
806
- const cKey = `${sid}:${cs2}:${ce2}`;
807
- let cHalo = candidateHaloMemo.get(cKey);
808
- if (cHalo === undefined) {
809
- cHalo = spanHalo(ctx, cBytes, cs2, ce2);
810
- candidateHaloMemo.set(cKey, cHalo);
811
- }
812
- const distributional = qHalo !== null && cHalo !== null
813
- ? cosine(qHalo, cHalo)
814
- : 0;
815
- if (diagnostics) {
816
- diagnostics.synonymChecks++;
817
- diagnostics.bestSynonym = Math.max(diagnostics.bestSynonym, distributional);
818
- }
819
- // Graded identity: byte geometry remains the cheap first tier;
820
- // VSA company is the synonym tier when differently-spelled forms
821
- // occupy the same learnt distributional role.
822
- if ((!balanced || geometric < bar) &&
823
- distributional < synonymBar) {
824
- continue;
825
- }
826
- if (!unanimous(u, cSpan, query.subarray(qs2 - W, qs2), query.subarray(qe2, qe2 + W)))
827
- continue;
828
- subs.push({ qs: qs2, qe: qe2, cs: cs2, ce: ce2 });
829
- accepted = true;
830
- break outer;
831
- }
832
- }
833
- if (!accepted && (uLen > W || cLen > reachCap))
834
- ok = false;
835
- }
836
- // A candidate with ZERO gaps needs no substitution and might seem like
837
- // the strongest possible bridge, but accepting it here is a trap: this
838
- // mechanism runs only where recall's own resonance/echo tiers already
839
- // declined to ground a same-shape, zero-substitution match — usually
840
- // because the query is a strict byte-PREFIX of several candidates
841
- // (many trained "The capital of X is Y." facts share the query "The
842
- // capital of France is" as a substring once the true France fact is
843
- // filtered out for lacking a continuation edge) and nothing here
844
- // corroborates picking one candidate's completion over another's
845
- // (observed live: prefix-completion bridged to an unrelated "London"
846
- // trivia distractor over the true France fact, which precedes it in
847
- // resonance rank but has no outgoing edge to bridge through). This
848
- // mechanism exists to explain SUBSTITUTIONS; a query needing none is
849
- // recall's job, not the bridge's.
850
- if (!ok)
851
- continue;
852
- if (diagnostics)
853
- diagnostics.structurallyValid++;
854
- // Coverage: matched runs plus accepted substitutions must dominate the
855
- // query, every interior gap already proved ≤ W above, and the EDGES
856
- // must be explained to the same one-window tolerance — the same "at
857
- // most one river window of foreign content" identityBar prices. The
858
- // live junk this closes: alignments that matched a query's scaffolding
859
- // and one substitution but left the query's whole trailing content
860
- // ("…planet orbiting our sun.", 24 bytes) unexplained, yet still
861
- // half-dominated the byte count.
862
- const spans = [
863
- ...matched,
864
- ...subs.map((s) => [s.qs, s.qe]),
865
- ].sort((x, y) => x[0] - y[0]);
866
- let covered = 0;
867
- let reachEnd = 0;
868
- for (const [s, e] of spans) {
869
- if (e <= reachEnd)
870
- continue;
871
- covered += e - Math.max(s, reachEnd);
872
- reachEnd = Math.max(reachEnd, e);
873
- }
874
- if (diagnostics) {
875
- diagnostics.bestCovered = Math.max(diagnostics.bestCovered, covered);
876
- const candidateGapBytes = gaps.reduce((n, g) => n + g.ce - g.cs, 0);
877
- diagnostics.closest.push({
878
- id: sid,
879
- covered,
880
- leading: spans[0][0],
881
- trailing: query.length - reachEnd,
882
- gaps: gaps.length,
883
- substitutions: subs.length,
884
- queryGapBytes: gaps.reduce((n, g) => n + g.qe - g.qs, 0),
885
- candidateGapBytes,
886
- gapRanges: gaps.map((g) => [g.qs, g.qe, g.cs, g.ce]),
887
- candidateSurplus: cBytes.length - covered - candidateGapBytes,
888
- gapsExplained: gaps.every((g) => explainedSpan(cBytes, g.cs, g.ce)),
889
- });
890
- diagnostics.closest.sort((a, b) => b.covered - a.covered ||
891
- a.leading + a.trailing - b.leading - b.trailing ||
892
- a.id - b.id);
893
- if (diagnostics.closest.length > W)
894
- diagnostics.closest.length = W;
895
- }
896
- if (spans[0][0] > W || query.length - reachEnd > W)
897
- continue;
898
- if (!dominates(covered, query.length))
899
- continue;
900
- if (diagnostics)
901
- diagnostics.coverageValid++;
902
- // ZERO-SUBSTITUTION ADMISSION — an IDENTITY claim, not a substitution.
903
- //
904
- // A candidate needing no substitution is normally refused (see the trap
905
- // above), and that refusal is right for the case it was written for: the
906
- // query is a strict byte-PREFIX of several candidates, each of which
907
- // continues differently, and nothing here corroborates picking one
908
- // continuation over another. But that trap has a signature — the
909
- // candidate carries substantial content BEYOND the alignment, and that
910
- // surplus is exactly the "answer" the bridge would be inventing.
911
- //
912
- // The opposite shape is not ambiguous at all: the alignment explains BOTH
913
- // strings end to end, and the only thing between them is sub-quantum glue
914
- // — typographic punctuation the fold treats as structure. Then the two
915
- // are the SAME learnt form, and grounding through its edge returns that
916
- // form's own trained answer, never a chosen-among-many completion.
917
- //
918
- // Why the ladder cannot reach these otherwise: the gist is a STRUCTURAL
919
- // signature, so a mid-string insertion shifts every fold boundary after
920
- // it. Measured: `Who wrote Romeo and Juliet?` against the trained
921
- // `Who wrote "Romeo and Juliet"?` — two inserted quote characters — scores
922
- // cos 0.377, BELOW unrelated neighbours like "Who wrote the opera
923
- // Carmen??" (0.603). Recall's identity tiers gate on identityBar (0.969
924
- // here) and its reach tiers on 0.875, so no gist-based tier can ever see
925
- // it; only byte-exact alignment can, which is what this function does.
926
- //
927
- // The claim is deliberately strict, in three parts:
928
- //
929
- // • QUERY SIDE — EXACT. Every byte of the query must be a literal
930
- // match against the candidate: covered === query.length, no slack at
931
- // all, not even sub-quantum. The query is what we are answering, so
932
- // an identity claim about it may write off NOTHING. This is stricter
933
- // than the ≤ W edge tolerance the substituted path uses, and it has
934
- // to be: with a one-window allowance, `what is 2^10?` matched the
935
- // trained `what is 2+2?` — "^10" against "+2", four bytes, both sides
936
- // below W — and answered "2+2 is 4.", outweighing cover's authoritative
937
- // ALU result. Below W, byte OVERLAP is chance rather than evidence;
938
- // that never made a below-W DIFFERENCE meaningless, and digits are the
939
- // case that proves it.
940
- // • CANDIDATE SIDE, INTERIOR — each gap must be an EXPLAINED span (see
941
- // explainedSpan): sub-quantum glue, or corpus-global scaffolding.
942
- // This side is asymmetric ON PURPOSE. Material the CANDIDATE has and
943
- // the query omits is not something the asker asked about: if it is
944
- // scaffolding, dropping it changes nothing ("What is *the process of*
945
- // photosynthesis?"); if it is discriminative, the candidate answers a
946
- // DIFFERENT, narrower question ("Is *heavy* water wet?") and must be
947
- // refused. Only the corpus can tell those apart, and it does.
948
- // • CANDIDATE SIDE, SURPLUS — its bytes are the matched runs
949
- // (byte-identical to the query's, hence the same total length) plus
950
- // its own gap spans; anything past that is surplus, and surplus is
951
- // the prefix trap. A prefix-completion candidate fails here by the
952
- // whole length of the completion it wanted to supply — which is why
953
- // admitting scaffolding interiors does not reopen that trap.
954
- //
955
- // Ordered cheapest-first: the two arithmetic tests run before
956
- // explainedSpan, whose per-window `reachOf` climbs are the only costly
957
- // part (shared through the response/conversation reach memo, and reached
958
- // only by a candidate that already survived every structural gate).
959
- if (subs.length === 0) {
960
- if (covered !== query.length)
961
- continue;
962
- const cGap = gaps.reduce((n, g) => n + (g.ce - g.cs), 0);
963
- if (cBytes.length - covered - cGap > W)
964
- continue;
965
- if (!gaps.every((g) => explainedSpan(cBytes, g.cs, g.ce)))
966
- continue;
967
- }
968
- if (diagnostics)
969
- diagnostics.identityValid++;
970
- // KNOWN content may never be dismissed — see dismissedKnownContent
971
- // (the live case: "what is the capital of france" aligning into a
972
- // Matrix synopsis by writing off "ance" — a stored window of the
973
- // trained "France" — as a gap, while genuinely novel spans like
974
- // test/49's untrained "Name" remain tolerable).
975
- if (dismissedKnownQ(spans))
976
- continue;
977
- if (diagnostics)
978
- diagnostics.knownContentValid++;
979
- if (covered > bestAccounted) {
980
- bestAccounted = covered;
981
- best = { id: sid, accounted: spans, subs };
982
- if (diagnostics)
983
- diagnostics.bestRank = candidateIndex;
984
- }
985
- }
986
- if (best !== null) {
987
- ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [
988
- rNode(ctx, best.id, "bridged-context"),
989
- ...best.subs.map((s) => rItem(query.subarray(s.qs, s.qe), "substituted")),
990
- ], `a trained context accounts for the query up to ${best.subs.length} ` +
991
- `corroborated substitution(s) — grounding through its learnt edges`, undefined, diagnostics);
992
- }
993
- else {
994
- ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [], "candidate contexts were proposed, but none passed the bridge's " +
995
- "structural identity and corroboration gates", undefined, diagnostics);
996
- }
997
- return best;
998
- }