@hviana/sema 0.5.2 → 0.5.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 (41) hide show
  1. package/AGENTS.md +114 -52
  2. package/HOW_IT_WORKS.md +275 -184
  3. package/dist/src/mind/bridge.d.ts +5 -7
  4. package/dist/src/mind/bridge.js +6 -97
  5. package/dist/src/mind/match.d.ts +159 -0
  6. package/dist/src/mind/match.js +300 -7
  7. package/dist/src/mind/mechanisms/prefix-completion.d.ts +22 -0
  8. package/dist/src/mind/{prefix-completion.js → mechanisms/prefix-completion.js} +64 -91
  9. package/dist/src/mind/mechanisms/recall.js +10 -108
  10. package/dist/src/mind/mechanisms/reference.d.ts +6 -0
  11. package/dist/src/mind/mechanisms/reference.js +296 -0
  12. package/dist/src/mind/mind.d.ts +1 -1
  13. package/dist/src/mind/pipeline-mechanism.d.ts +56 -1
  14. package/dist/src/mind/pipeline-mechanism.js +104 -3
  15. package/dist/src/mind/pipeline.d.ts +1 -1
  16. package/dist/src/mind/pipeline.js +13 -1
  17. package/dist/src/mind/traverse.d.ts +38 -0
  18. package/dist/src/mind/traverse.js +91 -1
  19. package/dist/src/store.d.ts +4 -4
  20. package/jsr.json +6 -0
  21. package/package.json +1 -1
  22. package/src/mind/bridge.ts +10 -104
  23. package/src/mind/match.ts +416 -7
  24. package/src/mind/{prefix-completion.ts → mechanisms/prefix-completion.ts} +66 -92
  25. package/src/mind/mechanisms/recall.ts +9 -126
  26. package/src/mind/mechanisms/reference.ts +343 -0
  27. package/src/mind/mind.ts +12 -8
  28. package/src/mind/pipeline-mechanism.ts +120 -3
  29. package/src/mind/pipeline.ts +16 -2
  30. package/src/mind/traverse.ts +92 -1
  31. package/src/store.ts +13 -4
  32. package/test/33-multi-candidate.test.mjs +21 -11
  33. package/test/70-prefix-completion.test.mjs +1 -1
  34. package/test/72-prefix-candidate-supply.test.mjs +7 -9
  35. package/test/74-prefix-trap-not-sprung-early.test.mjs +1 -1
  36. package/test/76-reference-binding.test.mjs +471 -0
  37. package/dist/src/mind/frame-filler.d.ts +0 -15
  38. package/dist/src/mind/frame-filler.js +0 -535
  39. package/dist/src/mind/prefix-completion.d.ts +0 -59
  40. package/src/mind/frame-filler.ts +0 -604
  41. package/test/69-frame-filler.test.mjs +0 -115
@@ -1,5 +1,20 @@
1
- // prefix-completion.ts — Grounding a query that IS the opening of a trained
2
- // form.
1
+ // mechanisms/prefix-completion.ts — Grounding a query that IS the opening of a
2
+ // trained form (Grounding V).
3
+ //
4
+ // A MECHANISM, NOT A TIER. This used to run inside recall's refusal path, in
5
+ // a fixed if-chain that first-match-wins — the shape CAST was refactored away
6
+ // from, where placement rather than the cost ladder decided. Its claim is
7
+ // maximal (every query byte literally matched, from offset zero, against a
8
+ // trained form) at one STEP, so as a market candidate it competes honestly and
9
+ // the decider weighs it like everything else. It is registered LAST: recall's
10
+ // exact self-match makes an IDENTITY claim about the query while this makes a
11
+ // CONTAINMENT one, and on an exact grade tie the identity claim is the
12
+ // stronger evidence — the same ordering §2.3's ladders use.
13
+ //
14
+ // Its SUPPLY moved too, and further: `formsOpenedBy` (traverse.ts) answers a
15
+ // question about the STORE — "which trained forms does this byte run open?" —
16
+ // so it is retrieval machinery any mechanism may ask, not this one's private
17
+ // helper.
3
18
  //
4
19
  // THE SHAPE. `The capital of France is` grounds nothing, while
5
20
  // `The capital of France is Paris.` is trained and reads back byte-exact. The
@@ -74,95 +89,10 @@
74
89
  // Uniqueness is judged on the continuation BYTES, not on the candidate id: the
75
90
  // same continuation reached through two trained forms is one answer, not an
76
91
  // ambiguity.
77
- import { bytesEqual } from "../bytes.js";
78
- import { rItem } from "./trace.js";
79
- import { canonicalWindows, leafIdPrefix } from "./canonical.js";
80
- import { hubBound } from "./traverse.js";
81
- /** Trained forms the query may OPEN, proposed from the write side's own
82
- * leaf-id window index — the supply of last resort for {@link
83
- * prefixCompletion}.
84
- *
85
- * WHY A SECOND SUPPLY EXISTS. The ranked list this mechanism normally reads
86
- * is a resonance list, and resonance cannot rank a proper prefix: measured on
87
- * the trained store, cos(prefix, form) falls from 0.9629 at a one-byte
88
- * truncation to 0.6206 at three bytes, against a reachThreshold of 0.8750.
89
- * Three bytes of truncation put the answer out of reach on GEOMETRY, not on a
90
- * bug, so no k and no re-ranking recovers it.
91
- *
92
- * WHY THIS ROUTE WORKS WHERE THE FOLD DOES NOT. A query's own fold is
93
- * useless here: content addressing is not phrase-position-invariant, so a
94
- * standalone prefix folds to a DIFFERENT node than the same bytes sitting
95
- * inside a longer deposit, and neither the prefix's own node nor its
96
- * ancestors lead to the deposit (measured: the 22-byte prefix of the
97
- * photosynthesis form resolves, is shared by 6 contexts, and does not have
98
- * the form among its ancestors). Leaf ids ARE position-invariant — they are
99
- * content-addressed on single bytes — and `indexSubSpans` already interns a
100
- * flat branch over every canonical WINDOW of a deposit's leaf-id stream, with
101
- * containment edges to the chunks that window spans. A query that is a
102
- * prefix therefore shares those window nodes exactly, and reaches the deposit
103
- * by climbing containment then parents. Nothing is added to the write side;
104
- * this reads an index training already built.
105
- *
106
- * BOUNDED (§2.8), AND WITH NO NEW THRESHOLD. The window whose containment is
107
- * SMALLEST carries the most evidence, and one saturated at `hubBound` carries
108
- * none — that is the same √N reading of "hub" the rest of the mind uses, not
109
- * a tuned knob. The upward walk spends a budget of `hubBound` nodes and
110
- * fans out by W, so a hub query enumerates nothing and the caller stays
111
- * silent rather than guessing (§2.13). Measured on the trained store: the
112
- * photosynthesis form at a one-byte truncation picks a window with 52
113
- * containers, visits 446 nodes, and yields exactly ONE candidate that
114
- * survives the caller's byte compare — the form itself.
115
- *
116
- * These are PROPOSALS only. Every candidate still faces the byte-exact
117
- * prefix compare and all three guards below, so a wrong proposal costs one
118
- * bounded read and can never be voiced (§2.3). */
119
- export function prefixCandidates(ctx, query) {
120
- const store = ctx.store;
121
- const W = ctx.space.maxGroup;
122
- const run = leafIdPrefix(ctx, query);
123
- // The widest canonical window is the most discriminative one the write side
124
- // ever interned; a query too short to spell one carries no window evidence.
125
- const len = canonicalWindows(W)[1];
126
- if (run.length < len)
127
- return [];
128
- const bound = hubBound(ctx);
129
- let best = null;
130
- let bestN = 0;
131
- for (let off = 0; off + len <= run.length; off++) {
132
- const wid = store.findBranch(run.slice(off, off + len));
133
- if (wid === null)
134
- continue;
135
- const n = store.containersSlice(wid, 0, bound).length;
136
- // Empty says the window spans no chunk; saturated says it is a hub, whose
137
- // containment discriminates nothing. Neither is evidence.
138
- if (n === 0 || n >= bound)
139
- continue;
140
- if (best === null || n < bestN) {
141
- best = wid;
142
- bestN = n;
143
- }
144
- }
145
- if (best === null)
146
- return [];
147
- let frontier = store.containersSlice(best, 0, bound);
148
- const seen = new Set(frontier);
149
- let budget = bound;
150
- while (frontier.length > 0 && budget > 0) {
151
- const next = [];
152
- for (const f of frontier) {
153
- if (budget-- <= 0)
154
- break;
155
- for (const p of store.parentsFirst(f, W)) {
156
- if (seen.has(p))
157
- continue;
158
- seen.add(p);
159
- next.push(p);
160
- }
161
- }
162
- frontier = next;
163
- }
164
- return [...seen];
165
- }
92
+ import { bytesEqual } from "../../bytes.js";
93
+ import { rItem } from "../trace.js";
94
+ import { formsOpenedBy } from "../traverse.js";
95
+ import { STEP } from "../graph-search.js";
166
96
  /** The sole trained form the query opens — or null when no candidate opens with
167
97
  * it, when the continuation is sub-quantum, when a candidate's continuation
168
98
  * cannot be read through, or when the candidates disagree.
@@ -268,3 +198,46 @@ export function prefixCompletion(ctx, query, ranked) {
268
198
  }
269
199
  return done(found[0], "one trained form opens with this query, and continues perceivably", data);
270
200
  }
201
+ // ── Pipeline mechanism ──────────────────────────────────────────────────────
202
+ export const prefixMechanism = {
203
+ name: "prefix",
204
+ provenance: "prefix",
205
+ async floor(ctx, query, _pre, worthRunning) {
206
+ // One projection: the form is voiced whole, nothing is substituted.
207
+ // INVESTMENT DISCIPLINE — the supplies below are the response's wide
208
+ // candidate list and a bounded √N walk, so neither is touched until the
209
+ // bound can still beat the incumbent.
210
+ if (!worthRunning(STEP))
211
+ return STEP;
212
+ // A query with no room for a perceivable continuation inside the phrase
213
+ // cap cannot clear guard 2, so it is not worth a single read.
214
+ const cap = query.length * ctx.space.maxGroup;
215
+ if (query.length === 0 || cap < query.length + ctx.space.maxGroup) {
216
+ return null;
217
+ }
218
+ return STEP;
219
+ },
220
+ async run(ctx, query, pre) {
221
+ // The response's shared wide list first; only when it supplies nothing does
222
+ // the write side's window index propose. That ordering is the whole cost
223
+ // story: a query the ranked list can already explain pays not one extra
224
+ // read, and the bounded walk is spent only where the alternative is an
225
+ // empty answer. A second SUPPLY, not a second mechanism — the same three
226
+ // guards decide either way.
227
+ const completed = prefixCompletion(ctx, query, await pre.wideResonance()) ??
228
+ prefixCompletion(ctx, query, formsOpenedBy(ctx, query));
229
+ if (completed === null)
230
+ return [];
231
+ return [{
232
+ bytes: completed.form,
233
+ // Every query byte is literally matched against the form, so there is
234
+ // nothing to be humble about in the accounting — the same reading the
235
+ // IDENTITY bridge takes.
236
+ accounted: [[0, query.length]],
237
+ moves: STEP,
238
+ unexplained: "",
239
+ // NOT complete: the query is a proper PREFIX, so the form may carry more
240
+ // past the remainder this voiced.
241
+ }];
242
+ },
243
+ };
@@ -1,10 +1,10 @@
1
- // mechanisms/recall.ts — Recall by resonance (Grounding IV).
1
+ // mechanisms/recall.ts — Recall by resonance (Grounding VI).
2
2
  //
3
3
  // The recall mechanism resonates the whole query's gist against the content
4
4
  // index and grounds the nearest learned form. Four tiers, orderly degrading
5
5
  // from exact self-match to honest echo.
6
6
  import { cosine } from "../../vec.js";
7
- import { conceptThreshold, consensusFloor, dominates, identityBar, reachThreshold, significanceBar, } from "../../geometry.js";
7
+ import { consensusFloor, dominates, identityBar, reachThreshold, significanceBar, } from "../../geometry.js";
8
8
  import { gistOf, read, resolve } from "../primitives.js";
9
9
  import { bytesEqual, indexOf } from "../../bytes.js";
10
10
  import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
@@ -13,8 +13,6 @@ import { CONCEPT, STEP } from "../graph-search.js";
13
13
  import { unexplainedLabel } from "../rationale.js";
14
14
  import { rItem, rNode } from "../trace.js";
15
15
  import { substitutionBridge } from "../bridge.js";
16
- import { frameFillerSubstitution } from "../frame-filler.js";
17
- import { prefixCandidates, prefixCompletion } from "../prefix-completion.js";
18
16
  /** Recall the answer by resonating the whole query against the content index. */
19
17
  export async function recallByResonance(ctx, query, pre) {
20
18
  const t = ctx.trace?.enter("recallByResonance", [
@@ -110,7 +108,10 @@ export async function recallByResonance(ctx, query, pre) {
110
108
  }
111
109
  }
112
110
  }
113
- const whole = await ctx.store.resonate(queryGist, k);
111
+ // The response's ONE top-k read (Precomputed.resonance) — the same list the
112
+ // frame inventory is assembled from, so a query that reaches both pays for a
113
+ // single ANN query rather than two identical ones.
114
+ const whole = await pre.resonance();
114
115
  if (whole.length === 0) {
115
116
  return ground(null, "empty store — nothing to resonate with", [], 0);
116
117
  }
@@ -293,42 +294,10 @@ export async function recallByResonance(ctx, query, pre) {
293
294
  }
294
295
  }
295
296
  // 3b. Corroborated-substitution bridge — refusal-path only (bridge.ts).
296
- // MEMOISED ACROSS EVERY REMAINING TIER, and that is load-bearing rather than
297
- // tidy: the bridge, prefix completion and the frame filler all read the SAME
298
- // candidate list, so the exhaustive branch runs at most once per response.
299
- // Without the memo each tier re-issues it — measured at 490 ms median against
300
- // 13 ms non-exhaustive (36x).
301
- const wideIdsOnce = async () => {
302
- // When the top resonance hit is below the concept threshold, the query
303
- // gist has no concept-level match to any stored form — an exhaustive √N
304
- // ANN would only score more vectors below the bar (profiled at 38K–40K
305
- // annVectorReads per refusing query on a 325K-context store). The
306
- // bridge's structural channels (junction walks, anchor climbs) are the
307
- // correct proposal source for a query whose gist has no clean match;
308
- // the ANN cannot propose what the gist cannot rank.
309
- // The condition above is the SCORE of the top hit, not the size of the
310
- // corpus. It used to be spelled `corpusN(ctx) <= (k · W)³`, which asks
311
- // a different question and answers it wrongly at exactly the scale the
312
- // note was written from: on the trained store N = 325,608 with k = 24
313
- // and W = 4 puts the cube at 884,736, so that store took the exhaustive
314
- // branch — the very branch measured here as 38K–40K annVectorReads.
315
- // Measured cost of the mismatch: substitutionBridge 8,544ms of a
316
- // 19,548ms think (44%), against 1,248ms and 14,218ms without it, with
317
- // every answer in the battery byte-identical and the suite unchanged
318
- // at 445/445. Corpus size was never the discriminator; whether the
319
- // gist ranks ANYTHING at concept level is.
320
- //
321
- // Reading it as the note states also removes a duplicated (k · W)³ —
322
- // the same cube gates crossRegionVotes' walk budget, where it likewise
323
- // never engages at real scale (see attention.ts).
324
- if (whole.length > 0 && whole[0].score >= conceptThreshold(ctx.store.D)) {
325
- const exhaustive = await ctx.store.resonate(queryGist, hubBound(ctx), true);
326
- return exhaustive.map((h) => h.id);
327
- }
328
- return whole.map((h) => h.id);
329
- };
330
- let wide = null;
331
- const wideIds = () => (wide ??= wideIdsOnce());
297
+ // The WIDE candidate list every past-the-top-k mechanism reads lives on
298
+ // Precomputed (see wideResonance): shared across the whole response, so the
299
+ // exhaustive branch runs at most once whoever first-touches it.
300
+ const wideIds = () => pre.wideResonance();
332
301
  // Every gist-based tier has failed; before refusing, align the query
333
302
  // byte-for-byte against the trained contexts its own stored windows
334
303
  // anchor, accepting mismatches only as corpus-attested, concept-bar
@@ -439,73 +408,6 @@ export async function recallByResonance(ctx, query, pre) {
439
408
  bridged.subs.length === 0);
440
409
  }
441
410
  }
442
- // 3b′. PREFIX COMPLETION — refusal-path only (prefix-completion.ts).
443
- // Inside the bridge's block, and deliberately: it consumes `wideIds`, the
444
- // list the bridge has already fetched, so it costs a bounded byte compare
445
- // per candidate and not one resonance. The claim it makes is the
446
- // strongest in the ladder — every query byte is a LITERAL match from
447
- // offset zero of a trained form — so it needs no projection and no reach
448
- // gate. It runs after the bridge only because the bridge answers the
449
- // richer relation when it can; a prefix match that the bridge also
450
- // explains is the same trained form either way.
451
- {
452
- // The resonance list first; only when it supplies nothing does the
453
- // write side's leaf-id window index propose (prefixCandidates). That
454
- // ordering is the whole cost story: a query the ranked list can already
455
- // explain pays not one extra read, and the fallback's bounded walk is
456
- // spent only where the alternative is an empty answer. It is a second
457
- // SUPPLY, not a second mechanism — the same three guards decide.
458
- const completed = prefixCompletion(ctx, query, await wideIds()) ??
459
- prefixCompletion(ctx, query, prefixCandidates(ctx, query));
460
- if (completed !== null) {
461
- return ground(completed.form, "prefix completion — the query IS the opening of exactly one " +
462
- "trained form, which this grounds whole",
463
- // Every query byte is literally matched against the form. The
464
- // completion is the form's own continuation, not a substitution, so
465
- // there is nothing to be humble about in the accounting — the same
466
- // reading the IDENTITY bridge above takes.
467
- whole_, STEP, false,
468
- // NOT complete: the query is a proper PREFIX, so the form may carry
469
- // more past the remainder this tier voiced.
470
- false);
471
- }
472
- }
473
- }
474
- // 3c. FRAME-FILLER SUBSTITUTION — refusal-path only (frame-filler.ts).
475
- // The bridge has failed, and for the shape this tier answers it MUST fail:
476
- // a definite description standing where a proper noun stands is not a
477
- // similarity relation the bridge can price (raw balance refuses
478
- // `dominates(6, 37)`, and correctly — that is the France/Spain trap). This
479
- // tier makes a different claim: not that the two spans resemble each other,
480
- // but that the store ALREADY HOLDS this query with the filler in the
481
- // description's place, byte-exactly. A key the store does not hold is
482
- // discarded, so the answer is always a trained continuation.
483
- {
484
- // THE COHORT NEEDS EVIDENCE, AND THE REFUSAL PATH HAS ALREADY BOUGHT IT.
485
- // This tier reads constituency from what a cohort of exemplars does NOT
486
- // share, so its resolution is bounded by how many instances of the frame it
487
- // can see. The top-k resonance hits are too few — on the two-hop probe the
488
- // exemplars holding the query's discriminative content number TWO, and two
489
- // structures agree on so little that a whole clause reads as content. The
490
- // exhaustive list the bridge fetched is the same evidence at ~570 wide, and
491
- // it is already paid for (memoised above, so this costs no ANN call).
492
- const filled = frameFillerSubstitution(ctx, query, await wideIds());
493
- if (filled !== null) {
494
- const g = await project(ctx, filled.id, queryGist);
495
- // The same restated-fragment and manufactured-answer guards every tier
496
- // above applies: a projection contained in the FILLER is the
497
- // substitution restated as if it were knowledge, not knowledge.
498
- if (g !== null && g.length > 0 && !restates(g) &&
499
- indexOf(filled.filler, g, 0) < 0 &&
500
- !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
501
- return ground(g, "frame-filler substitution — a trained form IS this query with a " +
502
- "corroborated filler in the described span's place",
503
- // The frame is literally matched against the resolved form and the
504
- // described span is explained by the substitution — the same
505
- // matched-plus-substituted accounting the bridge reports.
506
- [[0, query.length]], CONCEPT + STEP);
507
- }
508
- }
509
411
  }
510
412
  // The refusal/echo decision. The echo returns a stored form's bytes AS
511
413
  // the answer — a near-identity claim about the query — and identity-grade
@@ -0,0 +1,6 @@
1
+ import type { MindContext } from "../types.js";
2
+ import type { MechanismResult, PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
3
+ /** Voice the query's referents through their frame's own attested carriage, or
4
+ * null when the corpus does not attest one. */
5
+ export declare function bindReference(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<MechanismResult | null>;
6
+ export declare const referenceMechanism: PipelineMechanism;
@@ -0,0 +1,296 @@
1
+ // mechanisms/reference.ts — Reference: voice a slot with the context's own
2
+ // bytes (Grounding IV).
3
+ //
4
+ // This file is a CONFIGURATION of the shared frame reading in match.ts, not a
5
+ // pipeline of its own. The three parts it configures live where §2.5 puts
6
+ // them and are reachable by any mechanism:
7
+ //
8
+ // matcher Precomputed.frames() — the frame INVENTORY: which ranked
9
+ // candidates read as instances of the query's own frame, and
10
+ // where each leaves the query VARIABLE. Shared, election-free.
11
+ // projection follow() — each instance's own learnt continuation.
12
+ // gate carriesFillers() — the carriage licence.
13
+ //
14
+ // What this file adds is the reading and the price: elect one frame from the
15
+ // inventory, demand the licence of it, splice, and state the cost.
16
+ //
17
+ // WHAT KIND OF ACT THIS IS. bridge.ts refuses this shape and is right to:
18
+ // `attestedQ` demands the query-side span be corpus-attested, because a
19
+ // SUBSTITUTION asserts equivalence ("these two spans mean the same"), and
20
+ // nothing can corroborate an equivalence claim about bytes the corpus has
21
+ // never seen. A REFERENCE asserts no equivalence. It asserts POSITION —
22
+ // "this is the thing you named, where the corpus keeps one" — and the bytes
23
+ // come from the asker, so voicing them cannot fabricate corpus knowledge.
24
+ // What CAN be fabricated is the relation claimed about them, which is exactly
25
+ // what the licence withholds.
26
+ //
27
+ // The same reasoning is why the bridge is NOT rewired to consult the frame
28
+ // inventory, though it is now free to: the bridge grounds through its
29
+ // candidate's continuation UNSUBSTITUTED, so admitting a slot-gap there would
30
+ // voice the corpus's filler for the asker's referent — the misreference
31
+ // measured live on the trained store ("How do you say 'flurbish' in French?"
32
+ // answered "the way to say hello is \"Bonjour\""). Nor is CAST rewired: its
33
+ // frame gate is WEAVE-local while a slot is COHORT-local, and substituting one
34
+ // population for the other is the error §2.7 names. The notion is made
35
+ // AVAILABLE, never imposed.
36
+ import { carriesFillers, distinct, follow, substituteAll } from "../match.js";
37
+ import { dominates } from "../../geometry.js";
38
+ import { bytesEqual, indexOf } from "../../bytes.js";
39
+ import { unexplainedLabel } from "../rationale.js";
40
+ import { STEP } from "../graph-search.js";
41
+ import { rItem, rNode, traceFail } from "../trace.js";
42
+ /** The minimum number of instances that can establish a frame. One instance
43
+ * agrees with nothing, so no carriage is attested — the same "two or no
44
+ * constituent" reading frame-filler's contentRuns applies.
45
+ *
46
+ * THIS IS ALSO THE MECHANISM'S REACH. Evidence comes from the shared top-k
47
+ * resonance, so a frame the corpus instantiates only ONCE within k is not
48
+ * reachable here. Measured on the trained store: `How do you say 'flurbish'
49
+ * in French?` finds one instance of its frame in the top 24 — the rest are
50
+ * `How do you make …`, a different frame — so this abstains and recall's
51
+ * scaffolding-dominated tier answers with the CORPUS's filler. That
52
+ * misreference is recall's, and widening the supply is not the fix: the
53
+ * exhaustive √N list recall's refusal path builds costs hundreds of
54
+ * milliseconds and this runs before it. Abstaining on thin evidence is the
55
+ * honest reading (§2.13). */
56
+ const MIN_INSTANCES = 2;
57
+ /** THE VOICING GATES — this mechanism's own reading of a pairing, applied here
58
+ * and NOT in the shared matcher.
59
+ *
60
+ * Every one of these is a requirement for SUBSTITUTING AND SPEAKING, not for
61
+ * knowing where a pairing varies, so each belongs to the consumer that
62
+ * speaks. They lived in `frameSlots` first and made the shared reading
63
+ * reference-shaped: three of four real pairings were hidden from every other
64
+ * consumer, including a definite description standing where a noun stands —
65
+ * the shape the frame filler exists for. A shared layer with one usable
66
+ * consumer is private code at a public address.
67
+ *
68
+ * 1. EVERY slot must be a SUBSTITUTION. An insertion or deletion means the
69
+ * query says more or less than the frame does, and there is no occupant to
70
+ * carry. (A consumer that WANTS insertions — "what did the asker add?" —
71
+ * now sees them; this one cannot use them.)
72
+ * 2. EVERY slot must reach one river window on BOTH sides. Below one window
73
+ * byte overlap is chance, not evidence — the floor identityBar, the
74
+ * bridge's attestedQ and recognition's site test all draw.
75
+ * 3. The FRAME MUST DOMINATE the query, or this is a different sentence that
76
+ * happens to align somewhere. It is also what bounds the slot count
77
+ * without a constant: every slot costs at least a window, and the frame
78
+ * must still be more than half the query.
79
+ * 4. FILLERS PAIRWISE DISTINCT. Two slots holding the same bytes make the
80
+ * occurrence → referent mapping ambiguous, and no evidence resolves it. */
81
+ function voiceable(inst, W, queryLen) {
82
+ if (inst.slots.length === 0)
83
+ return false;
84
+ for (const slot of inst.slots) {
85
+ if (slot.kind !== "substitution")
86
+ return false;
87
+ if (slot.qe - slot.qs < W || slot.ce - slot.cs < W)
88
+ return false;
89
+ }
90
+ if (!dominates(inst.covered, queryLen))
91
+ return false;
92
+ return distinct(inst.slots.map((s) => s.filler));
93
+ }
94
+ /** Elect ONE frame from the inventory: the voiceable instances that place
95
+ * their slots in the same query spans.
96
+ *
97
+ * THE ELECTION IS THIS MECHANISM'S, not the inventory's (see
98
+ * Precomputed.frames). Demanding that everything which aligned agree would
99
+ * be the wrong population: the top-k is ranked by gist, not by frame, so it
100
+ * mixes them — measured, `How do you say 'flurbish' in French?` returns one
101
+ * instance of its own frame and a dozen of `How do you make …`, which align
102
+ * on the shared opening and put their slot somewhere else entirely. One
103
+ * dissenting frame would then veto every binding.
104
+ *
105
+ * Instances of ONE frame put their slots in ONE place, so grouping by the
106
+ * whole slot signature and keeping the modal group IS the frame. The
107
+ * signature is every slot, not one: two candidates agreeing about a file name
108
+ * but disagreeing about whether a second thing was named are instances of two
109
+ * different frames, and mixing them would let a one-slot instance vouch for a
110
+ * two-slot binding it says nothing about. */
111
+ function electFrame(inventory, W, queryLen) {
112
+ const bySignature = new Map();
113
+ for (const inst of inventory) {
114
+ if (!voiceable(inst, W, queryLen))
115
+ continue;
116
+ const key = inst.slots.map((s) => `${s.qs}:${s.qe}`).join(",");
117
+ const group = bySignature.get(key);
118
+ if (group === undefined)
119
+ bySignature.set(key, [inst]);
120
+ else
121
+ group.push(inst);
122
+ }
123
+ let best = [];
124
+ for (const group of bySignature.values()) {
125
+ // Ties keep the FIRST group in insertion order, which is resonance rank —
126
+ // corpus-determined, like every other tie-break here (§2.1).
127
+ if (group.length > best.length)
128
+ best = group;
129
+ }
130
+ return best;
131
+ }
132
+ /** Voice the query's referents through their frame's own attested carriage, or
133
+ * null when the corpus does not attest one. */
134
+ export async function bindReference(ctx, query, pre) {
135
+ const t = ctx.trace?.enter("bindReference", [rItem(query, "query")]);
136
+ const fail = traceFail(t);
137
+ const frame = electFrame(await pre.frames(), ctx.space.maxGroup, query.length);
138
+ if (frame.length < MIN_INSTANCES) {
139
+ return fail(`${frame.length} instance(s) of one frame — agreement needs ` +
140
+ `${MIN_INSTANCES}`);
141
+ }
142
+ const slots = frame[0].slots.map((s) => [s.qs, s.qe]);
143
+ const fillersOf = (inst) => inst.slots.map((s) => s.filler);
144
+ const referents = slots.map(([s, e]) => query.subarray(s, e));
145
+ // A referent must be the ASKER's, never the engine's own words. A completed
146
+ // reply stays available to recognition and the climb as context, but quoting
147
+ // it back as a referent launders the engine's own output into evidence — the
148
+ // same rule the weave applies when it aligns only the asker's stream. ANY
149
+ // slot falling inside one refuses the whole binding.
150
+ for (const [rs, re] of slots) {
151
+ for (const [as_, ae] of ctx.answeredSpans) {
152
+ if (rs < ae && as_ < re) {
153
+ return fail("a referent lies inside a completed reply — not the asker's");
154
+ }
155
+ }
156
+ }
157
+ // Two slots may not name the same bytes, for the same reason two fillers may
158
+ // not (frameSlots applies it to every instance): the mapping from occurrence
159
+ // to referent would be ambiguous.
160
+ if (!distinct(referents)) {
161
+ return fail("two slots name the same bytes — the binding is ambiguous");
162
+ }
163
+ // ── THE LICENCE ────────────────────────────────────────────────────────
164
+ // Every instance must agree, against the first, that its continuation is its
165
+ // own fillers carried through one fixed form. Unanimity, exactly as the
166
+ // bridge's `unanimous` demands of a frame before it will substitute.
167
+ //
168
+ // Continuations are followed ONE AT A TIME, inside the test, because the
169
+ // overwhelmingly common outcome is refusal and a refusal usually comes from
170
+ // the first comparison — a filler-dependent frame disagrees on instance 2 of
171
+ // 12. Reading all of them up front pays the whole cohort's projections to
172
+ // discard them. Each goes through the shared projection, so an ambiguous
173
+ // instance is disambiguated exactly as it would be anywhere else.
174
+ const leadsNowhere = "an instance of the frame leads nowhere";
175
+ const first = await follow(ctx, frame[0].id, pre.guide);
176
+ if (first === null || first.length === 0)
177
+ return fail(leadsNowhere);
178
+ for (let i = 1; i < frame.length; i++) {
179
+ const cont = await follow(ctx, frame[i].id, pre.guide);
180
+ if (cont === null || cont.length === 0)
181
+ return fail(leadsNowhere);
182
+ if (!carriesFillers(first, fillersOf(frame[0]), cont, fillersOf(frame[i]))) {
183
+ ctx.trace?.step("referenceLicence", [rItem(first, "instance"), rNode(ctx, frame[i].id, "against")], [rItem(cont, "attested")], "refused — the frame's answer carries content that depends on WHICH " +
184
+ "filler, so the corpus cannot supply it for a new one");
185
+ return fail("the frame's answer is not a carriage of its fillers");
186
+ }
187
+ }
188
+ // THE BINDING IS A BYTE CONSTRUCTION, NOT A SEARCH, and deliberately so.
189
+ // Articulation splices through ctx.search.cover because WHICH voicing wins is
190
+ // genuinely searched — several candidate substitutions compete. Here the
191
+ // licence has already determined the substitution byte-exactly; a search that
192
+ // can only confirm a determined result is ceremony, not composition. The
193
+ // precedent is frame-filler, which constructs its lookup key the same way.
194
+ const bytes = substituteAll(first, fillersOf(frame[0]).map((needle, s) => ({ needle, repl: referents[s] })));
195
+ if (bytes.length === 0)
196
+ return fail("the binding produced nothing");
197
+ // Answering with the question is not answering — the same restated-fragment
198
+ // guard every recall tier applies.
199
+ if (bytes.length < query.length && indexOf(query, bytes, 0) >= 0) {
200
+ return fail("the binding restates part of the question");
201
+ }
202
+ const carried = !bytesEqual(bytes, first);
203
+ ctx.trace?.step("bindReferent", [
204
+ ...referents.map((r, s) => rItem(r, `referent ${s + 1}`, undefined, slots[s])),
205
+ ...frame.map((inst) => rNode(ctx, inst.id, "instance")),
206
+ ], [rItem(bytes, "bound")], carried
207
+ ? `carry the asker's ${slots.length} referent(s) through the frame's own ` +
208
+ `answer — ${frame.length} instances attest the carriage byte-exactly`
209
+ : `the frame's answer does not depend on its filler(s) — ` +
210
+ `${frame.length} instances attest the same continuation`);
211
+ // WHAT THIS EXPLAINS: the frame it matched literally, AND every slot. A slot
212
+ // is not a hole in the explanation — it is an act this mechanism PAID for,
213
+ // one STEP each in `moves` below, and leaving it unaccounted charges the same
214
+ // act twice (once as a move, once at PASS per byte, which is far the larger).
215
+ // The bridge records the same reasoning for its own substitutions.
216
+ const accounted = [
217
+ ...frame[0].matched.filter(([s, e]) => e > s),
218
+ ...slots,
219
+ ].sort((a, b) => a[0] - b[0]);
220
+ t?.done([rItem(bytes, "answer")], "reference — the asker's referent(s) voiced through a slot the corpus " +
221
+ "attests as a carriage");
222
+ return {
223
+ bytes,
224
+ accounted,
225
+ // The acts: one BINDING per slot plus one edge FOLLOW across the frame's
226
+ // own continuation — the same per-projection price CAST's substitution
227
+ // schema pays for the structurally analogous act. Not CONCEPT: the ladder
228
+ // reserves that for halo-mediated acts, and a reference is decided by byte
229
+ // identity, not distributional company. Per-slot matters: a two-slot
230
+ // binding claims strictly more than a one-slot binding, so where both are
231
+ // licensed the smaller claim wins.
232
+ moves: STEP * slots.length + STEP,
233
+ unexplained: unexplainedLabel(query, accounted),
234
+ // NOT scaffolding. That field counts answer bytes carried through BECAUSE
235
+ // NOTHING EXPLAINED THEM; a referent is carried because the frame's slot
236
+ // explains it, and it is accounted above. Reporting it would make every
237
+ // licensed binding lose its equal-grade tie-breaks by construction.
238
+ //
239
+ // COMPLETE — post-grounding must not extend a binding. The bound answer is
240
+ // a byte string this mechanism CONSTRUCTED; the corpus never said it, so
241
+ // pivoting through it treats the engine's own construction as a trained
242
+ // fact — the same laundering the answeredSpans guard above refuses in the
243
+ // other direction. Measured: with the frame "How do I compile X?" ->
244
+ // "Run gcc X" and a stored "Run gcc main.c" -> "then execute ./a.out", the
245
+ // binding produced "Run gcc main.c" and reason() pivoted straight past it,
246
+ // answering "then execute ./a.out" — the referent gone AND the question
247
+ // unanswered. This is what MechanismResult.complete documents: the query
248
+ // IS a trained context (here, an instance of a trained frame), so its
249
+ // continuation is the whole read-out. Fusion still runs; it is gated on
250
+ // the query REMAINDER, which a binding accounting frame plus slots leaves
251
+ // empty.
252
+ complete: true,
253
+ };
254
+ }
255
+ // ── Pipeline mechanism ──────────────────────────────────────────────────────
256
+ export const referenceMechanism = {
257
+ name: "reference",
258
+ provenance: "reference",
259
+ async floor(ctx, query, pre, worthRunning) {
260
+ // The floor is exactly one binding plus one follow — the cheapest shape a
261
+ // result can take. INVESTMENT DISCIPLINE: when that already cannot beat
262
+ // the incumbent, return it UNINVESTED rather than first-touching the shared
263
+ // inventory (cast.ts and extraction.ts are the reference implementations).
264
+ const bound = STEP + STEP;
265
+ if (!worthRunning(bound))
266
+ return bound;
267
+ // A frame needs a query long enough to hold one, and a slot needs at least
268
+ // one window of its own beside it.
269
+ if (query.length < 2 * ctx.space.maxGroup)
270
+ return null;
271
+ // A query the store holds outright is not a reference to anything — its own
272
+ // edges answer it, and binding would re-derive what recall reads directly.
273
+ // O(|query|) probes, already computed for this response.
274
+ if (pre.queryResolved !== null)
275
+ return null;
276
+ // NO SCAFFOLDING GATE HERE, DELIBERATELY. `allWindowsAreScaffolding` gates
277
+ // recall's scaffolding-dominated tier and looks like the obvious third
278
+ // gate, but it is calibrated for mechanisms grounding THROUGH the query's
279
+ // stored windows, where "every window is a hub" means the query says
280
+ // nothing the corpus can be held to. A reference query's discriminative
281
+ // content is the SLOT — exactly the part the corpus cannot attest — so the
282
+ // predicate reports "all scaffolding" for the purest references there are.
283
+ // Measured on the byte-modality fixture (test/76): the frame's windows sit
284
+ // in all three instances and the referents' in none, so the gate fired and
285
+ // the mechanism abstained on an answer it had already derived correctly.
286
+ //
287
+ // What holds this honest is not window rarity but the licence: instances
288
+ // agreeing on one slot signature, each leading somewhere, and unanimous
289
+ // BYTE-EXACT carriage across all of them.
290
+ return bound;
291
+ },
292
+ async run(ctx, query, pre) {
293
+ const bound = await bindReference(ctx, query, pre);
294
+ return bound === null ? [] : [bound];
295
+ },
296
+ };
@@ -145,7 +145,7 @@ export declare class Mind implements MindContext {
145
145
  _gistCache: BoundedMap<number, Vec>;
146
146
  _depositTrees: BoundedMap<string, import("./types.js").DepositCacheEntry>;
147
147
  _depositLens: Set<number>;
148
- _internIds: WeakMap<Sema, number>;
148
+ _internIds: WeakMap<import("../sema.js").Sema, number>;
149
149
  private _nextConvId;
150
150
  private _conversations;
151
151
  /** Canonical node id of a byte span. Required by GraphSearchHost & MindContext. */