@hviana/sema 0.4.2 → 0.4.3

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 +136 -0
  42. package/dist/src/derive/src/deduction.js +159 -0
  43. package/dist/src/derive/src/index.d.ts +8 -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 +175 -0
  56. package/dist/src/geometry.js +823 -0
  57. package/dist/src/index.d.ts +17 -0
  58. package/dist/src/index.js +19 -0
  59. package/dist/src/ingest-cache.d.ts +41 -0
  60. package/dist/src/ingest-cache.js +165 -0
  61. package/dist/src/meter.d.ts +176 -0
  62. package/dist/src/meter.js +274 -0
  63. package/dist/src/mind/articulation.d.ts +6 -0
  64. package/dist/src/mind/articulation.js +99 -0
  65. package/dist/src/mind/attention.d.ts +414 -0
  66. package/dist/src/mind/attention.js +2082 -0
  67. package/dist/src/mind/bridge.d.ts +39 -0
  68. package/dist/src/mind/bridge.js +947 -0
  69. package/dist/src/mind/canonical.d.ts +34 -0
  70. package/dist/src/mind/canonical.js +93 -0
  71. package/dist/src/mind/graph-search.d.ts +294 -0
  72. package/dist/src/mind/graph-search.js +996 -0
  73. package/dist/src/mind/index.d.ts +9 -0
  74. package/dist/src/mind/index.js +5 -0
  75. package/dist/src/mind/junction.d.ts +137 -0
  76. package/dist/src/mind/junction.js +342 -0
  77. package/dist/src/mind/learning.d.ts +75 -0
  78. package/dist/src/mind/learning.js +270 -0
  79. package/dist/src/mind/match.d.ts +181 -0
  80. package/dist/src/mind/match.js +655 -0
  81. package/dist/src/mind/mechanisms/alu.d.ts +4 -0
  82. package/dist/src/mind/mechanisms/alu.js +36 -0
  83. package/dist/src/mind/mechanisms/cast.d.ts +89 -0
  84. package/dist/src/mind/mechanisms/cast.js +784 -0
  85. package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
  86. package/dist/src/mind/mechanisms/confluence.js +255 -0
  87. package/dist/src/mind/mechanisms/cover.d.ts +6 -0
  88. package/dist/src/mind/mechanisms/cover.js +227 -0
  89. package/dist/src/mind/mechanisms/extraction.d.ts +33 -0
  90. package/dist/src/mind/mechanisms/extraction.js +300 -0
  91. package/dist/src/mind/mechanisms/recall.d.ts +16 -0
  92. package/dist/src/mind/mechanisms/recall.js +364 -0
  93. package/dist/src/mind/mind.d.ts +337 -0
  94. package/dist/src/mind/mind.js +617 -0
  95. package/dist/src/mind/pipeline-mechanism.d.ts +172 -0
  96. package/dist/src/mind/pipeline-mechanism.js +465 -0
  97. package/dist/src/mind/pipeline.d.ts +49 -0
  98. package/dist/src/mind/pipeline.js +275 -0
  99. package/dist/src/mind/primitives.d.ts +66 -0
  100. package/dist/src/mind/primitives.js +306 -0
  101. package/dist/src/mind/rationale.d.ts +139 -0
  102. package/dist/src/mind/rationale.js +163 -0
  103. package/dist/src/mind/reasoning.d.ts +40 -0
  104. package/dist/src/mind/reasoning.js +280 -0
  105. package/dist/src/mind/recognition.d.ts +20 -0
  106. package/dist/src/mind/recognition.js +504 -0
  107. package/dist/src/mind/resonance.d.ts +23 -0
  108. package/dist/src/mind/resonance.js +0 -0
  109. package/dist/src/mind/trace.d.ts +15 -0
  110. package/dist/src/mind/trace.js +73 -0
  111. package/dist/src/mind/traverse.d.ts +126 -0
  112. package/dist/src/mind/traverse.js +650 -0
  113. package/dist/src/mind/types.d.ts +333 -0
  114. package/dist/src/mind/types.js +130 -0
  115. package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
  116. package/dist/src/rabitq-ivf/src/database.js +201 -0
  117. package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
  118. package/dist/src/rabitq-ivf/src/index.js +4 -0
  119. package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
  120. package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
  121. package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
  122. package/dist/src/rabitq-ivf/src/prng.js +36 -0
  123. package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
  124. package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
  125. package/dist/src/sema.d.ts +31 -0
  126. package/dist/src/sema.js +63 -0
  127. package/dist/src/store-sqlite.d.ts +184 -0
  128. package/dist/src/store-sqlite.js +942 -0
  129. package/dist/src/store.d.ts +678 -0
  130. package/dist/src/store.js +1703 -0
  131. package/dist/src/vec.d.ts +31 -0
  132. package/dist/src/vec.js +109 -0
  133. package/package.json +1 -1
@@ -0,0 +1,947 @@
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 { 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
+ // CORROBORATION (see the module-level doc) over the precomputed window
333
+ // facts: the query span [qs,qe) attests when every full W-window inside
334
+ // it is a stored flat form and at least one is reused across ≥ 2
335
+ // containers. Spans shorter than W carry no window of their own and can
336
+ // never substitute.
337
+ const attestedQ = (qs, qe) => {
338
+ if (qe - qs < W)
339
+ return false;
340
+ let reused = false;
341
+ for (let o = qs; o + W <= qe; o++) {
342
+ if (!winStored[o])
343
+ return false;
344
+ if (winReused[o])
345
+ reused = true;
346
+ }
347
+ return reused;
348
+ };
349
+ // dismissedKnownContent (see above) over the same precomputed facts.
350
+ const dismissedKnownQ = (spans) => {
351
+ const sorted = [...spans].sort((a, b) => a[0] - b[0]);
352
+ let cursor = 0;
353
+ for (const [s, e] of [...sorted, [query.length, query.length]]) {
354
+ for (let o = cursor; o + W <= s; o++)
355
+ if (winStored[o])
356
+ return true;
357
+ cursor = Math.max(cursor, e);
358
+ }
359
+ return false;
360
+ };
361
+ // ── EXPLAINED SPANS — the scaffolding judgement, corpus-global ──────────
362
+ //
363
+ // The question every gap poses is "may the two forms differ HERE without
364
+ // differing in what they SAY?", and that is the discriminative-vs-
365
+ // scaffolding question AGENTS §2.7 names, over the CORPUS-GLOBAL
366
+ // population. It already has one definition — `dominates(reachOf(...), N)`,
367
+ // the same gate confluence's filler test uses ("scaffolding never binds").
368
+ // Nothing new is derived here; the bar is read, not invented.
369
+ //
370
+ // A span is explained when EITHER
371
+ // • it is sub-quantum (< W) — typographic glue, the tolerance identityBar
372
+ // already prices ("below one river window, byte overlap is chance"); or
373
+ // • every full W-window inside it is COMMON by the store's own climb:
374
+ // the ascent SATURATES (the window sits in more places than √N — the
375
+ // climb's own definition of non-discriminative), or it resolves to a
376
+ // majority of the corpus's contexts. "the process of ", " is the ".
377
+ //
378
+ // THE READING MATTERS, not just the population (AGENTS §2.7). This
379
+ // deliberately does NOT go through `reachOf`, which maps BOTH "saturated"
380
+ // and "reaches nothing" to Infinity. For IDF weighting those are the same
381
+ // thing (no usable identity evidence); for THIS question they are
382
+ // opposites — a window reaching nothing is novel content, the most
383
+ // discriminative material there is, and reading it as Infinity would call
384
+ // it scaffolding. Measured: with `reachOf`, "Is water wet?" was answered
385
+ // with "No, heavy water is not wet." — "heav"/"eavy" occur once, reach no
386
+ // edge-bearing ancestor, and were written off as filler. So an
387
+ // empty-rooted window is NEVER explained, and neither is an untrained one
388
+ // (the same principle attestedQ applies to the query side).
389
+ const reachMemo = sharedReachMemo(ctx);
390
+ const explainedSpan = (bytes, from, to) => {
391
+ if (to - from < W)
392
+ return true;
393
+ const common = (start, end) => {
394
+ if (end - start < W)
395
+ return false;
396
+ for (let o = start; o + W <= end; o++) {
397
+ const ids = leafIdRun(ctx, bytes, o, o + W);
398
+ if (ids === null)
399
+ return false;
400
+ const wid = ctx.store.findBranch(ids);
401
+ if (wid === null)
402
+ return false;
403
+ const r = edgeAncestors(ctx, wid, N, reachMemo);
404
+ if (r.saturated)
405
+ continue; // in too many places to discriminate
406
+ if (r.roots.length === 0)
407
+ return false; // reaches nothing: novel content
408
+ if (!dominates(r.contextsReached, N))
409
+ return false;
410
+ }
411
+ return true;
412
+ };
413
+ if (common(from, to))
414
+ return true;
415
+ // Alignment may attach the shared delimiter to either side of an inserted
416
+ // phrase. Up to W-1 boundary bytes are below the fold's identity scale;
417
+ // classify the phrase by a full-window interior core when one exists.
418
+ // This does not erase a short discriminative insertion: "heavy" still
419
+ // leaves the full `heav`/`eavy` windows for the corpus-global test.
420
+ for (let left = 0; left < W; left++) {
421
+ for (let right = 0; right < W; right++) {
422
+ if (left + right === 0 || left + right >= W)
423
+ continue;
424
+ if (common(from + left, to - right))
425
+ return true;
426
+ }
427
+ }
428
+ return false;
429
+ };
430
+ anchors.sort((a, b) => a.rarity - b.rarity);
431
+ // Up to W anchors, at least one window apart — the quantum's own count.
432
+ const picked = [];
433
+ for (const a of anchors) {
434
+ if (picked.length >= W)
435
+ break;
436
+ if (picked.some((p) => Math.abs(p.off - a.off) < W))
437
+ continue;
438
+ picked.push(a);
439
+ }
440
+ if (diagnostics)
441
+ diagnostics.picked = picked.length;
442
+ // 2. Candidate trained contexts. Two proposal channels, one verifier:
443
+ // (a) the caller's PROPOSED hits — recall's whole-query resonance
444
+ // ranking, the retrieval structure built to surface near-paraphrase
445
+ // forms the window climb cannot single out at corpus scale; (b) each
446
+ // picked anchor climbed to its edge-bearing ancestors (the same climb
447
+ // consensus voting uses). Both are only ever PROPOSALS — every
448
+ // candidate passes the same byte-exact alignment and gates below.
449
+ const seen = new Set();
450
+ const candidates = [];
451
+ // Exact co-occurrence proposes contexts the whole-form ANN can miss when a
452
+ // short insertion shifts every later fold boundary. The byte alignment below
453
+ // remains the decider. All pairs share one candidateCap·W junction
454
+ // allowance, ordered
455
+ // by their rarest side and then span: a rare content window joined to a
456
+ // distant frame boundary discriminates a whole question better than two
457
+ // neighbouring rare windows inside the same word.
458
+ if (query.length <= 2 * reachCap) {
459
+ const pairs = [];
460
+ for (let i = 0; i < picked.length; i++) {
461
+ for (let j = i + 1; j < picked.length; j++) {
462
+ pairs.push([picked[i], picked[j]]);
463
+ }
464
+ }
465
+ pairs.sort((a, b) => Math.min(a[0].rarity, a[1].rarity) -
466
+ Math.min(b[0].rarity, b[1].rarity) ||
467
+ Math.abs(b[0].off - b[1].off) - Math.abs(a[0].off - a[1].off) ||
468
+ a[0].rarity + a[1].rarity - b[0].rarity - b[1].rarity);
469
+ const structuralBudget = {
470
+ n: chainReach(W) * W * ctx.cfg.recallQueryK,
471
+ };
472
+ for (const [left, right] of pairs.slice(0, W)) {
473
+ 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);
474
+ for (const hit of found) {
475
+ if (candidates.length >= candidateCap)
476
+ break;
477
+ if (seen.has(hit.id) || !ctx.store.hasNext(hit.id))
478
+ continue;
479
+ seen.add(hit.id);
480
+ candidates.push(hit.id);
481
+ if (diagnostics)
482
+ diagnostics.structuralProposed++;
483
+ }
484
+ }
485
+ }
486
+ // Once exact structural proposals fill the shared cap, no caller proposal
487
+ // can enter the verifier. Do not evaluate the lazy ANN thunk merely to
488
+ // discard every result at the loop's first guard.
489
+ const proposedIds = candidates.length < candidateCap ? await proposed() : [];
490
+ if (diagnostics)
491
+ diagnostics.proposed = proposedIds.length;
492
+ for (const sid of proposedIds) {
493
+ if (candidates.length >= candidateCap)
494
+ break;
495
+ if (seen.has(sid))
496
+ continue;
497
+ seen.add(sid);
498
+ const tb = candidateBytes(sid);
499
+ if (tb === null)
500
+ continue;
501
+ if (!picked.some((a) => indexOf(tb, query.subarray(a.off, a.off + W), 0) >= 0))
502
+ continue;
503
+ let use = sid;
504
+ if (!ctx.store.hasNext(use)) {
505
+ const folded = foldTree(ctx, perceive(ctx, tb), 0).node;
506
+ if (folded === null || folded === sid || !ctx.store.hasNext(folded)) {
507
+ continue;
508
+ }
509
+ use = folded;
510
+ if (seen.has(use))
511
+ continue;
512
+ seen.add(use);
513
+ }
514
+ candidates.push(use);
515
+ if (diagnostics)
516
+ diagnostics.proposedGrounded++;
517
+ }
518
+ // Proposal channel — carries its caller's own bound (recall's resonance
519
+ // k), sharing the 2·recallQueryK candidate allowance
520
+ // with the structural and climb channels. A proposal may
521
+ // be a FLAT content twin whose continuation edge lives on the
522
+ // fold-shaped deposit node with the same bytes — the same twin split
523
+ // canonResolve bridges by re-folding (primitives.ts) — but the re-fold
524
+ // (a full perceive of the candidate's bytes) is paid only for proposals
525
+ // that could align at all: alignment can only seed at a picked anchor
526
+ // window occurring literally in the candidate (measured: unconditional
527
+ // re-folds multiplied the refusal-path latency several-fold).
528
+ // FIRST TOUCH of the caller's proposals — past every gate that could have
529
+ // refused without them (see substitutionBridge's doc).
530
+ // Climb channel — edge-bearing ancestors only, decided by the indexed
531
+ // O(1) hasNext; no byte is read here (the climb visits hundreds of
532
+ // roots, and reading each was measured to dominate the refusal path).
533
+ for (const a of picked) {
534
+ const reach = edgeAncestors(ctx, a.id, N, reachMemo);
535
+ for (const sid of reach.roots) {
536
+ if (candidates.length >= candidateCap)
537
+ break;
538
+ if (seen.has(sid))
539
+ continue;
540
+ seen.add(sid);
541
+ if (!ctx.store.hasNext(sid))
542
+ continue;
543
+ candidates.push(sid);
544
+ if (diagnostics)
545
+ diagnostics.climbed++;
546
+ }
547
+ if (candidates.length >= candidateCap)
548
+ break;
549
+ }
550
+ // 3. Align each candidate; gate its mismatches; keep the best.
551
+ // Over-cap candidates are dropped here rather than earlier: the climb
552
+ // channel deliberately reads no bytes while collecting (the climb visits
553
+ // hundreds of roots), so this is where its proposals are first sized.
554
+ //
555
+ // Candidate bytes are read LAZILY — on first access during the seed
556
+ // check — not eagerly for every collected id. On a 325K-context store
557
+ // the climb channel alone can propose hundreds of edge-bearing ancestors
558
+ // (hubBound = 571), most of which will never contain a picked anchor
559
+ // window and would be discarded at the seed check without their bytes
560
+ // ever being consulted. Eager reads for 500+ candidates each traversing
561
+ // the DAG (profiled at 12K node records and 73KB of bytes read per
562
+ // refusing query) is the dominant remaining bridge cost after the ANN
563
+ // gate. A Map stays available for the frame-unanimity scan below, which
564
+ // only needs bytes of candidates that actually seeded.
565
+ const seededBytes = new Map();
566
+ /** Read a candidate's bytes once; cache for the seed check AND for the
567
+ * frame-unanimity scan that follows alignment. Returns null when the
568
+ * candidate exceeds the phrase-scale cap or has no content. */
569
+ const bytesOfCandidate = (sid) => {
570
+ const hit = seededBytes.get(sid);
571
+ if (hit !== undefined)
572
+ return hit;
573
+ const b = candidateBytes(sid);
574
+ if (b !== null)
575
+ seededBytes.set(sid, b);
576
+ return b;
577
+ };
578
+ if (diagnostics)
579
+ diagnostics.phraseScale = seededBytes.size;
580
+ // FRAME UNANIMITY: a substitution U → C inside the frame (Lf, Rf) is
581
+ // groundable only when the collected candidates — the store's own sample
582
+ // of contexts sharing the query's content — are unanimous about the
583
+ // filler: every occurrence of Lf…Rf across them holds either U (the
584
+ // query's own word, corroboration) or C. A THIRD distinct filler means
585
+ // the frame is a VALUE SLOT ("was born in _" held Germany, Poland,
586
+ // England, Serbia — observed live), and picking one value would assert
587
+ // knowledge the store does not have. Consensus of the store's own
588
+ // instances, no similarity judgement, no tuned constant.
589
+ // Requires a genuine CORROBORATING sighting, not merely the absence of a
590
+ // conflicting one: scanning only the handful of resonance/climb-proposed
591
+ // candidates means the frame can easily occur NOWHERE else among them
592
+ // (observed live: "of Fra[nce]" -> "of Spain si[nce]" passed vacuously —
593
+ // the frame "tal …nce " never recurred among the collected candidates at
594
+ // all, so there was no consensus, only an absence of disagreement, yet
595
+ // the substitution was accepted). "Unanimous" must mean the store's own
596
+ // instances agree, which requires at least one instance to consult.
597
+ const unanimous = (u, c, lf, rf) => {
598
+ for (const bytes of seededBytes.values()) {
599
+ let from = 0;
600
+ for (;;) {
601
+ const i = indexOf(bytes, lf, from);
602
+ if (i < 0)
603
+ break;
604
+ from = i + 1;
605
+ const start = i + lf.length;
606
+ const j = indexOf(bytes.subarray(start, start + reachCap + rf.length), rf, 0);
607
+ if (j < 0)
608
+ continue;
609
+ const filler = bytes.subarray(start, start + j);
610
+ if (filler.length === 0)
611
+ continue;
612
+ if (!bytesEqual(filler, u) && !bytesEqual(filler, c))
613
+ return false;
614
+ }
615
+ }
616
+ return true;
617
+ };
618
+ // (A candidate need NOT contain the query's rarest window literally: the
619
+ // rarest window may sit INSIDE the very word being substituted (observed
620
+ // live: "chemical symbol for water" whose rarest window "l sy" spans
621
+ // "symbol" — the trained formula-question can never contain it). A
622
+ // candidate that instead dodges the query's known content by writing it
623
+ // off as gaps is refused by dismissedKnownContent below, which subsumes
624
+ // the old rarest-window containment gate: rare windows inside an accepted
625
+ // substitution are accounted for; rare windows outside the accepted spans
626
+ // force refusal (the Matrix-synopsis junk stays dead by exactly that
627
+ // check — verified live).
628
+ let best = null;
629
+ let bestAccounted = 0;
630
+ const queryHaloMemo = new Map();
631
+ const candidateHaloMemo = new Map();
632
+ for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
633
+ const sid = candidates[candidateIndex];
634
+ // Read bytes lazily — most climb-proposed candidates have no picked
635
+ // anchor window and will never pass the seed check below, so their
636
+ // bytes are never read at all.
637
+ const cBytes = bytesOfCandidate(sid);
638
+ if (cBytes === null)
639
+ continue;
640
+ // Seed at the rarest picked anchor that literally occurs in this
641
+ // candidate.
642
+ let seed = null;
643
+ for (const a of picked) {
644
+ const co = indexOf(cBytes, query.subarray(a.off, a.off + W), 0);
645
+ if (co >= 0) {
646
+ seed = { qo: a.off, co };
647
+ break;
648
+ }
649
+ }
650
+ if (seed === null)
651
+ continue;
652
+ if (diagnostics)
653
+ diagnostics.seeded++;
654
+ const { matched, gaps } = align(ctx, query, cBytes, seed.qo, seed.co);
655
+ if (diagnostics)
656
+ diagnostics.aligned++;
657
+ // Investment gate: even treating every two-sided mismatch as a valid
658
+ // synonym, can this alignment satisfy the bridge's final coverage rule?
659
+ // Distributional span composition performs bounded ancestor climbs; never
660
+ // pay for it on a candidate arithmetic already proves cannot win.
661
+ let matchStart = query.length;
662
+ let matchEnd = 0;
663
+ let potential = 0;
664
+ for (const [s, e] of matched) {
665
+ matchStart = Math.min(matchStart, s);
666
+ matchEnd = Math.max(matchEnd, e);
667
+ potential += e - s;
668
+ }
669
+ for (const g of gaps) {
670
+ if (g.qe > g.qs && g.ce > g.cs)
671
+ potential += g.qe - g.qs;
672
+ }
673
+ if (matchStart > W || query.length - matchEnd > W ||
674
+ !dominates(potential, query.length))
675
+ continue;
676
+ // Gate each mismatch: a corroborated, geometrically-identified
677
+ // substitution counts as accounted; anything else stays a gap.
678
+ //
679
+ // A raw mismatch is the MINIMAL byte diff ("big" ↔ "lar" inside
680
+ // biggest/largest), usually below the scale at which either side is a
681
+ // corpus unit. The true unit is found by EXPANSION: absorb flanking
682
+ // bytes from the adjacent matched runs (equal on both sides by
683
+ // construction, so both spans grow identically) until the query side
684
+ // attests and the pair clears the concept bar — smallest expansion
685
+ // first, capped at chainReach like the mismatch itself. Absorbed
686
+ // bytes were already matched, so coverage is unchanged.
687
+ const subs = [];
688
+ let ok = true;
689
+ for (const g of gaps) {
690
+ const uLen = g.qe - g.qs, cLen = g.ce - g.cs;
691
+ if (uLen === 0 || cLen === 0 || uLen > reachCap || cLen > reachCap) {
692
+ // Pure insertion/deletion or over-long mismatch. Query-side: one
693
+ // perception window (the identityBar tolerance). Candidate-side:
694
+ // one chain reach (W², the two-level composite bound) — a genuine
695
+ // paraphrase inserts inflection-scale material ("does water boil"
696
+ // ↔ "should water be boiled"), while a divergent candidate jumps
697
+ // hundreds of bytes between the query's frames.
698
+ if (uLen > W || cLen > reachCap)
699
+ ok = false;
700
+ continue;
701
+ }
702
+ // RAW BALANCE gate (closes the OPEN GAP above): the two sides of a
703
+ // genuine lexical substitution swap comparable amounts of content —
704
+ // "big"/"lar" (3/3 bytes, before expansion absorbs the shared "gest"
705
+ // suffix to reach an attestable "biggest"/"largest"). A candidate
706
+ // whose two sentences simply diverge into unrelated continuations
707
+ // produces a LOPSIDED raw mismatch instead — the live wrong answer's
708
+ // raw gap was "Fra"/"Spai" widened to (3,8) by the align sweep
709
+ // finding a coincidental "nce " match years later in "since" — 3
710
+ // bytes of query content standing in for 8 bytes of candidate
711
+ // content is not a word swap, it is two different sentences that
712
+ // happen to share a few letters. Uses the SAME dominates() bar
713
+ // (part*2 > whole) applied throughout the codebase, symmetrically:
714
+ // the smaller raw side must be more than half the larger. Applies
715
+ // to the RAW gap for GEOMETRIC identity, before expansion — expansion
716
+ // only ever grows both sides by IDENTICAL absorbed bytes, so it cannot
717
+ // fix an imbalance that was already there. Distributional synonym
718
+ // evidence is exempt: two phrases may occupy the same role at very
719
+ // different lengths.
720
+ let accepted = false;
721
+ const balanced = dominates(Math.min(uLen, cLen), Math.max(uLen, cLen));
722
+ const maxExtra = reachCap - Math.max(uLen, cLen);
723
+ outer: for (let extra = 0; extra <= maxExtra; extra++) {
724
+ for (let a = 0; a <= extra; a++) {
725
+ const b = extra - a;
726
+ const qs2 = g.qs - a, qe2 = g.qe + b;
727
+ const cs2 = g.cs - a, ce2 = g.ce + b;
728
+ if (qs2 < 0 || qe2 > query.length)
729
+ continue;
730
+ if (cs2 < 0 || ce2 > cBytes.length)
731
+ continue;
732
+ // INTERIOR gate: a substitution must sit INSIDE matched
733
+ // structure — at least one full window of matched bytes must
734
+ // remain adjacent on BOTH sides after absorption. Every junk
735
+ // substitution observed live sat at the query's edge, with only
736
+ // terminal punctuation beyond it ("…born in [England].",
737
+ // "…capital of [Zamunda]?"): an edge mismatch is the query
738
+ // trailing off into different content, not a word standing in a
739
+ // shared frame.
740
+ const leftOk = matched.some(([s, e]) => e >= qs2 && qs2 - s >= W);
741
+ const rightOk = matched.some(([s, e]) => s <= qe2 && e - qe2 >= W);
742
+ if (!leftOk || !rightOk)
743
+ continue;
744
+ if (!attestedQ(qs2, qe2))
745
+ continue;
746
+ const u = query.subarray(qs2, qe2);
747
+ const cSpan = cBytes.subarray(cs2, ce2);
748
+ const geometric = cosine(perceive(ctx, u).v, perceive(ctx, cSpan).v);
749
+ const qKey = `${qs2}:${qe2}`;
750
+ let qHalo = queryHaloMemo.get(qKey);
751
+ if (qHalo === undefined) {
752
+ qHalo = spanHalo(ctx, query, qs2, qe2);
753
+ queryHaloMemo.set(qKey, qHalo);
754
+ }
755
+ const cKey = `${sid}:${cs2}:${ce2}`;
756
+ let cHalo = candidateHaloMemo.get(cKey);
757
+ if (cHalo === undefined) {
758
+ cHalo = spanHalo(ctx, cBytes, cs2, ce2);
759
+ candidateHaloMemo.set(cKey, cHalo);
760
+ }
761
+ const distributional = qHalo !== null && cHalo !== null
762
+ ? cosine(qHalo, cHalo)
763
+ : 0;
764
+ if (diagnostics) {
765
+ diagnostics.synonymChecks++;
766
+ diagnostics.bestSynonym = Math.max(diagnostics.bestSynonym, distributional);
767
+ }
768
+ // Graded identity: byte geometry remains the cheap first tier;
769
+ // VSA company is the synonym tier when differently-spelled forms
770
+ // occupy the same learnt distributional role.
771
+ if ((!balanced || geometric < bar) &&
772
+ distributional < synonymBar) {
773
+ continue;
774
+ }
775
+ if (!unanimous(u, cSpan, query.subarray(qs2 - W, qs2), query.subarray(qe2, qe2 + W)))
776
+ continue;
777
+ subs.push({ qs: qs2, qe: qe2, cs: cs2, ce: ce2 });
778
+ accepted = true;
779
+ break outer;
780
+ }
781
+ }
782
+ if (!accepted && (uLen > W || cLen > reachCap))
783
+ ok = false;
784
+ }
785
+ // A candidate with ZERO gaps needs no substitution and might seem like
786
+ // the strongest possible bridge, but accepting it here is a trap: this
787
+ // mechanism runs only where recall's own resonance/echo tiers already
788
+ // declined to ground a same-shape, zero-substitution match — usually
789
+ // because the query is a strict byte-PREFIX of several candidates
790
+ // (many trained "The capital of X is Y." facts share the query "The
791
+ // capital of France is" as a substring once the true France fact is
792
+ // filtered out for lacking a continuation edge) and nothing here
793
+ // corroborates picking one candidate's completion over another's
794
+ // (observed live: prefix-completion bridged to an unrelated "London"
795
+ // trivia distractor over the true France fact, which precedes it in
796
+ // resonance rank but has no outgoing edge to bridge through). This
797
+ // mechanism exists to explain SUBSTITUTIONS; a query needing none is
798
+ // recall's job, not the bridge's.
799
+ if (!ok)
800
+ continue;
801
+ if (diagnostics)
802
+ diagnostics.structurallyValid++;
803
+ // Coverage: matched runs plus accepted substitutions must dominate the
804
+ // query, every interior gap already proved ≤ W above, and the EDGES
805
+ // must be explained to the same one-window tolerance — the same "at
806
+ // most one river window of foreign content" identityBar prices. The
807
+ // live junk this closes: alignments that matched a query's scaffolding
808
+ // and one substitution but left the query's whole trailing content
809
+ // ("…planet orbiting our sun.", 24 bytes) unexplained, yet still
810
+ // half-dominated the byte count.
811
+ const spans = [
812
+ ...matched,
813
+ ...subs.map((s) => [s.qs, s.qe]),
814
+ ].sort((x, y) => x[0] - y[0]);
815
+ let covered = 0;
816
+ let reachEnd = 0;
817
+ for (const [s, e] of spans) {
818
+ if (e <= reachEnd)
819
+ continue;
820
+ covered += e - Math.max(s, reachEnd);
821
+ reachEnd = Math.max(reachEnd, e);
822
+ }
823
+ if (diagnostics) {
824
+ diagnostics.bestCovered = Math.max(diagnostics.bestCovered, covered);
825
+ const candidateGapBytes = gaps.reduce((n, g) => n + g.ce - g.cs, 0);
826
+ diagnostics.closest.push({
827
+ id: sid,
828
+ covered,
829
+ leading: spans[0][0],
830
+ trailing: query.length - reachEnd,
831
+ gaps: gaps.length,
832
+ substitutions: subs.length,
833
+ queryGapBytes: gaps.reduce((n, g) => n + g.qe - g.qs, 0),
834
+ candidateGapBytes,
835
+ gapRanges: gaps.map((g) => [g.qs, g.qe, g.cs, g.ce]),
836
+ candidateSurplus: cBytes.length - covered - candidateGapBytes,
837
+ gapsExplained: gaps.every((g) => explainedSpan(cBytes, g.cs, g.ce)),
838
+ });
839
+ diagnostics.closest.sort((a, b) => b.covered - a.covered ||
840
+ a.leading + a.trailing - b.leading - b.trailing ||
841
+ a.id - b.id);
842
+ if (diagnostics.closest.length > W)
843
+ diagnostics.closest.length = W;
844
+ }
845
+ if (spans[0][0] > W || query.length - reachEnd > W)
846
+ continue;
847
+ if (!dominates(covered, query.length))
848
+ continue;
849
+ if (diagnostics)
850
+ diagnostics.coverageValid++;
851
+ // ZERO-SUBSTITUTION ADMISSION — an IDENTITY claim, not a substitution.
852
+ //
853
+ // A candidate needing no substitution is normally refused (see the trap
854
+ // above), and that refusal is right for the case it was written for: the
855
+ // query is a strict byte-PREFIX of several candidates, each of which
856
+ // continues differently, and nothing here corroborates picking one
857
+ // continuation over another. But that trap has a signature — the
858
+ // candidate carries substantial content BEYOND the alignment, and that
859
+ // surplus is exactly the "answer" the bridge would be inventing.
860
+ //
861
+ // The opposite shape is not ambiguous at all: the alignment explains BOTH
862
+ // strings end to end, and the only thing between them is sub-quantum glue
863
+ // — typographic punctuation the fold treats as structure. Then the two
864
+ // are the SAME learnt form, and grounding through its edge returns that
865
+ // form's own trained answer, never a chosen-among-many completion.
866
+ //
867
+ // Why the ladder cannot reach these otherwise: the gist is a STRUCTURAL
868
+ // signature, so a mid-string insertion shifts every fold boundary after
869
+ // it. Measured: `Who wrote Romeo and Juliet?` against the trained
870
+ // `Who wrote "Romeo and Juliet"?` — two inserted quote characters — scores
871
+ // cos 0.377, BELOW unrelated neighbours like "Who wrote the opera
872
+ // Carmen??" (0.603). Recall's identity tiers gate on identityBar (0.969
873
+ // here) and its reach tiers on 0.875, so no gist-based tier can ever see
874
+ // it; only byte-exact alignment can, which is what this function does.
875
+ //
876
+ // The claim is deliberately strict, in three parts:
877
+ //
878
+ // • QUERY SIDE — EXACT. Every byte of the query must be a literal
879
+ // match against the candidate: covered === query.length, no slack at
880
+ // all, not even sub-quantum. The query is what we are answering, so
881
+ // an identity claim about it may write off NOTHING. This is stricter
882
+ // than the ≤ W edge tolerance the substituted path uses, and it has
883
+ // to be: with a one-window allowance, `what is 2^10?` matched the
884
+ // trained `what is 2+2?` — "^10" against "+2", four bytes, both sides
885
+ // below W — and answered "2+2 is 4.", outweighing cover's authoritative
886
+ // ALU result. Below W, byte OVERLAP is chance rather than evidence;
887
+ // that never made a below-W DIFFERENCE meaningless, and digits are the
888
+ // case that proves it.
889
+ // • CANDIDATE SIDE, INTERIOR — each gap must be an EXPLAINED span (see
890
+ // explainedSpan): sub-quantum glue, or corpus-global scaffolding.
891
+ // This side is asymmetric ON PURPOSE. Material the CANDIDATE has and
892
+ // the query omits is not something the asker asked about: if it is
893
+ // scaffolding, dropping it changes nothing ("What is *the process of*
894
+ // photosynthesis?"); if it is discriminative, the candidate answers a
895
+ // DIFFERENT, narrower question ("Is *heavy* water wet?") and must be
896
+ // refused. Only the corpus can tell those apart, and it does.
897
+ // • CANDIDATE SIDE, SURPLUS — its bytes are the matched runs
898
+ // (byte-identical to the query's, hence the same total length) plus
899
+ // its own gap spans; anything past that is surplus, and surplus is
900
+ // the prefix trap. A prefix-completion candidate fails here by the
901
+ // whole length of the completion it wanted to supply — which is why
902
+ // admitting scaffolding interiors does not reopen that trap.
903
+ //
904
+ // Ordered cheapest-first: the two arithmetic tests run before
905
+ // explainedSpan, whose per-window `reachOf` climbs are the only costly
906
+ // part (shared through the response/conversation reach memo, and reached
907
+ // only by a candidate that already survived every structural gate).
908
+ if (subs.length === 0) {
909
+ if (covered !== query.length)
910
+ continue;
911
+ const cGap = gaps.reduce((n, g) => n + (g.ce - g.cs), 0);
912
+ if (cBytes.length - covered - cGap > W)
913
+ continue;
914
+ if (!gaps.every((g) => explainedSpan(cBytes, g.cs, g.ce)))
915
+ continue;
916
+ }
917
+ if (diagnostics)
918
+ diagnostics.identityValid++;
919
+ // KNOWN content may never be dismissed — see dismissedKnownContent
920
+ // (the live case: "what is the capital of france" aligning into a
921
+ // Matrix synopsis by writing off "ance" — a stored window of the
922
+ // trained "France" — as a gap, while genuinely novel spans like
923
+ // test/49's untrained "Name" remain tolerable).
924
+ if (dismissedKnownQ(spans))
925
+ continue;
926
+ if (diagnostics)
927
+ diagnostics.knownContentValid++;
928
+ if (covered > bestAccounted) {
929
+ bestAccounted = covered;
930
+ best = { id: sid, accounted: spans, subs };
931
+ if (diagnostics)
932
+ diagnostics.bestRank = candidateIndex;
933
+ }
934
+ }
935
+ if (best !== null) {
936
+ ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [
937
+ rNode(ctx, best.id, "bridged-context"),
938
+ ...best.subs.map((s) => rItem(query.subarray(s.qs, s.qe), "substituted")),
939
+ ], `a trained context accounts for the query up to ${best.subs.length} ` +
940
+ `corroborated substitution(s) — grounding through its learnt edges`, undefined, diagnostics);
941
+ }
942
+ else {
943
+ ctx.trace?.step("substitutionBridge", [rItem(query, "query")], [], "candidate contexts were proposed, but none passed the bridge's " +
944
+ "structural identity and corroboration gates", undefined, diagnostics);
945
+ }
946
+ return best;
947
+ }