@hviana/sema 0.4.7 → 0.5.0

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 (63) hide show
  1. package/AGENTS.md +290 -77
  2. package/HOW_IT_WORKS.md +2170 -735
  3. package/dist/example/train_base.d.ts +9 -3
  4. package/dist/example/train_base.js +21 -4
  5. package/dist/src/canon.d.ts +19 -0
  6. package/dist/src/canon.js +28 -0
  7. package/dist/src/geometry.d.ts +52 -0
  8. package/dist/src/geometry.js +87 -1
  9. package/dist/src/mind/bridge.js +27 -1
  10. package/dist/src/mind/frame-filler.d.ts +15 -0
  11. package/dist/src/mind/frame-filler.js +535 -0
  12. package/dist/src/mind/learning.js +6 -11
  13. package/dist/src/mind/mechanisms/cast.js +72 -2
  14. package/dist/src/mind/mechanisms/cover.js +6 -1
  15. package/dist/src/mind/mechanisms/extraction.js +27 -0
  16. package/dist/src/mind/mechanisms/recall.js +214 -34
  17. package/dist/src/mind/mind.d.ts +49 -1
  18. package/dist/src/mind/mind.js +137 -10
  19. package/dist/src/mind/pipeline-mechanism.d.ts +7 -0
  20. package/dist/src/mind/pipeline.js +29 -1
  21. package/dist/src/mind/prefix-completion.d.ts +59 -0
  22. package/dist/src/mind/prefix-completion.js +270 -0
  23. package/dist/src/mind/primitives.d.ts +29 -10
  24. package/dist/src/mind/primitives.js +52 -61
  25. package/dist/src/mind/recognition.js +119 -9
  26. package/dist/src/mind/traverse.d.ts +32 -0
  27. package/dist/src/mind/traverse.js +52 -0
  28. package/dist/src/mind/types.d.ts +55 -16
  29. package/dist/src/mind/types.js +68 -19
  30. package/dist/src/store.d.ts +21 -0
  31. package/dist/src/store.js +21 -0
  32. package/example/train_base.ts +21 -4
  33. package/package.json +1 -1
  34. package/src/canon.ts +28 -0
  35. package/src/geometry.ts +100 -1
  36. package/src/mind/bridge.ts +34 -0
  37. package/src/mind/frame-filler.ts +604 -0
  38. package/src/mind/learning.ts +5 -9
  39. package/src/mind/mechanisms/cast.ts +70 -2
  40. package/src/mind/mechanisms/cover.ts +6 -1
  41. package/src/mind/mechanisms/extraction.ts +27 -0
  42. package/src/mind/mechanisms/recall.ts +236 -37
  43. package/src/mind/mind.ts +154 -14
  44. package/src/mind/pipeline-mechanism.ts +7 -0
  45. package/src/mind/pipeline.ts +33 -1
  46. package/src/mind/prefix-completion.ts +314 -0
  47. package/src/mind/primitives.ts +59 -70
  48. package/src/mind/recognition.ts +117 -6
  49. package/src/mind/traverse.ts +52 -0
  50. package/src/mind/types.ts +98 -42
  51. package/src/store.ts +25 -0
  52. package/test/13-conversation.test.mjs +13 -0
  53. package/test/57-fusion-order.test.mjs +65 -0
  54. package/test/66-query-edge-whitespace.test.mjs +99 -0
  55. package/test/67-climb-anchor-breadth.test.mjs +113 -0
  56. package/test/68-extraction-unanchored.test.mjs +79 -0
  57. package/test/69-frame-filler.test.mjs +115 -0
  58. package/test/70-prefix-completion.test.mjs +170 -0
  59. package/test/71-embedded-canon-equivalence.test.mjs +121 -0
  60. package/test/72-prefix-candidate-supply.test.mjs +114 -0
  61. package/test/73-scaffolding-only-bridge-abstains.test.mjs +178 -0
  62. package/test/74-prefix-trap-not-sprung-early.test.mjs +114 -0
  63. package/test/75-multiturn-context-optimisation.test.mjs +1082 -0
@@ -1182,12 +1182,80 @@ export async function counterfactualTransfer(
1182
1182
  // [...] context will be the seat") — its own bytes ARE that seat
1183
1183
  // directly, with no predecessor to even check (it was found by a
1184
1184
  // forward edge, not matched in the query).
1185
- const b = seats !== undefined
1185
+ let b = seats !== undefined
1186
1186
  ? seats[1]
1187
1187
  : bestAnalog.point !== null
1188
1188
  ? await seatOf(bestAnalog.point, false)
1189
1189
  : read(ctx, bestAnalog.anchor);
1190
- const answer = await joinWithBridge(ctx, a, b);
1190
+ // AN ECHO IS NOT A VOICE. `allowForward: false` above leaves seatOfNode
1191
+ // with one last resort — the point's OWN BYTES — and when the aligned
1192
+ // anchor is a QUESTION node those bytes are the question itself. The
1193
+ // comparison then hands the asker their own words back: "What is the
1194
+ // capital of France? And what is the largest planet?" answered "The
1195
+ // capital of France is Paris.What is the largest planet?", one topic
1196
+ // answered and the other merely repeated. (The same corpus answered BOTH
1197
+ // when asked in the opposite order — the echo was never about the topic,
1198
+ // only about whether the climb happened to land on the question node or
1199
+ // the answer node.)
1200
+ //
1201
+ // The fix is NOT to allow the forward edge for every directly aligned
1202
+ // analog. "Directly aligned" does not mean "the query named it": a point
1203
+ // can be aligned by HALO similarity with no literal overlap at all, and
1204
+ // test/43 pins exactly that case — an analog whose own bytes are already a
1205
+ // complete Q+A unit, cited structurally, whose forward edge is an
1206
+ // unrelated next quiz question. There, stopping at its own bytes is
1207
+ // right, because those bytes are an answer and nothing was echoed.
1208
+ //
1209
+ // What separates the two is the RESTATEMENT, which is directly testable:
1210
+ // a seat whose bytes already occur in the query says nothing the asker did
1211
+ // not just say, so it cannot be this analog's contribution — and only then
1212
+ // is the continuation the query literally asked for worth following. Same
1213
+ // `restatesQuery` primitive the substitution schema above already gates
1214
+ // its own forward step on; no new constant and no new notion of "named".
1215
+ // Read the restatement UNDER THE RESPONSE'S OWN EQUIVALENCE. Byte-exact
1216
+ // containment misses the case that actually occurs: the trained node is
1217
+ // "What is the largest planet?" while the query asks "And what is the
1218
+ // largest planet?" — the same words, one capital letter apart, so
1219
+ // `indexOf` finds nothing and the echo sails through. `ctx.canon` is the
1220
+ // response's injected notion of "the same text" (case, width, whitespace);
1221
+ // consulting it here is the same fallback `resolve` already makes when an
1222
+ // exact content lookup misses, and it keeps this mechanism from carrying
1223
+ // any idea of its own about what a character is.
1224
+ const echoesQuery = (x: Uint8Array): boolean => {
1225
+ if (restatesQuery(query, x)) return true;
1226
+ const canon = ctx.canon;
1227
+ if (canon === null) return false;
1228
+ const cq = canon(query), cx = canon(x);
1229
+ return cx.length < cq.length && indexOf(cq, cx, 0) >= 0;
1230
+ };
1231
+ if (echoesQuery(b)) {
1232
+ const fwd = await follow(ctx, bestAnalog.anchor, qv);
1233
+ if (fwd !== null && fwd.length > 0 && !echoesQuery(fwd)) b = fwd;
1234
+ }
1235
+ // VOICED IN THE ORDER THE QUERY POSED THEM. `a` is the DOMINANT point
1236
+ // and `b` the analog, which is a ranking by consensus strength — not by
1237
+ // where either was asked about. Reading the pair out in that ranking
1238
+ // makes a two-topic answer's order depend on which topic resonated
1239
+ // harder, so the same two questions asked in the opposite order produce
1240
+ // the same sentence: measured on test/57, "What is the largest planet?
1241
+ // And what is the capital of France?" answered "The capital of France is
1242
+ // Paris.The largest planet is Jupiter." — both halves right, the order
1243
+ // backwards, because France was the dominant point (accounted [[33,62],
1244
+ // [0,27]] — the runs are literally in reverse query order).
1245
+ //
1246
+ // This is the SAME rule fuseAttention already applies one layer up ("a
1247
+ // multi-topic answer should read in the order the question posed its
1248
+ // topics"), applied to the pair a single comparison voices itself. Each
1249
+ // point's position is the earliest query byte its own aligned runs stand
1250
+ // on — the same runs `cmpAccounted` prices the schema by, so order and
1251
+ // cost read one source.
1252
+ const earliest = (p: Point): number =>
1253
+ runSpans(p).reduce((m, [s]) => Math.min(m, s), Infinity);
1254
+ const analogPoint = bestAnalog.point ?? bestAnalog.src;
1255
+ const swap = earliest(analogPoint) < earliest(dominant);
1256
+ const answer = swap
1257
+ ? await joinWithBridge(ctx, b, a)
1258
+ : await joinWithBridge(ctx, a, b);
1191
1259
  record(
1192
1260
  answer,
1193
1261
  "analogical comparison — each analog voiced by the context that establishes its role",
@@ -15,7 +15,7 @@ import { read, resolve } from "../primitives.js";
15
15
  import { guidedFirst, hubBound } from "../traverse.js";
16
16
  import { conceptHop } from "../match.js";
17
17
  import { bridge } from "../resonance.js";
18
- import { liftAnswer, segRestatesQuery } from "../types.js";
18
+ import { liftAnswer, liftedScaffolding, segRestatesQuery } from "../types.js";
19
19
  import { decodeText, unexplainedLabel } from "../rationale.js";
20
20
  import { indexOf } from "../../bytes.js";
21
21
  import type { RationaleItem } from "../rationale.js";
@@ -308,6 +308,11 @@ export const coverMechanism: PipelineMechanism = {
308
308
  moves: 0,
309
309
  weight: solved!.cost, // A*LD derivation's g-value IS the weight
310
310
  unexplained: unexplainedLabel(query, accounted),
311
+ // How much of the composed answer is the asker's own unexplained words
312
+ // (the spans the liftAnswer trace above labels "scaffolding"). Cover is
313
+ // the mechanism that can carry them, because a PASS span still lands in
314
+ // the cover it returns.
315
+ scaffolding: liftedScaffolding(segs, query.length, query, W),
311
316
  }];
312
317
  },
313
318
  };
@@ -113,6 +113,33 @@ export async function extractBySkill(
113
113
  subQuantum++;
114
114
  continue;
115
115
  }
116
+ // AN UNANCHORED READ IS NOT AN EXTRACTION. This function's contract (see
117
+ // the doc above) is that `accounted` carries "the located frames AND any
118
+ // read span BOUNDED by located frames on both sides", while an open-ended
119
+ // read "remains a guess about where the span stops — it stays unaccounted".
120
+ // EMPTY accounted is the degenerate case of that: NO frame of the exemplar
121
+ // was located in the query at all, so nothing ties the bytes just read to
122
+ // this question — the skill applied its exemplar's geometry to a query it
123
+ // never matched.
124
+ //
125
+ // The live case (analyze_training.ts F, the battery's ONLY wrong non-silent
126
+ // answer): "Which city is France's seat of government?" answered "Which ci"
127
+ // — a fragment of the query itself — from the exemplar "What is dll", with
128
+ // accounted=[] and pieces=1. isSpanShaped is deliberately permissive (a
129
+ // sparse-subsequence check), so it accepts exemplars whose relation to the
130
+ // query is coincidental gap-matching; requiring at least one LOCATED frame
131
+ // is the structural evidence that permissiveness leaves out.
132
+ //
133
+ // Scoped to extraction ON PURPOSE. The same test at the pipeline's
134
+ // post-grounding density check was tried and REVERTED: `accounted` is passed
135
+ // empty BY CONVENTION on recall's own tiers (recall.ts's ground(…, [], …)),
136
+ // so a density veto there refused six legitimate reverse-recall groundings.
137
+ // Here the field is this mechanism's own output and carries its documented
138
+ // meaning, so the test is sound exactly where the convention does not reach.
139
+ if (built.accounted.length === 0) {
140
+ subQuantum++;
141
+ continue;
142
+ }
116
143
  if (shapeMisses > 0 || subQuantum > 0) {
117
144
  ctx.trace?.step(
118
145
  "trySkillAnchors",
@@ -8,6 +8,7 @@ import { cosine } from "../../vec.js";
8
8
  import {
9
9
  conceptThreshold,
10
10
  consensusFloor,
11
+ dominates,
11
12
  identityBar,
12
13
  reachThreshold,
13
14
  significanceBar,
@@ -15,13 +16,15 @@ import {
15
16
  import type { MindContext } from "../types.js";
16
17
  import { gistOf, read, resolve } from "../primitives.js";
17
18
  import { bytesEqual, indexOf } from "../../bytes.js";
18
- import { corpusN, hubBound } from "../traverse.js";
19
+ import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
19
20
  import { follow, project, reverseContext } from "../match.js";
20
21
  import { CONCEPT, STEP } from "../graph-search.js";
21
22
  import { unexplainedLabel } from "../rationale.js";
22
23
  import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
23
24
  import { rItem, rNode } from "../trace.js";
24
25
  import { substitutionBridge } from "../bridge.js";
26
+ import { frameFillerSubstitution } from "../frame-filler.js";
27
+ import { prefixCandidates, prefixCompletion } from "../prefix-completion.js";
25
28
 
26
29
  /** A recall result. */
27
30
  export interface RecallResult {
@@ -256,13 +259,94 @@ export async function recallByResonance(
256
259
 
257
260
  // 2. Scaffolding-dominated.
258
261
  if (top.score >= sig) {
259
- const N = corpusN(ctx);
260
- const minVote = consensusFloor(N);
261
262
  // The committed points of attention ARE the shared climb's roots (same
262
263
  // query, same k, same DF mode) — read them from Precomputed instead of
263
264
  // re-climbing, so even a traced response pays for the climb once.
264
265
  const forest = (await pre.attention()).roots;
265
- if (forest.length > 0 && forest[0].vote >= minVote) {
266
+ // TRUST THE ANCHOR ON ITS BREADTH, NOT ON ITS ABSOLUTE VOTE.
267
+ //
268
+ // This gate read `forest[0].vote >= consensusFloor(N)`. Attention.breadth's
269
+ // own contract (types.ts) says why that is the wrong quantity: the IDF vote
270
+ // is "an absolute, ln(N)-scaled quantity that means 'strong' on a small
271
+ // store and 'weak' on a large one for the SAME degree of genuine
272
+ // consensus", while breadth is the SCALE-INVARIANT reading — "a point whose
273
+ // breadth clears `dominates` (> half the query's regions corroborate it) is
274
+ // real consensus; one that does not is a coincidental single-region echo".
275
+ // Attention.peak's contract makes the same point from the other side:
276
+ // comparing a POOLED SUM against a floor that prices ONE region's evidence
277
+ // is a dimensional error.
278
+ //
279
+ // Measured on the 15.7M-node store (N=325,615, so the old floor was 13.19).
280
+ // The absolute vote cannot separate right from wrong at this scale, and the
281
+ // proof is a probe that must stay SILENT:
282
+ //
283
+ // anchor picked by the climb vote breadth correct?
284
+ // "What is the chemical formula …" 10.60 0.556 RIGHT
285
+ // "Qual é a capital de França?" 8.19 0.667 RIGHT
286
+ // "Who wrote the play Romeo …?" 8.25 0.833 RIGHT
287
+ // "How do you say "good morning" …" 10.77 0.800 RIGHT
288
+ // "What is the commercial capital …" 12.69 0.333 Zamunda — MUST be silent
289
+ // "Menene sunan ginin mafi tsayi …" 12.79 0.214 wrong (Hausa)
290
+ // "Today is the 5th of March …" 10.36 0.000 wrong
291
+ //
292
+ // Zamunda's junk attractor outvotes every correct anchor, so no vote
293
+ // threshold admits the right ones without admitting fabrication — while
294
+ // breadth > ½ admits exactly the four correct anchors and nothing else.
295
+ // The old floor was simply never cleared on a corpus this large: the tier
296
+ // was dead code here, which is why 12 probes fell through to silence.
297
+ //
298
+ // `dominates(breadth, 1)` is the SAME half-dominance predicate used
299
+ // throughout, applied to the fraction — no new constant, and the bar the
300
+ // breadth contract names. COST: none; breadth is already computed and
301
+ // carried on every Attention the climb returns.
302
+ //
303
+ // The two readings are ALTERNATIVES, never a substitution. REPLACING the
304
+ // vote test with the breadth test was tried and broke 7 tests: on a small
305
+ // store ln(N) is low, so the vote bar is the one that legitimately fires
306
+ // there, and — as Attention.clusters' own contract warns — "breadth starves
307
+ // a genuine, evenly-split multi-topic query, since no root in a real N-way
308
+ // split can exceed half the vote" (the two 3.1 two-topic fusion tests are
309
+ // exactly that shape). Each reading is sufficient on its own evidence: a
310
+ // vote that clears the absolute floor is strong enough wherever the corpus
311
+ // is small enough for that to mean something, and a breadth past ½ is real
312
+ // consensus at any scale. ORing them keeps every admission the floor
313
+ // already made and adds only the scale-invariant ones it could never see.
314
+ //
315
+ // BREADTH ALSO NEEDS DISCRIMINATIVENESS. Breadth asks how much of the
316
+ // query corroborates the anchor, never whether the anchor SAYS anything: on
317
+ // a one-context store every region trivially corroborates the only anchor
318
+ // there is, so breadth is 1 while the anchor's IDF is 0 — and test/31 A2
319
+ // ("explain quantum chromodynamics" against a lone cat fact) answered the
320
+ // cat, which is fabrication. A region's IDF contribution for an anchor
321
+ // reached through c of N contexts is ln(N/c), so requiring it to exceed
322
+ // ln 2 is requiring c·2 < N — the SAME half-dominance reading used
323
+ // everywhere, expressed in the IDF's own units rather than as a new bar.
324
+ // `peak` is that per-region contribution, and reading it here is what
325
+ // Attention.peak's contract asks of a consumer gating on this evidence.
326
+ //
327
+ // AND THE QUERY MUST SAY SOMETHING. Both readings above price the
328
+ // ANCHOR's evidence; neither asks whether the QUERY discriminates
329
+ // anything. A query that is entirely corpus-global scaffolding gives the
330
+ // corpus nothing to be held to, and this tier — which exists to serve
331
+ // scaffolding-DOMINATED queries — is exactly where that runs out.
332
+ // Measured on the trained store: "What is the capital " answered "Colombo
333
+ // is the commercial capital of Sri Lanka…" on breadth 0.667 / clusters 1,
334
+ // and every window it spells is a hub ("What":572). See
335
+ // allWindowsAreScaffolding for the full separation, including the probes
336
+ // this tier serves CORRECTLY, which all retain a discriminating window
337
+ // ("what is the capital of france" → "f fr":248).
338
+ //
339
+ // DISPERSION WAS TRIED HERE FIRST AND FALSIFIED — do not retry it: the
340
+ // fabrication and the no-punctuation robustness probe have the IDENTICAL
341
+ // profile (breadth 0.667, clusters 1), so requiring clusters >= 2 silenced
342
+ // "what is the capital of france" too and cost the battery a probe.
343
+ const minVote = consensusFloor(corpusN(ctx));
344
+ if (
345
+ forest.length > 0 &&
346
+ !allWindowsAreScaffolding(ctx, query) &&
347
+ (forest[0].vote >= minVote ||
348
+ (dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))
349
+ ) {
266
350
  const g = await project(ctx, forest[0].anchor, queryGist);
267
351
  // The anchor cleared the consensus floor, but the floor prices the
268
352
  // ANCHOR's evidence, not the projection's: a junk attractor can clear
@@ -301,6 +385,49 @@ export async function recallByResonance(
301
385
  }
302
386
  }
303
387
  // 3b. Corroborated-substitution bridge — refusal-path only (bridge.ts).
388
+ // MEMOISED ACROSS EVERY REMAINING TIER, and that is load-bearing rather than
389
+ // tidy: the bridge, prefix completion and the frame filler all read the SAME
390
+ // candidate list, so the exhaustive branch runs at most once per response.
391
+ // Without the memo each tier re-issues it — measured at 490 ms median against
392
+ // 13 ms non-exhaustive (36x).
393
+ const wideIdsOnce = async (): Promise<ReadonlyArray<number>> => {
394
+ // When the top resonance hit is below the concept threshold, the query
395
+ // gist has no concept-level match to any stored form — an exhaustive √N
396
+ // ANN would only score more vectors below the bar (profiled at 38K–40K
397
+ // annVectorReads per refusing query on a 325K-context store). The
398
+ // bridge's structural channels (junction walks, anchor climbs) are the
399
+ // correct proposal source for a query whose gist has no clean match;
400
+ // the ANN cannot propose what the gist cannot rank.
401
+ // The condition above is the SCORE of the top hit, not the size of the
402
+ // corpus. It used to be spelled `corpusN(ctx) <= (k · W)³`, which asks
403
+ // a different question and answers it wrongly at exactly the scale the
404
+ // note was written from: on the trained store N = 325,608 with k = 24
405
+ // and W = 4 puts the cube at 884,736, so that store took the exhaustive
406
+ // branch — the very branch measured here as 38K–40K annVectorReads.
407
+ // Measured cost of the mismatch: substitutionBridge 8,544ms of a
408
+ // 19,548ms think (44%), against 1,248ms and 14,218ms without it, with
409
+ // every answer in the battery byte-identical and the suite unchanged
410
+ // at 445/445. Corpus size was never the discriminator; whether the
411
+ // gist ranks ANYTHING at concept level is.
412
+ //
413
+ // Reading it as the note states also removes a duplicated (k · W)³ —
414
+ // the same cube gates crossRegionVotes' walk budget, where it likewise
415
+ // never engages at real scale (see attention.ts).
416
+ if (whole.length > 0 && whole[0].score >= conceptThreshold(ctx.store.D)) {
417
+ const exhaustive = await ctx.store.resonate(
418
+ queryGist,
419
+ hubBound(ctx),
420
+ true,
421
+ );
422
+ return exhaustive.map((h) => h.id);
423
+ }
424
+ return whole.map((h) => h.id);
425
+ };
426
+ let wide: Promise<ReadonlyArray<number>> | null = null;
427
+ const wideIds = (): Promise<
428
+ ReadonlyArray<number>
429
+ > => (wide ??= wideIdsOnce());
430
+
304
431
  // Every gist-based tier has failed; before refusing, align the query
305
432
  // byte-for-byte against the trained contexts its own stored windows
306
433
  // anchor, accepting mismatches only as corpus-attested, concept-bar
@@ -321,39 +448,6 @@ export async function recallByResonance(
321
448
  // exact co-occurrence and bounded anchor ascent are the bridge's structural
322
449
  // proposal channels, while an exhaustive ANN call made every honest
323
450
  // refusal cost hundreds of milliseconds regardless of k.
324
- const wideIds = async (): Promise<ReadonlyArray<number>> => {
325
- // When the top resonance hit is below the concept threshold, the query
326
- // gist has no concept-level match to any stored form — an exhaustive √N
327
- // ANN would only score more vectors below the bar (profiled at 38K–40K
328
- // annVectorReads per refusing query on a 325K-context store). The
329
- // bridge's structural channels (junction walks, anchor climbs) are the
330
- // correct proposal source for a query whose gist has no clean match;
331
- // the ANN cannot propose what the gist cannot rank.
332
- // The condition above is the SCORE of the top hit, not the size of the
333
- // corpus. It used to be spelled `corpusN(ctx) <= (k · W)³`, which asks
334
- // a different question and answers it wrongly at exactly the scale the
335
- // note was written from: on the trained store N = 325,608 with k = 24
336
- // and W = 4 puts the cube at 884,736, so that store took the exhaustive
337
- // branch — the very branch measured here as 38K–40K annVectorReads.
338
- // Measured cost of the mismatch: substitutionBridge 8,544ms of a
339
- // 19,548ms think (44%), against 1,248ms and 14,218ms without it, with
340
- // every answer in the battery byte-identical and the suite unchanged
341
- // at 445/445. Corpus size was never the discriminator; whether the
342
- // gist ranks ANYTHING at concept level is.
343
- //
344
- // Reading it as the note states also removes a duplicated (k · W)³ —
345
- // the same cube gates crossRegionVotes' walk budget, where it likewise
346
- // never engages at real scale (see attention.ts).
347
- if (whole.length > 0 && whole[0].score >= conceptThreshold(ctx.store.D)) {
348
- const exhaustive = await ctx.store.resonate(
349
- queryGist,
350
- hubBound(ctx),
351
- true,
352
- );
353
- return exhaustive.map((h) => h.id);
354
- }
355
- return whole.map((h) => h.id);
356
- };
357
451
  const bridged = await substitutionBridge(ctx, query, wideIds);
358
452
  if (bridged !== null) {
359
453
  const g = await project(ctx, bridged.id, queryGist);
@@ -369,8 +463,33 @@ export async function recallByResonance(
369
463
  bridged.subs.some((s) =>
370
464
  indexOf(cBytes.subarray(s.cs, s.ce), g!, 0) >= 0
371
465
  );
466
+ // THE PREFIX TRAP IS NOT THIS TIER'S TO SPRING. With no substitutions
467
+ // the claim is "a trained context IS this query, up to filler". When
468
+ // the query is a STRICT BYTE PREFIX of that context, the claim is false
469
+ // in the one way that matters: the candidate's extra tail is precisely
470
+ // the DISCRIMINATING part, and dismissing it as filler asserts a
471
+ // specification the asker never made. Measured on a 4,300-fact fixture
472
+ // of "what is the value of <i>?": the query "what is the value of"
473
+ // bridged with subs [] to "what is the value of 0?" and answered "the
474
+ // value of 0 is 0" — one arbitrary pick from 4,300 equally-matching
475
+ // contexts, every one of which fits the query exactly as well.
476
+ //
477
+ // The engine ALREADY has the right machinery for this shape:
478
+ // prefixCompletion runs a few lines below and carries the three guards
479
+ // this tier lacks — unreadable-continuation veto, sub-quantum
480
+ // continuation, and UNIQUENESS (distinct continuations ⇒ refuse), which
481
+ // is exactly what 4,300 competing values must trip. So this is not a
482
+ // new rule and not a new threshold: it is deferring a prefix decision to
483
+ // the tier that owns it (§2.5, one factored machinery). Byte-strict on
484
+ // purpose — a candidate differing by case or punctuation ("what is the
485
+ // capital of france" → "What is the capital of France?") is NOT a byte
486
+ // prefix, keeps grounding here, and is unaffected.
487
+ const strictPrefix = g !== null &&
488
+ cBytes.length > query.length &&
489
+ indexOf(cBytes, query, 0) === 0;
372
490
  if (
373
491
  g !== null && g.length > 0 && !restates(g) && !manufactured &&
492
+ !(bridged.subs.length === 0 && strictPrefix) &&
374
493
  !(g.length < query.length && indexOf(query, g, 0) >= 0)
375
494
  ) {
376
495
  return ground(
@@ -428,6 +547,86 @@ export async function recallByResonance(
428
547
  );
429
548
  }
430
549
  }
550
+
551
+ // 3b′. PREFIX COMPLETION — refusal-path only (prefix-completion.ts).
552
+ // Inside the bridge's block, and deliberately: it consumes `wideIds`, the
553
+ // list the bridge has already fetched, so it costs a bounded byte compare
554
+ // per candidate and not one resonance. The claim it makes is the
555
+ // strongest in the ladder — every query byte is a LITERAL match from
556
+ // offset zero of a trained form — so it needs no projection and no reach
557
+ // gate. It runs after the bridge only because the bridge answers the
558
+ // richer relation when it can; a prefix match that the bridge also
559
+ // explains is the same trained form either way.
560
+ {
561
+ // The resonance list first; only when it supplies nothing does the
562
+ // write side's leaf-id window index propose (prefixCandidates). That
563
+ // ordering is the whole cost story: a query the ranked list can already
564
+ // explain pays not one extra read, and the fallback's bounded walk is
565
+ // spent only where the alternative is an empty answer. It is a second
566
+ // SUPPLY, not a second mechanism — the same three guards decide.
567
+ const completed = prefixCompletion(ctx, query, await wideIds()) ??
568
+ prefixCompletion(ctx, query, prefixCandidates(ctx, query));
569
+ if (completed !== null) {
570
+ return ground(
571
+ completed.form,
572
+ "prefix completion — the query IS the opening of exactly one " +
573
+ "trained form, which this grounds whole",
574
+ // Every query byte is literally matched against the form. The
575
+ // completion is the form's own continuation, not a substitution, so
576
+ // there is nothing to be humble about in the accounting — the same
577
+ // reading the IDENTITY bridge above takes.
578
+ whole_,
579
+ STEP,
580
+ false,
581
+ // NOT complete: the query is a proper PREFIX, so the form may carry
582
+ // more past the remainder this tier voiced.
583
+ false,
584
+ );
585
+ }
586
+ }
587
+ }
588
+
589
+ // 3c. FRAME-FILLER SUBSTITUTION — refusal-path only (frame-filler.ts).
590
+ // The bridge has failed, and for the shape this tier answers it MUST fail:
591
+ // a definite description standing where a proper noun stands is not a
592
+ // similarity relation the bridge can price (raw balance refuses
593
+ // `dominates(6, 37)`, and correctly — that is the France/Spain trap). This
594
+ // tier makes a different claim: not that the two spans resemble each other,
595
+ // but that the store ALREADY HOLDS this query with the filler in the
596
+ // description's place, byte-exactly. A key the store does not hold is
597
+ // discarded, so the answer is always a trained continuation.
598
+ {
599
+ // THE COHORT NEEDS EVIDENCE, AND THE REFUSAL PATH HAS ALREADY BOUGHT IT.
600
+ // This tier reads constituency from what a cohort of exemplars does NOT
601
+ // share, so its resolution is bounded by how many instances of the frame it
602
+ // can see. The top-k resonance hits are too few — on the two-hop probe the
603
+ // exemplars holding the query's discriminative content number TWO, and two
604
+ // structures agree on so little that a whole clause reads as content. The
605
+ // exhaustive list the bridge fetched is the same evidence at ~570 wide, and
606
+ // it is already paid for (memoised above, so this costs no ANN call).
607
+ const filled = frameFillerSubstitution(ctx, query, await wideIds());
608
+ if (filled !== null) {
609
+ const g = await project(ctx, filled.id, queryGist);
610
+ // The same restated-fragment and manufactured-answer guards every tier
611
+ // above applies: a projection contained in the FILLER is the
612
+ // substitution restated as if it were knowledge, not knowledge.
613
+ if (
614
+ g !== null && g.length > 0 && !restates(g) &&
615
+ indexOf(filled.filler, g, 0) < 0 &&
616
+ !(g.length < query.length && indexOf(query, g, 0) >= 0)
617
+ ) {
618
+ return ground(
619
+ g,
620
+ "frame-filler substitution — a trained form IS this query with a " +
621
+ "corroborated filler in the described span's place",
622
+ // The frame is literally matched against the resolved form and the
623
+ // described span is explained by the substitution — the same
624
+ // matched-plus-substituted accounting the bridge reports.
625
+ [[0, query.length]],
626
+ CONCEPT + STEP,
627
+ );
628
+ }
629
+ }
431
630
  }
432
631
 
433
632
  // The refusal/echo decision. The echo returns a stored form's bytes AS