@hviana/sema 0.5.3 → 0.5.5

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.
package/AGENTS.md CHANGED
@@ -179,12 +179,12 @@ matchers (`locate`, `alignRuns`, `alignGraded`, `alignAround`/`frameSlots`,
179
179
  `bestHaloMate`, `haloSiblings`, `analogyStrength` and its structural tier
180
180
  `sharedFrameStrength`, `spanHalo`, `spanSynonymStrength`), projections
181
181
  (`follow`, `reverseContext`, `project`, `conceptHop`), the span-shape family
182
- (`skillExemplar`, `isSpanShaped`, `containsSpan`), and the two STRUCTURAL gates
183
- that are byte predicates rather than derived thresholds (`isSpanShaped`,
184
- `carriesFillers`). `mind/traverse.ts` owns the graph readings (`edgeAncestors`,
185
- `reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`, `leadsSomewhere`,
186
- `allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus scale (`corpusN`,
187
- `hubBound`, `hubCap`, `atomReach`).
182
+ (`skillExemplar`, `isSpanShaped`, `containsSpan`), and the STRUCTURAL gates that
183
+ are byte predicates rather than derived thresholds (`isSpanShaped`,
184
+ `carriesFillers`, `voicesDisplacedFiller`). `mind/traverse.ts` owns the graph
185
+ readings (`edgeAncestors`, `reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`,
186
+ `leadsSomewhere`, `allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus
187
+ scale (`corpusN`, `hubBound`, `hubCap`, `atomReach`).
188
188
 
189
189
  _Follow it:_ before writing a new generalising mechanism, express it as a
190
190
  (matcher, direction, gate) triple. If those already exist, the mechanism is a
package/HOW_IT_WORKS.md CHANGED
@@ -4314,6 +4314,22 @@ The tier accounts for exactly the query span whose evidence carried the winning
4314
4314
  point of attention (§17.6's `start`–`end`), not the whole query — a consensus
4315
4315
  vote for "ice" among scaffolding does not explain the word "steel". One CONCEPT.
4316
4316
 
4317
+ One further refusal is specific to grounding _through an anchor_. This tier
4318
+ voices the anchor's continuation, so when the query is the same structure as the
4319
+ anchor with one position filled differently, that continuation speaks the
4320
+ **anchor's** occupant of the position — fluent, specific, and about the wrong
4321
+ thing (`How do I compile main.c?` answered `Run gcc hello.c`). A displaced slot
4322
+ alone does not license the refusal, because it cannot separate a different
4323
+ _thing_ from a different _word_: `symbol` ← `formula` has exactly the shape
4324
+ `main` ← `hello` does, and refusing it silences a correct answer phrased in the
4325
+ corpus's own words. The discriminator is whether the projection still shares
4326
+ perceivable content with the query outside that position — one river window is
4327
+ enough. `The chemical formula for water is H2O.` shares `for water`;
4328
+ `Run gcc hello.c` shares only `.c`, two bytes, below the scale at which overlap
4329
+ stops being chance. The gate refuses only; putting the asker's own occupant in
4330
+ its place is a stronger claim needing the corpus's carriage evidence, which is
4331
+ §20.5's business.
4332
+
4317
4333
  ### 21.4 Tier 3 — the nearest grounded hit, at the query-relative fraction
4318
4334
 
4319
4335
  Walk the hits nearest-first and ground the first whose grounding explains enough
@@ -173,6 +173,59 @@ export interface FrameInstance {
173
173
  *
174
174
  * Null only for a degenerate pairing (either side empty). */
175
175
  export declare function frameSlots(ctx: MindContext, query: Uint8Array, cand: Uint8Array, id: number): FrameInstance | null;
176
+ /** THE DISPLACED-FILLER GATE: does `projection` speak the ANCHOR's occupant of
177
+ * a position the query fills differently?
178
+ *
179
+ * A mechanism grounding through an anchor voices that anchor's continuation.
180
+ * When the query is the same structure as the anchor with one position filled
181
+ * differently — a different filename, a different word — the anchor's
182
+ * continuation is ABOUT THE ANCHOR'S occupant, and voicing it answers a
183
+ * question the asker did not ask. It is worse than silence, because it is
184
+ * fluent and specific and wrong:
185
+ *
186
+ * trained `How do I compile hello.c?` -> `Run gcc hello.c`
187
+ * asked `How do I compile main.c?`
188
+ * voiced `Run gcc hello.c` <- the corpus's file, not the asker's
189
+ *
190
+ * The same shape on the trained 15.7M-node store: `How do you say 'flurbish'
191
+ * in French?` answers "the way to say hello is \"Bonjour\"".
192
+ *
193
+ * THIS IS NOT THE RESTATED-FRAGMENT GUARD. That one asks whether the
194
+ * projection is a piece of the QUERY; this asks whether it is a piece of the
195
+ * ANCHOR that the query displaced. Neither implies the other, and the
196
+ * observed failures pass the restatement guard cleanly.
197
+ *
198
+ * Three conditions, all byte-exact and all necessary:
199
+ *
200
+ * 1. the query and the anchor must be ONE STRUCTURE — what they share has to
201
+ * dominate the query, or the query is not a variant of the anchor at all
202
+ * and the anchor's occupant of anything is beside the point;
203
+ * 2. both sides of the position must reach one river window — below it byte
204
+ * overlap is chance, not evidence (the floor identityBar and the bridge's
205
+ * attestedQ both draw);
206
+ * 3. the projection must voice the anchor's filler and NOT the query's
207
+ * referent. Voicing both is a projection that carried the asker's own
208
+ * occupant through, which is exactly what a licensed reference does and
209
+ * must stay allowed;
210
+ * 4. and the projection must share NO perceivable content with the query
211
+ * outside that position — no run of one river window.
212
+ *
213
+ * GATE 4 IS WHAT SEPARATES A DIFFERENT THING FROM A DIFFERENT WORD, and
214
+ * without it this refuses correct answers. A displaced slot alone cannot
215
+ * tell them apart: `symbol` <- `formula` and `main` <- `hello` are the same
216
+ * shape to the matcher — one substitution slot, frame dominating. Measured
217
+ * on the trained store, gates 1-3 alone silenced
218
+ *
219
+ * Q `What is the chemical symbol for water?`
220
+ * A `The chemical formula for water is H2O.`
221
+ *
222
+ * which is right, and merely phrased in the corpus's own words. The answer
223
+ * shares `the chemical ` and ` for water` with the question, so it is plainly
224
+ * about what was asked. `Run gcc hello.c` against `How do I compile main.c?`
225
+ * shares nothing but `.c` — two bytes, below the window where overlap stops
226
+ * being chance — so it is not about what was asked at all. No new constant:
227
+ * W is the same floor identityBar, attestedQ and the site test already draw. */
228
+ export declare function voicesDisplacedFiller(ctx: MindContext, query: Uint8Array, anchor: Uint8Array, projection: Uint8Array): boolean;
176
229
  /** Whether every member is byte-distinct from the others. */
177
230
  export declare function distinct(items: readonly Uint8Array[]): boolean;
178
231
  /** Substitute every `needle -> repl` pair SIMULTANEOUSLY: one left-to-right
@@ -30,7 +30,7 @@
30
30
  // thresholds — isSpanShaped and carriesFillers — live here beside the matchers
31
31
  // they gate.
32
32
  import { addInto, cosine, dot, normalize, zeros } from "../vec.js";
33
- import { conceptThreshold, identityBar, significanceBar, } from "../geometry.js";
33
+ import { conceptThreshold, dominates, identityBar, significanceBar, } from "../geometry.js";
34
34
  import { bytesEqual, indexOf } from "../bytes.js";
35
35
  import { chainReach, leafIdRun } from "./canonical.js";
36
36
  import { foldTree, gistOf, perceive, read, resolve } from "./primitives.js";
@@ -440,6 +440,86 @@ export function frameSlots(ctx, query, cand, id) {
440
440
  const covered = spans.reduce((n, [s, e]) => n + e - s, 0);
441
441
  return { id, slots, matched: spans, covered };
442
442
  }
443
+ /** THE DISPLACED-FILLER GATE: does `projection` speak the ANCHOR's occupant of
444
+ * a position the query fills differently?
445
+ *
446
+ * A mechanism grounding through an anchor voices that anchor's continuation.
447
+ * When the query is the same structure as the anchor with one position filled
448
+ * differently — a different filename, a different word — the anchor's
449
+ * continuation is ABOUT THE ANCHOR'S occupant, and voicing it answers a
450
+ * question the asker did not ask. It is worse than silence, because it is
451
+ * fluent and specific and wrong:
452
+ *
453
+ * trained `How do I compile hello.c?` -> `Run gcc hello.c`
454
+ * asked `How do I compile main.c?`
455
+ * voiced `Run gcc hello.c` <- the corpus's file, not the asker's
456
+ *
457
+ * The same shape on the trained 15.7M-node store: `How do you say 'flurbish'
458
+ * in French?` answers "the way to say hello is \"Bonjour\"".
459
+ *
460
+ * THIS IS NOT THE RESTATED-FRAGMENT GUARD. That one asks whether the
461
+ * projection is a piece of the QUERY; this asks whether it is a piece of the
462
+ * ANCHOR that the query displaced. Neither implies the other, and the
463
+ * observed failures pass the restatement guard cleanly.
464
+ *
465
+ * Three conditions, all byte-exact and all necessary:
466
+ *
467
+ * 1. the query and the anchor must be ONE STRUCTURE — what they share has to
468
+ * dominate the query, or the query is not a variant of the anchor at all
469
+ * and the anchor's occupant of anything is beside the point;
470
+ * 2. both sides of the position must reach one river window — below it byte
471
+ * overlap is chance, not evidence (the floor identityBar and the bridge's
472
+ * attestedQ both draw);
473
+ * 3. the projection must voice the anchor's filler and NOT the query's
474
+ * referent. Voicing both is a projection that carried the asker's own
475
+ * occupant through, which is exactly what a licensed reference does and
476
+ * must stay allowed;
477
+ * 4. and the projection must share NO perceivable content with the query
478
+ * outside that position — no run of one river window.
479
+ *
480
+ * GATE 4 IS WHAT SEPARATES A DIFFERENT THING FROM A DIFFERENT WORD, and
481
+ * without it this refuses correct answers. A displaced slot alone cannot
482
+ * tell them apart: `symbol` <- `formula` and `main` <- `hello` are the same
483
+ * shape to the matcher — one substitution slot, frame dominating. Measured
484
+ * on the trained store, gates 1-3 alone silenced
485
+ *
486
+ * Q `What is the chemical symbol for water?`
487
+ * A `The chemical formula for water is H2O.`
488
+ *
489
+ * which is right, and merely phrased in the corpus's own words. The answer
490
+ * shares `the chemical ` and ` for water` with the question, so it is plainly
491
+ * about what was asked. `Run gcc hello.c` against `How do I compile main.c?`
492
+ * shares nothing but `.c` — two bytes, below the window where overlap stops
493
+ * being chance — so it is not about what was asked at all. No new constant:
494
+ * W is the same floor identityBar, attestedQ and the site test already draw. */
495
+ export function voicesDisplacedFiller(ctx, query, anchor, projection) {
496
+ const W = ctx.space.maxGroup;
497
+ const inst = frameSlots(ctx, query, anchor, 0);
498
+ if (inst === null)
499
+ return false;
500
+ if (!dominates(inst.covered, query.length))
501
+ return false;
502
+ for (const slot of inst.slots) {
503
+ if (slot.kind !== "substitution")
504
+ continue;
505
+ if (slot.qe - slot.qs < W || slot.filler.length < W)
506
+ continue;
507
+ if (indexOf(projection, slot.filler, 0) < 0)
508
+ continue;
509
+ const referent = query.subarray(slot.qs, slot.qe);
510
+ if (indexOf(projection, referent, 0) >= 0)
511
+ continue;
512
+ // Gate 4: does the projection still speak about the query's FRAME? A run
513
+ // of one window anywhere outside the displaced position is enough — the
514
+ // answer is then about the thing that was asked, in the corpus's own
515
+ // wording. Sharing nothing means it is about something else.
516
+ const shared = alignRuns(ctx, query, projection).some((r) => r.qe - r.qs >= W && (r.qe <= slot.qs || r.qs >= slot.qe));
517
+ if (shared)
518
+ continue;
519
+ return true;
520
+ }
521
+ return false;
522
+ }
443
523
  /** Whether every member is byte-distinct from the others. */
444
524
  export function distinct(items) {
445
525
  for (let i = 0; i < items.length; i++) {
@@ -8,7 +8,7 @@ import { consensusFloor, dominates, identityBar, reachThreshold, significanceBar
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";
11
- import { follow, project, reverseContext } from "../match.js";
11
+ import { follow, project, reverseContext, voicesDisplacedFiller, } from "../match.js";
12
12
  import { CONCEPT, STEP } from "../graph-search.js";
13
13
  import { unexplainedLabel } from "../rationale.js";
14
14
  import { rItem, rNode } from "../trace.js";
@@ -270,7 +270,19 @@ export async function recallByResonance(ctx, query, pre) {
270
270
  (forest[0].vote >= minVote ||
271
271
  (dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))) {
272
272
  const g = await project(ctx, forest[0].anchor, queryGist);
273
- // The anchor cleared the consensus floor, but the floor prices the
273
+ // THE ANCHOR'S OCCUPANT IS NOT THE ASKER'S. This tier grounds an anchor
274
+ // the climb elected, and voices that anchor's continuation. When the
275
+ // query is the same structure with one position filled differently, the
276
+ // continuation speaks the ANCHOR's occupant of that position — a fluent,
277
+ // specific, wrong answer (see voicesDisplacedFiller). Refusing here is
278
+ // silence, not a redirection: putting the asker's referent in its place
279
+ // is a stronger claim that needs the corpus's own carriage evidence, and
280
+ // that is `reference`'s job.
281
+ const anchorBytes = read(ctx, forest[0].anchor, query.length * ctx.space.maxGroup + 1);
282
+ if (g !== null && voicesDisplacedFiller(ctx, query, anchorBytes, g)) {
283
+ ctx.trace?.step("displacedFiller", [rItem(query, "query"), rNode(ctx, forest[0].anchor, "anchor")], [rItem(g, "withheld")], "refused — the anchor's continuation speaks the anchor's own " +
284
+ "occupant of a position this query fills differently");
285
+ } // The anchor cleared the consensus floor, but the floor prices the
274
286
  // ANCHOR's evidence, not the projection's: a junk attractor can clear
275
287
  // it and project a PIECE OF THE QUERY back at it (the observed
276
288
  // "buenos días in English" → "English" fragment). A projection that
@@ -278,7 +290,7 @@ export async function recallByResonance(ctx, query, pre) {
278
290
  // — never an answer (the same principle as `restates` above, extended
279
291
  // to fragments). Genuine anchor groundings — longer than the query,
280
292
  // or disjoint from it — pass untouched.
281
- if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
293
+ else if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
282
294
  return ground(g, "scaffolding-dominated query — ground the consensus-climb anchor", [[forest[0].start, forest[0].end]], CONCEPT);
283
295
  }
284
296
  }
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://jsr.io/schema/config-file.v1.json",
3
3
  "name": "@hviana/sema",
4
- "version": "0.5.3",
4
+ "version": "0.5.5",
5
5
  "exports": "./src/index.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hviana/sema",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Sema: a non-parametric, instance-based reasoning system.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
package/src/mind/match.ts CHANGED
@@ -612,6 +612,87 @@ export function frameSlots(
612
612
  return { id, slots, matched: spans, covered };
613
613
  }
614
614
 
615
+ /** THE DISPLACED-FILLER GATE: does `projection` speak the ANCHOR's occupant of
616
+ * a position the query fills differently?
617
+ *
618
+ * A mechanism grounding through an anchor voices that anchor's continuation.
619
+ * When the query is the same structure as the anchor with one position filled
620
+ * differently — a different filename, a different word — the anchor's
621
+ * continuation is ABOUT THE ANCHOR'S occupant, and voicing it answers a
622
+ * question the asker did not ask. It is worse than silence, because it is
623
+ * fluent and specific and wrong:
624
+ *
625
+ * trained `How do I compile hello.c?` -> `Run gcc hello.c`
626
+ * asked `How do I compile main.c?`
627
+ * voiced `Run gcc hello.c` <- the corpus's file, not the asker's
628
+ *
629
+ * The same shape on the trained 15.7M-node store: `How do you say 'flurbish'
630
+ * in French?` answers "the way to say hello is \"Bonjour\"".
631
+ *
632
+ * THIS IS NOT THE RESTATED-FRAGMENT GUARD. That one asks whether the
633
+ * projection is a piece of the QUERY; this asks whether it is a piece of the
634
+ * ANCHOR that the query displaced. Neither implies the other, and the
635
+ * observed failures pass the restatement guard cleanly.
636
+ *
637
+ * Three conditions, all byte-exact and all necessary:
638
+ *
639
+ * 1. the query and the anchor must be ONE STRUCTURE — what they share has to
640
+ * dominate the query, or the query is not a variant of the anchor at all
641
+ * and the anchor's occupant of anything is beside the point;
642
+ * 2. both sides of the position must reach one river window — below it byte
643
+ * overlap is chance, not evidence (the floor identityBar and the bridge's
644
+ * attestedQ both draw);
645
+ * 3. the projection must voice the anchor's filler and NOT the query's
646
+ * referent. Voicing both is a projection that carried the asker's own
647
+ * occupant through, which is exactly what a licensed reference does and
648
+ * must stay allowed;
649
+ * 4. and the projection must share NO perceivable content with the query
650
+ * outside that position — no run of one river window.
651
+ *
652
+ * GATE 4 IS WHAT SEPARATES A DIFFERENT THING FROM A DIFFERENT WORD, and
653
+ * without it this refuses correct answers. A displaced slot alone cannot
654
+ * tell them apart: `symbol` <- `formula` and `main` <- `hello` are the same
655
+ * shape to the matcher — one substitution slot, frame dominating. Measured
656
+ * on the trained store, gates 1-3 alone silenced
657
+ *
658
+ * Q `What is the chemical symbol for water?`
659
+ * A `The chemical formula for water is H2O.`
660
+ *
661
+ * which is right, and merely phrased in the corpus's own words. The answer
662
+ * shares `the chemical ` and ` for water` with the question, so it is plainly
663
+ * about what was asked. `Run gcc hello.c` against `How do I compile main.c?`
664
+ * shares nothing but `.c` — two bytes, below the window where overlap stops
665
+ * being chance — so it is not about what was asked at all. No new constant:
666
+ * W is the same floor identityBar, attestedQ and the site test already draw. */
667
+ export function voicesDisplacedFiller(
668
+ ctx: MindContext,
669
+ query: Uint8Array,
670
+ anchor: Uint8Array,
671
+ projection: Uint8Array,
672
+ ): boolean {
673
+ const W = ctx.space.maxGroup;
674
+ const inst = frameSlots(ctx, query, anchor, 0);
675
+ if (inst === null) return false;
676
+ if (!dominates(inst.covered, query.length)) return false;
677
+ for (const slot of inst.slots) {
678
+ if (slot.kind !== "substitution") continue;
679
+ if (slot.qe - slot.qs < W || slot.filler.length < W) continue;
680
+ if (indexOf(projection, slot.filler, 0) < 0) continue;
681
+ const referent = query.subarray(slot.qs, slot.qe);
682
+ if (indexOf(projection, referent, 0) >= 0) continue;
683
+ // Gate 4: does the projection still speak about the query's FRAME? A run
684
+ // of one window anywhere outside the displaced position is enough — the
685
+ // answer is then about the thing that was asked, in the corpus's own
686
+ // wording. Sharing nothing means it is about something else.
687
+ const shared = alignRuns(ctx, query, projection).some((r) =>
688
+ r.qe - r.qs >= W && (r.qe <= slot.qs || r.qs >= slot.qe)
689
+ );
690
+ if (shared) continue;
691
+ return true;
692
+ }
693
+ return false;
694
+ }
695
+
615
696
  /** Whether every member is byte-distinct from the others. */
616
697
  export function distinct(items: readonly Uint8Array[]): boolean {
617
698
  for (let i = 0; i < items.length; i++) {
@@ -17,7 +17,12 @@ import type { MindContext } from "../types.js";
17
17
  import { gistOf, read, resolve } from "../primitives.js";
18
18
  import { bytesEqual, indexOf } from "../../bytes.js";
19
19
  import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
20
- import { follow, project, reverseContext } from "../match.js";
20
+ import {
21
+ follow,
22
+ project,
23
+ reverseContext,
24
+ voicesDisplacedFiller,
25
+ } from "../match.js";
21
26
  import { CONCEPT, STEP } from "../graph-search.js";
22
27
  import { unexplainedLabel } from "../rationale.js";
23
28
  import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
@@ -349,7 +354,28 @@ export async function recallByResonance(
349
354
  (dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))
350
355
  ) {
351
356
  const g = await project(ctx, forest[0].anchor, queryGist);
352
- // The anchor cleared the consensus floor, but the floor prices the
357
+ // THE ANCHOR'S OCCUPANT IS NOT THE ASKER'S. This tier grounds an anchor
358
+ // the climb elected, and voices that anchor's continuation. When the
359
+ // query is the same structure with one position filled differently, the
360
+ // continuation speaks the ANCHOR's occupant of that position — a fluent,
361
+ // specific, wrong answer (see voicesDisplacedFiller). Refusing here is
362
+ // silence, not a redirection: putting the asker's referent in its place
363
+ // is a stronger claim that needs the corpus's own carriage evidence, and
364
+ // that is `reference`'s job.
365
+ const anchorBytes = read(
366
+ ctx,
367
+ forest[0].anchor,
368
+ query.length * ctx.space.maxGroup + 1,
369
+ );
370
+ if (g !== null && voicesDisplacedFiller(ctx, query, anchorBytes, g)) {
371
+ ctx.trace?.step(
372
+ "displacedFiller",
373
+ [rItem(query, "query"), rNode(ctx, forest[0].anchor, "anchor")],
374
+ [rItem(g, "withheld")],
375
+ "refused — the anchor's continuation speaks the anchor's own " +
376
+ "occupant of a position this query fills differently",
377
+ );
378
+ } // The anchor cleared the consensus floor, but the floor prices the
353
379
  // ANCHOR's evidence, not the projection's: a junk attractor can clear
354
380
  // it and project a PIECE OF THE QUERY back at it (the observed
355
381
  // "buenos días in English" → "English" fragment). A projection that
@@ -357,7 +383,7 @@ export async function recallByResonance(
357
383
  // — never an answer (the same principle as `restates` above, extended
358
384
  // to fragments). Genuine anchor groundings — longer than the query,
359
385
  // or disjoint from it — pass untouched.
360
- if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
386
+ else if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
361
387
  return ground(
362
388
  g,
363
389
  "scaffolding-dominated query — ground the consensus-climb anchor",
@@ -403,6 +403,78 @@ test("a referent may not be lifted out of the engine's OWN reply", async () => {
403
403
  await m.store.close();
404
404
  });
405
405
 
406
+ // ── THE DISPLACED-FILLER GATE ──────────────────────────────────────────────
407
+ //
408
+ // Reference needs two instances before it will voice a binding: one instance
409
+ // agrees with nothing, so nothing distinguishes "Run gcc hello.c" (a carriage
410
+ // that follows the filename) from "hello.c needs -lm" (a fact about that one
411
+ // file). Abstaining there is right.
412
+ //
413
+ // What is NOT right is what used to happen next. Recall's
414
+ // scaffolding-dominated tier grounds the anchor the climb elected and voices
415
+ // that anchor's continuation — which speaks the ANCHOR's occupant of the very
416
+ // position the query fills differently. Measured on a one-example corpus,
417
+ // `How do I compile main.c?` answered `Run gcc hello.c`; on the trained
418
+ // 15.7M-node store, `How do you say 'flurbish' in French?` answers "the way to
419
+ // say hello is \"Bonjour\"". Fluent, specific, and about the wrong thing.
420
+ //
421
+ // The gate REFUSES ONLY. It never puts the asker's referent in the corpus's
422
+ // place — that is a stronger claim needing the corpus's own carriage evidence,
423
+ // which is what the licence above supplies. Where the corpus cannot license a
424
+ // binding, silence is the honest degradation.
425
+
426
+ test("a projection may not voice the ANCHOR's occupant of a displaced slot", async () => {
427
+ const m = await mindWith([
428
+ ["How do I compile hello.c?", "Run gcc hello.c"],
429
+ ["What is the capital of France?", "Paris"],
430
+ ]);
431
+ const out = clean((await m.respond("How do I compile main.c?")).bytes).trim();
432
+ assert.ok(
433
+ !out.includes("hello"),
434
+ `voiced the corpus's filename for the asker's file: ${JSON.stringify(out)}`,
435
+ );
436
+ assert.equal(out, "", "the honest degradation here is silence");
437
+ await m.store.close();
438
+ });
439
+
440
+ test("the gate is not the restated-fragment guard", async () => {
441
+ // The refused projection is not a piece of the QUERY — it passes the
442
+ // restatement guard cleanly — it is a piece of the ANCHOR the query
443
+ // displaced. Neither guard implies the other.
444
+ const m = await mindWith([
445
+ ["How do I compile hello.c?", "Run gcc hello.c"],
446
+ ["What is the capital of France?", "Paris"],
447
+ ]);
448
+ const q = "How do I compile main.c?";
449
+ assert.ok(
450
+ !q.includes("Run gcc hello.c"),
451
+ "fixture broken: the projection must NOT be a query fragment",
452
+ );
453
+ assert.equal(clean((await m.respond(q)).bytes).trim(), "");
454
+ await m.store.close();
455
+ });
456
+
457
+ test("a projection that carries the ASKER's own occupant is untouched", async () => {
458
+ // Voicing the anchor's filler AND the query's referent is what a licensed
459
+ // reference does; the gate must not touch it.
460
+ const m = await mindWith(CARRIED);
461
+ const r = await m.respond("How do I compile main.c?");
462
+ assert.equal(clean(r.bytes).trim(), "Run gcc main.c");
463
+ assert.equal(r.provenance, "reference");
464
+ await m.store.close();
465
+ });
466
+
467
+ test("a sub-quantum difference does not trip the gate", async () => {
468
+ // Below one river window byte overlap is chance, not a displaced slot.
469
+ const m = await mindWith([
470
+ ["What is the capital of France?", "Paris"],
471
+ ["Who wrote Hamlet?", "Shakespeare"],
472
+ ]);
473
+ const out = clean((await m.respond("What is the capital of Franc?")).bytes);
474
+ assert.match(out, /Paris/, `a near-identical query lost its answer: ${out}`);
475
+ await m.store.close();
476
+ });
477
+
406
478
  // ── NO HIJACK ──────────────────────────────────────────────────────────────
407
479
 
408
480
  test("a trained query is answered by its own edge, not by reference", async () => {