@hviana/sema 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/AGENTS.md +115 -53
  2. package/HOW_IT_WORKS.md +291 -184
  3. package/jsr.json +6 -0
  4. package/package.json +1 -1
  5. package/src/mind/bridge.ts +10 -104
  6. package/src/mind/match.ts +497 -7
  7. package/src/mind/{prefix-completion.ts → mechanisms/prefix-completion.ts} +66 -92
  8. package/src/mind/mechanisms/recall.ts +38 -129
  9. package/src/mind/mechanisms/reference.ts +343 -0
  10. package/src/mind/mind.ts +12 -8
  11. package/src/mind/pipeline-mechanism.ts +120 -3
  12. package/src/mind/pipeline.ts +16 -2
  13. package/src/mind/traverse.ts +92 -1
  14. package/src/store.ts +13 -4
  15. package/test/33-multi-candidate.test.mjs +21 -11
  16. package/test/70-prefix-completion.test.mjs +1 -1
  17. package/test/72-prefix-candidate-supply.test.mjs +7 -9
  18. package/test/74-prefix-trap-not-sprung-early.test.mjs +1 -1
  19. package/test/76-reference-binding.test.mjs +543 -0
  20. package/dist/example/demo.d.ts +0 -1
  21. package/dist/example/demo.js +0 -39
  22. package/dist/example/train_base.d.ts +0 -93
  23. package/dist/example/train_base.js +0 -2269
  24. package/dist/src/alphabet.d.ts +0 -7
  25. package/dist/src/alphabet.js +0 -33
  26. package/dist/src/alu/src/alu.d.ts +0 -185
  27. package/dist/src/alu/src/alu.js +0 -440
  28. package/dist/src/alu/src/expr.d.ts +0 -61
  29. package/dist/src/alu/src/expr.js +0 -318
  30. package/dist/src/alu/src/index.d.ts +0 -11
  31. package/dist/src/alu/src/index.js +0 -19
  32. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  33. package/dist/src/alu/src/kernel-arith.js +0 -264
  34. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  35. package/dist/src/alu/src/kernel-bits.js +0 -152
  36. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  37. package/dist/src/alu/src/kernel-logic.js +0 -60
  38. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  39. package/dist/src/alu/src/kernel-nd.js +0 -208
  40. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  41. package/dist/src/alu/src/kernel-numeric.js +0 -366
  42. package/dist/src/alu/src/operation.d.ts +0 -168
  43. package/dist/src/alu/src/operation.js +0 -189
  44. package/dist/src/alu/src/parser.d.ts +0 -221
  45. package/dist/src/alu/src/parser.js +0 -577
  46. package/dist/src/alu/src/resonance.d.ts +0 -55
  47. package/dist/src/alu/src/resonance.js +0 -126
  48. package/dist/src/alu/src/text.d.ts +0 -31
  49. package/dist/src/alu/src/text.js +0 -73
  50. package/dist/src/alu/src/value.d.ts +0 -109
  51. package/dist/src/alu/src/value.js +0 -300
  52. package/dist/src/alu/test/alu.test.d.ts +0 -1
  53. package/dist/src/alu/test/alu.test.js +0 -764
  54. package/dist/src/bytes.d.ts +0 -14
  55. package/dist/src/bytes.js +0 -59
  56. package/dist/src/canon.d.ts +0 -45
  57. package/dist/src/canon.js +0 -85
  58. package/dist/src/config.d.ts +0 -111
  59. package/dist/src/config.js +0 -91
  60. package/dist/src/derive/src/deduction.d.ts +0 -136
  61. package/dist/src/derive/src/deduction.js +0 -159
  62. package/dist/src/derive/src/index.d.ts +0 -8
  63. package/dist/src/derive/src/index.js +0 -11
  64. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  65. package/dist/src/derive/src/priority-queue.js +0 -73
  66. package/dist/src/derive/src/rewrite.d.ts +0 -56
  67. package/dist/src/derive/src/rewrite.js +0 -100
  68. package/dist/src/derive/src/trie.d.ts +0 -90
  69. package/dist/src/derive/src/trie.js +0 -217
  70. package/dist/src/derive/test/derive.test.d.ts +0 -1
  71. package/dist/src/derive/test/derive.test.js +0 -122
  72. package/dist/src/extension.d.ts +0 -37
  73. package/dist/src/extension.js +0 -7
  74. package/dist/src/geometry.d.ts +0 -233
  75. package/dist/src/geometry.js +0 -1089
  76. package/dist/src/index.d.ts +0 -17
  77. package/dist/src/index.js +0 -19
  78. package/dist/src/ingest-cache.d.ts +0 -41
  79. package/dist/src/ingest-cache.js +0 -165
  80. package/dist/src/meter.d.ts +0 -176
  81. package/dist/src/meter.js +0 -274
  82. package/dist/src/mind/articulation.d.ts +0 -6
  83. package/dist/src/mind/articulation.js +0 -99
  84. package/dist/src/mind/attention.d.ts +0 -430
  85. package/dist/src/mind/attention.js +0 -2418
  86. package/dist/src/mind/bridge.d.ts +0 -39
  87. package/dist/src/mind/bridge.js +0 -998
  88. package/dist/src/mind/canonical.d.ts +0 -34
  89. package/dist/src/mind/canonical.js +0 -93
  90. package/dist/src/mind/frame-filler.d.ts +0 -15
  91. package/dist/src/mind/frame-filler.js +0 -535
  92. package/dist/src/mind/graph-search.d.ts +0 -294
  93. package/dist/src/mind/graph-search.js +0 -996
  94. package/dist/src/mind/index.d.ts +0 -9
  95. package/dist/src/mind/index.js +0 -5
  96. package/dist/src/mind/junction.d.ts +0 -137
  97. package/dist/src/mind/junction.js +0 -358
  98. package/dist/src/mind/learning.d.ts +0 -75
  99. package/dist/src/mind/learning.js +0 -265
  100. package/dist/src/mind/match.d.ts +0 -192
  101. package/dist/src/mind/match.js +0 -666
  102. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  103. package/dist/src/mind/mechanisms/alu.js +0 -36
  104. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  105. package/dist/src/mind/mechanisms/cast.js +0 -1186
  106. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  107. package/dist/src/mind/mechanisms/confluence.js +0 -271
  108. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  109. package/dist/src/mind/mechanisms/cover.js +0 -232
  110. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  111. package/dist/src/mind/mechanisms/extraction.js +0 -327
  112. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  113. package/dist/src/mind/mechanisms/recall.js +0 -558
  114. package/dist/src/mind/mind.d.ts +0 -386
  115. package/dist/src/mind/mind.js +0 -754
  116. package/dist/src/mind/pipeline-mechanism.d.ts +0 -183
  117. package/dist/src/mind/pipeline-mechanism.js +0 -521
  118. package/dist/src/mind/pipeline.d.ts +0 -49
  119. package/dist/src/mind/pipeline.js +0 -331
  120. package/dist/src/mind/prefix-completion.d.ts +0 -59
  121. package/dist/src/mind/prefix-completion.js +0 -270
  122. package/dist/src/mind/primitives.d.ts +0 -85
  123. package/dist/src/mind/primitives.js +0 -333
  124. package/dist/src/mind/rationale.d.ts +0 -139
  125. package/dist/src/mind/rationale.js +0 -163
  126. package/dist/src/mind/reasoning.d.ts +0 -42
  127. package/dist/src/mind/reasoning.js +0 -305
  128. package/dist/src/mind/recognition.d.ts +0 -20
  129. package/dist/src/mind/recognition.js +0 -696
  130. package/dist/src/mind/resonance.d.ts +0 -35
  131. package/dist/src/mind/resonance.js +0 -365
  132. package/dist/src/mind/trace.d.ts +0 -15
  133. package/dist/src/mind/trace.js +0 -73
  134. package/dist/src/mind/traverse.d.ts +0 -158
  135. package/dist/src/mind/traverse.js +0 -716
  136. package/dist/src/mind/types.d.ts +0 -418
  137. package/dist/src/mind/types.js +0 -179
  138. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  139. package/dist/src/rabitq-ivf/src/database.js +0 -201
  140. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  141. package/dist/src/rabitq-ivf/src/index.js +0 -4
  142. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  143. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  144. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  145. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  146. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  147. package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
  148. package/dist/src/sema.d.ts +0 -41
  149. package/dist/src/sema.js +0 -77
  150. package/dist/src/store-sqlite.d.ts +0 -184
  151. package/dist/src/store-sqlite.js +0 -942
  152. package/dist/src/store.d.ts +0 -762
  153. package/dist/src/store.js +0 -1823
  154. package/dist/src/vec.d.ts +0 -31
  155. package/dist/src/vec.js +0 -109
  156. package/src/mind/frame-filler.ts +0 -604
  157. package/test/69-frame-filler.test.mjs +0 -115
@@ -0,0 +1,543 @@
1
+ // 76-reference-binding.test.mjs — REFERENCE: voicing a slot with the
2
+ // context's own bytes.
3
+ //
4
+ // THE GAP THIS CLOSES. Sema is otherwise a fully GROUND system: every item of
5
+ // the deduction system, every matcher and every gate compares ground bytes.
6
+ // Nothing represents a POSITION whose occupant comes from the context rather
7
+ // than the corpus, so no mechanism can tell
8
+ //
9
+ // "the corpus does not explain these bytes" (PASS — refuse)
10
+ //
11
+ // apart from
12
+ //
13
+ // "these bytes occupy a place the corpus keeps open" (bind).
14
+ //
15
+ // Both arrive as unaligned residue. Asked `How do I compile main.c?` against a
16
+ // corpus that knows hello.c / server.c / parser.c, the engine returned SILENCE
17
+ // — while the corpus attests, three times over, exactly what to do with a name
18
+ // in that slot.
19
+ //
20
+ // WHY IT IS NOT A SUBSTITUTION. bridge.ts refuses this shape and is right to:
21
+ // `attestedQ` demands the query-side span be corpus-attested, because a
22
+ // substitution asserts EQUIVALENCE, and nothing can corroborate an equivalence
23
+ // claim about bytes the corpus has never seen. A reference asserts no
24
+ // equivalence. It asserts POSITION, and the bytes come from the asker, so
25
+ // voicing them cannot fabricate corpus knowledge. The only thing that can be
26
+ // fabricated is the RELATION claimed about them — the licence's whole subject.
27
+ //
28
+ // THE LICENCE, IN ONE BYTE PREDICATE (match.ts carriesFillers). For two
29
+ // instances i, j of one frame:
30
+ //
31
+ // substituteAll(cont_i, fillers_i -> fillers_j) == cont_j
32
+ //
33
+ // If it holds, the corpus attests byte-exactly that the continuation is a
34
+ // function of the fillers and nothing else. No threshold, no similarity, no
35
+ // new constant. Three readings, all pinned below:
36
+ //
37
+ // CARRIED the continuation quotes the fillers -> splice the referents
38
+ // ABSORBED the continuation is constant -> the same test, vacuously
39
+ // REFUSED the continuation carries filler-DEPENDENT content
40
+ //
41
+ // THE MEASURED FABRICATION THIS FORBIDS. On the trained 15.7M-node store,
42
+ // `What is the capital of Zamunda?` resonates to a PURE cohort — every one of
43
+ // the top 14 hits an instance of `What is the capital of X?` — with an
44
+ // unambiguous slot. Every structural gate passes; only the licence refuses,
45
+ // on `replace("Tokyo", "Japan" -> "France") != "Paris"`. That refusal is the
46
+ // only thing between this mechanism and invented capitals, so the FABRICATION
47
+ // tests here are not decoration.
48
+ //
49
+ // THE SLOT IS AN ALIGNMENT GAP, NOT "THE NOVEL PART". Two readings of novelty
50
+ // were measured and both fail as a delimiter:
51
+ // * "positions covered by no stored W-window" only BRACKETS the slot — it is
52
+ // dilated by up to W-1 at each edge (measured: `main.c?` for `main.c`), so
53
+ // splicing it carries the query's punctuation into the answer;
54
+ // * at corpus scale it does not fire at all — at 325,615 contexts every
55
+ // window of `Zamunda` and `flurbish` is already attested somewhere.
56
+ // The gap between the query and an instance of its frame IS the varying slot,
57
+ // contracted to its core: the mirror of the bridge, which EXPANDS a gap until
58
+ // the query side attests.
59
+ import { test } from "node:test";
60
+ import assert from "node:assert/strict";
61
+ import { Mind } from "../dist/src/index.js";
62
+ import { SQliteStore } from "../dist/src/store-sqlite.js";
63
+ import { Precomputed } from "../dist/src/mind/pipeline-mechanism.js";
64
+ import { recognise } from "../dist/src/mind/recognition.js";
65
+ import { gistOf } from "../dist/src/mind/primitives.js";
66
+ import { bindReference } from "../dist/src/mind/mechanisms/reference.js";
67
+ import { frameSlots } from "../dist/src/mind/match.js";
68
+
69
+ const dec = new TextDecoder();
70
+ const clean = (b) => dec.decode((b ?? new Uint8Array()).filter((x) => x !== 0));
71
+
72
+ const mindWith = async (pairs) => {
73
+ const m = new Mind({ seed: 7, store: new SQliteStore({ path: ":memory:" }) });
74
+ await m.ingest(pairs);
75
+ return m;
76
+ };
77
+
78
+ /** The frame whose continuation QUOTES its filler. */
79
+ const CARRIED = [
80
+ ["How do I compile hello.c?", "Run gcc hello.c"],
81
+ ["How do I compile server.c?", "Run gcc server.c"],
82
+ ["How do I compile parser.c?", "Run gcc parser.c"],
83
+ ];
84
+
85
+ /** The frame whose continuation is CONSTANT across distinct fillers. */
86
+ const ABSORBED = [
87
+ ["What is the type of alpha?", "It is a variable."],
88
+ ["What is the type of beta?", "It is a variable."],
89
+ ["What is the type of delta?", "It is a variable."],
90
+ ];
91
+
92
+ /** The frame whose continuation depends on WHICH filler — no licence. */
93
+ const FABRICATION = [
94
+ ["What is the capital of France?", "Paris"],
95
+ ["What is the capital of Japan?", "Tokyo"],
96
+ ["What is the capital of Peru?", "Lima"],
97
+ ];
98
+
99
+ /** Two slots, both carried. */
100
+ const TWO_SLOT = [
101
+ ["Copy alpha.txt to beta.txt please", "cp alpha.txt beta.txt"],
102
+ ["Copy gamma.txt to delta.txt please", "cp gamma.txt delta.txt"],
103
+ ["Copy sigma.txt to omega.txt please", "cp sigma.txt omega.txt"],
104
+ ];
105
+
106
+ // ── THE SHARED REPRESENTATION ──────────────────────────────────────────────
107
+ //
108
+ // The slot notion is machinery, not one mechanism's private reading. It lives
109
+ // on Precomputed, reachable by ANY mechanism, and it reports an INVENTORY
110
+ // rather than electing a frame — a slot is a property of a PAIRING, not of the
111
+ // query, and committing to one reading in the shared container would push
112
+ // whichever consumer asked first onto every other.
113
+
114
+ test("the frame inventory REPORTS without judging", async () => {
115
+ // The shared reading must be usable by consumers other than this one, and
116
+ // that is not a matter of where the code sits — it is a matter of what the
117
+ // code hides. Four real pairings, only ONE of which reference can voice:
118
+ // every one must come back, correctly typed, so another consumer can apply
119
+ // its own reading.
120
+ //
121
+ // Measured before the gates were moved out of frameSlots: three of these
122
+ // four were reported as NOTHING, including a definite description standing
123
+ // where a proper noun stands — the shape the frame filler exists for. A
124
+ // shared layer with one usable consumer is private code at a public address.
125
+ const m = await mindWith([
126
+ ["What is the capital of France?", "Paris is the capital of France."],
127
+ ]);
128
+ const enc = (t) => new TextEncoder().encode(t);
129
+ const q = enc(
130
+ "What is the capital of the country where the " +
131
+ "Eiffel Tower is?",
132
+ );
133
+ const cand = enc("What is the capital of France?");
134
+
135
+ // 1. A SUBSTITUTION reference cannot voice: the varying part dwarfs the
136
+ // frame (covered 23 of 61), so `voiceable` refuses it — but the matcher
137
+ // must still report it.
138
+ const inst = frameSlots(m, q, cand, 1);
139
+ assert.ok(inst !== null, "the pairing was hidden");
140
+ assert.equal(inst.slots.length, 1);
141
+ assert.equal(inst.slots[0].kind, "substitution");
142
+ assert.equal(
143
+ clean(q.subarray(inst.slots[0].qs, inst.slots[0].qe)),
144
+ "the country where the Eiffel Tower is",
145
+ );
146
+ assert.equal(clean(inst.slots[0].filler), "France");
147
+ assert.equal(inst.covered, 23, "coverage must be reported, not judged");
148
+
149
+ // 2. An INSERTION — a real variation, and not a slot anything can carry.
150
+ const ins = frameSlots(
151
+ m,
152
+ enc("What is the capital of France, really?"),
153
+ cand,
154
+ 1,
155
+ );
156
+ assert.equal(ins.slots[0].kind, "insertion");
157
+ assert.equal(clean(new Uint8Array(ins.slots[0].filler)), "");
158
+
159
+ // 3. A DELETION, likewise.
160
+ const del = frameSlots(m, enc("What is the capital of Fran?"), cand, 1);
161
+ assert.equal(del.slots[0].kind, "deletion");
162
+ assert.equal(clean(del.slots[0].filler), "ce");
163
+
164
+ await m.store.close();
165
+ });
166
+
167
+ test("the inventory is shared, lazy, and elects no frame", async () => {
168
+ const m = await mindWith(CARRIED);
169
+ const q = new TextEncoder().encode("How do I compile main.c?");
170
+ m.beginResponse(undefined, null);
171
+ try {
172
+ m._edgeGuide = gistOf(m, q);
173
+ const pre = new Precomputed(m, q, recognise(m, q), [], m._edgeGuide);
174
+ const inventory = await pre.frames();
175
+ assert.ok(inventory.length >= 2, "no instances reported");
176
+ for (const inst of inventory) {
177
+ assert.ok(!("chosen" in inst), "the inventory must not elect");
178
+ assert.equal(typeof inst.covered, "number");
179
+ }
180
+ assert.equal(
181
+ new Set(inventory.map((i) => i.id)).size,
182
+ inventory.length,
183
+ "instances must stay distinct so a consumer can group them",
184
+ );
185
+ // Computed once and shared, not per-consumer.
186
+ assert.equal(await pre.frames(), inventory);
187
+ } finally {
188
+ m.endResponse(q.length);
189
+ }
190
+ await m.store.close();
191
+ });
192
+
193
+ // ── THE NEED ───────────────────────────────────────────────────────────────
194
+
195
+ test("CARRIED: the asker's name is voiced through the frame's own answer", async () => {
196
+ const m = await mindWith(CARRIED);
197
+ const r = await m.respond("How do I compile main.c?");
198
+ assert.equal(clean(r.bytes).trim(), "Run gcc main.c");
199
+ assert.equal(r.provenance, "reference");
200
+ await m.store.close();
201
+ });
202
+
203
+ test("CARRIED: the answer voices the ASKER's bytes, never the corpus's", async () => {
204
+ // The regression that matters most. The engine's nearest behaviour on this
205
+ // shape is to voice the CORPUS's filler — a confident misreference, worse
206
+ // than silence. Measured live on the trained store: `How do you say
207
+ // 'flurbish' in French?` answers "the way to say hello is \"Bonjour\"".
208
+ const m = await mindWith(CARRIED);
209
+ const out = clean((await m.respond("How do I compile main.c?")).bytes);
210
+ assert.match(out, /main\.c/);
211
+ for (const foreign of ["hello", "server", "parser"]) {
212
+ assert.ok(
213
+ !out.includes(foreign),
214
+ `answer named the corpus's filler ${foreign}: ${JSON.stringify(out)}`,
215
+ );
216
+ }
217
+ await m.store.close();
218
+ });
219
+
220
+ test("ABSORBED: a filler-independent answer is licensed by the same test", async () => {
221
+ const m = await mindWith(ABSORBED);
222
+ const r = await m.respond("What is the type of gamma?");
223
+ assert.equal(clean(r.bytes).trim(), "It is a variable.");
224
+ assert.equal(r.provenance, "reference");
225
+ await m.store.close();
226
+ });
227
+
228
+ test("the carriage may be structural, not just a copy", async () => {
229
+ // With the slot contracted to its varying core the filler is the BASENAME,
230
+ // carried to two places in the answer. Refusing this would refuse evidence
231
+ // the corpus actually holds.
232
+ const m = await mindWith([
233
+ ["How do I compile hello.c?", "Run gcc hello.c -o hello"],
234
+ ["How do I compile server.c?", "Run gcc server.c -o server"],
235
+ ["How do I compile parser.c?", "Run gcc parser.c -o parser"],
236
+ ]);
237
+ const r = await m.respond("How do I compile main.c?");
238
+ assert.equal(clean(r.bytes).trim(), "Run gcc main.c -o main");
239
+ await m.store.close();
240
+ });
241
+
242
+ // ── SEVERAL REFERENTS AT ONCE ──────────────────────────────────────────────
243
+ //
244
+ // A query may name more than one new thing, and a frame with two slots is not
245
+ // two frames. The licence generalises without weakening because it tests the
246
+ // WHOLE substitution at once.
247
+
248
+ test("TWO referents are bound together", async () => {
249
+ const m = await mindWith(TWO_SLOT);
250
+ const r = await m.respond("Copy input.txt to output.txt please");
251
+ assert.equal(clean(r.bytes).trim(), "cp input.txt output.txt");
252
+ assert.equal(r.provenance, "reference");
253
+ await m.store.close();
254
+ });
255
+
256
+ test("THREE referents, one of them carried to two places", async () => {
257
+ const m = await mindWith([
258
+ [
259
+ "Move alpha.txt from aaaa to bbbb now",
260
+ "mv aaaa/alpha.txt bbbb/alpha.txt",
261
+ ],
262
+ [
263
+ "Move gamma.txt from cccc to dddd now",
264
+ "mv cccc/gamma.txt dddd/gamma.txt",
265
+ ],
266
+ [
267
+ "Move sigma.txt from eeee to ffff now",
268
+ "mv eeee/sigma.txt ffff/sigma.txt",
269
+ ],
270
+ ]);
271
+ const r = await m.respond("Move input.txt from gggg to hhhh now");
272
+ assert.equal(clean(r.bytes).trim(), "mv gggg/input.txt hhhh/input.txt");
273
+ await m.store.close();
274
+ });
275
+
276
+ test("MULTI-SLOT licence: inventing around one slot refuses the whole binding", async () => {
277
+ // The command tracks both fillers, but the warning COUNT tracks neither — it
278
+ // is knowledge about the specific pair, which the store cannot have for a new
279
+ // one. One slot behaving is not a licence.
280
+ const m = await mindWith([
281
+ ["Compile hello.c with clang please", "clang hello.c gives 3 warnings"],
282
+ ["Compile server.c with rustc please", "rustc server.c gives 7 warnings"],
283
+ ["Compile parser.c with swiftc please", "swiftc parser.c gives 1 warnings"],
284
+ ]);
285
+ const r = await m.respond("Compile main.c with zigcc please");
286
+ assert.notEqual(r.provenance, "reference");
287
+ assert.ok(
288
+ !/\d+ warnings/.test(clean(r.bytes)),
289
+ `invented a warning count: ${JSON.stringify(clean(r.bytes))}`,
290
+ );
291
+ await m.store.close();
292
+ });
293
+
294
+ // ── THE LICENCE: what must NEVER be voiced ─────────────────────────────────
295
+
296
+ test("FABRICATION: a filler-DEPENDENT answer is refused", async () => {
297
+ const m = await mindWith(FABRICATION);
298
+ const r = await m.respond("What is the capital of Zamunda?");
299
+ const out = clean(r.bytes).trim();
300
+ assert.notEqual(r.provenance, "reference");
301
+ for (const city of ["Paris", "Tokyo", "Lima"]) {
302
+ assert.ok(
303
+ !out.includes(city),
304
+ `invented a capital for a place the corpus never saw: ${
305
+ JSON.stringify(out)
306
+ }`,
307
+ );
308
+ }
309
+ await m.store.close();
310
+ });
311
+
312
+ test("INCONSISTENT CARRIAGE: one instance breaking the pattern refuses all", async () => {
313
+ const m = await mindWith([
314
+ ["How do I compile hello.c?", "Run gcc hello.c -o out"],
315
+ ["How do I compile server.c?", "Run gcc server.c -o server"],
316
+ ["How do I compile parser.c?", "Run gcc parser.c -o parser"],
317
+ ]);
318
+ const r = await m.respond("How do I compile main.c?");
319
+ assert.notEqual(r.provenance, "reference");
320
+ assert.ok(
321
+ !/main\.c -o (main|out|server|parser)/.test(clean(r.bytes)),
322
+ `voiced an unattested carriage: ${JSON.stringify(clean(r.bytes))}`,
323
+ );
324
+ await m.store.close();
325
+ });
326
+
327
+ test("ONE INSTANCE: agreement needs two — a lone exemplar licenses nothing", async () => {
328
+ const m = await mindWith([
329
+ ["How do I compile hello.c?", "Run gcc hello.c"],
330
+ ["What is the capital of France?", "Paris"],
331
+ ]);
332
+ const r = await m.respond("How do I compile main.c?");
333
+ assert.notEqual(r.provenance, "reference");
334
+ await m.store.close();
335
+ });
336
+
337
+ test("the window floor applies PER SLOT, not to the query", async () => {
338
+ // `gcc` / `zig` are three bytes — under one river window, where byte overlap
339
+ // is chance. The first slot is a good referent; the second is not, and one
340
+ // bad slot refuses the binding rather than being dropped.
341
+ const m = await mindWith([
342
+ ["How do I compile hello.c with gcc?", "Run gcc hello.c"],
343
+ ["How do I compile server.c with tcc?", "Run tcc server.c"],
344
+ ["How do I compile parser.c with zcc?", "Run zcc parser.c"],
345
+ ]);
346
+ const r = await m.respond("How do I compile main.c with zig?");
347
+ assert.notEqual(r.provenance, "reference");
348
+ await m.store.close();
349
+ });
350
+
351
+ test("two slots naming the SAME bytes are ambiguous, not a binding", async () => {
352
+ // Which occurrence in the answer stands for which slot? Nothing says.
353
+ const m = await mindWith(TWO_SLOT);
354
+ const r = await m.respond("Copy input.txt to input.txt please");
355
+ assert.notEqual(r.provenance, "reference");
356
+ await m.store.close();
357
+ });
358
+
359
+ // ── THE FLOW ───────────────────────────────────────────────────────────────
360
+
361
+ test("post-grounding does not pivot PAST a binding", async () => {
362
+ // A bound answer is a byte string this mechanism CONSTRUCTED; the corpus
363
+ // never said it, so pivoting through it treats the engine's own construction
364
+ // as a trained fact — and here it also throws the answer away.
365
+ //
366
+ // Measured before `complete`: this answered "then execute ./a.out" — the
367
+ // referent gone AND the question unanswered.
368
+ const m = await mindWith([
369
+ ...CARRIED,
370
+ ["Run gcc main.c", "then execute ./a.out"],
371
+ ]);
372
+ const r = await m.respond("How do I compile main.c?");
373
+ assert.equal(clean(r.bytes).trim(), "Run gcc main.c");
374
+ assert.equal(r.provenance, "reference");
375
+ await m.store.close();
376
+ });
377
+
378
+ test("a referent may not be lifted out of the engine's OWN reply", async () => {
379
+ // Quoting a completed answer back as a referent launders the engine's output
380
+ // into evidence — the same rule the weave applies when it aligns only the
381
+ // asker's stream. Driven at the guard itself: the same query binds or
382
+ // refuses purely on whether the SLOT falls inside an answered span, so the
383
+ // control proves the refusal is the guard's doing and not an accident.
384
+ const m = await mindWith(CARRIED);
385
+ const q = new TextEncoder().encode("How do I compile main.c?");
386
+ const bind = async (answeredSpans) => {
387
+ m.beginResponse(undefined, null);
388
+ try {
389
+ m._edgeGuide = gistOf(m, q);
390
+ m.answeredSpans = answeredSpans;
391
+ const pre = new Precomputed(m, q, recognise(m, q), [], m._edgeGuide);
392
+ return await bindReference(m, q, pre);
393
+ } finally {
394
+ m.answeredSpans = [];
395
+ m.endResponse(q.length);
396
+ }
397
+ };
398
+ // The slot is "main" at 17-21 (see the inventory test above).
399
+ assert.equal(await bind([[16, 24]]), null, "bound out of its own reply");
400
+ const control = await bind([[0, 4]]);
401
+ assert.ok(control !== null, "the control did not bind");
402
+ assert.equal(clean(control.bytes).trim(), "Run gcc main.c");
403
+ await m.store.close();
404
+ });
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
+
478
+ // ── NO HIJACK ──────────────────────────────────────────────────────────────
479
+
480
+ test("a trained query is answered by its own edge, not by reference", async () => {
481
+ const m = await mindWith(CARRIED);
482
+ const r = await m.respond("How do I compile server.c?");
483
+ assert.equal(clean(r.bytes).trim(), "Run gcc server.c");
484
+ assert.notEqual(r.provenance, "reference");
485
+ await m.store.close();
486
+ });
487
+
488
+ test("honest silence survives: nothing relates, nothing is bound", async () => {
489
+ const m = await mindWith(CARRIED);
490
+ for (const q of ["xyzzy plugh quux baz?", "qq8f3kz9 vv2m1x7w?"]) {
491
+ const r = await m.respond(q);
492
+ assert.equal(clean(r.bytes).trim(), "", `expected silence for ${q}`);
493
+ }
494
+ await m.store.close();
495
+ });
496
+
497
+ test("the binding is deterministic", async () => {
498
+ const m = await mindWith(CARRIED);
499
+ const a = (await m.respond("How do I compile main.c?")).bytes;
500
+ const b = (await m.respond("How do I compile main.c?")).bytes;
501
+ assert.deepEqual([...a], [...b]);
502
+ await m.store.close();
503
+ });
504
+
505
+ // ── GENERIC: bytes, not text ───────────────────────────────────────────────
506
+
507
+ test("reference is a BYTE mechanism — no text, no canon, no character class", async () => {
508
+ // The same shape in a modality with no words, no case and no punctuation: a
509
+ // fixed byte frame with a varying region, carried into the reply. Nothing in
510
+ // the mechanism may consult what a byte MEANS.
511
+ const F = (region) =>
512
+ Uint8Array.from([0x01, 0x02, 0x03, 0x04, ...region, 0xfe, 0xff]);
513
+ const A = (region) => Uint8Array.from([0x10, 0x11, 0x12, 0x13, ...region]);
514
+ const m = new Mind({ seed: 7, store: new SQliteStore({ path: ":memory:" }) });
515
+ await m.ingest([
516
+ [F([0x41, 0x42, 0x43, 0x44, 0x45]), A([0x41, 0x42, 0x43, 0x44, 0x45])],
517
+ [F([0x51, 0x52, 0x53, 0x54, 0x55]), A([0x51, 0x52, 0x53, 0x54, 0x55])],
518
+ [F([0x61, 0x62, 0x63, 0x64, 0x65]), A([0x61, 0x62, 0x63, 0x64, 0x65])],
519
+ ]);
520
+ const novel = [0x71, 0x72, 0x73, 0x74, 0x75];
521
+ const r = await m.respond(F(novel));
522
+ assert.deepEqual([...r.bytes], [...A(novel)]);
523
+ assert.equal(r.provenance, "reference");
524
+ await m.store.close();
525
+ });
526
+
527
+ // ── OPTIMIZED ──────────────────────────────────────────────────────────────
528
+
529
+ test("the frame inventory and recall share ONE resonance call", async () => {
530
+ // Precomputed.resonance() is the response's one top-k read. Before it
531
+ // existed, a query reaching both paid two identical ANN queries.
532
+ const m = new Mind({
533
+ seed: 7,
534
+ store: new SQliteStore({ path: ":memory:" }),
535
+ profile: true,
536
+ });
537
+ await m.ingest(FABRICATION); // refuses, so BOTH mechanisms run to the end
538
+ await m.respond("What is the capital of Zamunda?");
539
+ const phase = m.lastCost.phases["resonance"];
540
+ assert.ok(phase, "the shared resonance phase never ran");
541
+ assert.equal(phase.calls, 1, "the shared resonance ran more than once");
542
+ await m.store.close();
543
+ });
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- // demo.ts — one short session that drives the WHOLE pipeline from one memory.
2
- //
3
- // We give Sema a handful of plain notes, then ask things that no single note
4
- // answers. The headline query is the third one: from three worked examples Sema
5
- // learns the shape of "X was painted by Y", lifts the painter out of a sentence
6
- // it has NEVER seen, and then — in the same pass — reasons forward to a separate
7
- // fact about that painter. The reply contains no word from the question. That is
8
- // retrieval, generalization, and reasoning composing as a single act, with every
9
- // step traceable back to the notes behind it.
10
- import { Mind } from "../src/index.js";
11
- import { SQliteStore } from "../src/store-sqlite.js";
12
- async function main() {
13
- const mind = new Mind({ store: new SQliteStore({ path: ":memory:" }) });
14
- const ask = async (q) => (await mind.respondText(q)).trim();
15
- // ── Jot down what we know. Each line is just (context → what follows). ──
16
- await mind.ingest([
17
- // One relation, shown three times — a pattern taught purely by example:
18
- ["The Mona Lisa was painted by Leonardo da Vinci.", "Leonardo da Vinci"],
19
- ["The Starry Night was painted by Vincent van Gogh.", "Vincent van Gogh"],
20
- [
21
- "The Night Watch was painted by Rembrandt van Rijn.",
22
- "Rembrandt van Rijn",
23
- ],
24
- // One stray fact, keyed on a name none of the examples mention:
25
- ["Pablo Picasso", "Pablo Picasso co-founded the Cubist movement"],
26
- ]);
27
- // 1) GENERALIZE — apply the learned pattern to an unseen sentence and read out
28
- // the painter. "Pablo Picasso" was never given as an answer; Sema locates it
29
- // by analogy to the three examples.
30
- console.log(await ask("The Weeping Woman was painted by Pablo Picasso."));
31
- // → "Pablo Picasso co-founded the Cubist movement"
32
- // …and, having found the painter, it KEEPS GOING: the name bridges into the
33
- // one fact it holds about him. The answer appears in no word of the question.
34
- // 2) COMPUTE — exact arithmetic, grounded right where the notes go silent.
35
- console.log(await ask("a museum charges 12*4 for a family ticket"));
36
- // → "48"
37
- await mind.store.close();
38
- }
39
- main();
@@ -1,93 +0,0 @@
1
- export interface Episode {
2
- context: string;
3
- continuation: string;
4
- }
5
- export type TrainingItem = string | Episode;
6
- /** Dedup + trim a concept's items: drop empty/degenerate pairs and exact
7
- * repeats so a concept never deposits the same form twice. */
8
- export declare function refineItems(items: TrainingItem[]): TrainingItem[];
9
- /** One normalized SmolSent row. */
10
- export interface SmolSentRow {
11
- src: string;
12
- trg: string;
13
- sl: string;
14
- tl: string;
15
- }
16
- /** Normalize a raw datasets-server row into a SmolSentRow, or null when it lacks
17
- * both sides or a side is implausibly large (a dump, not a sentence). */
18
- export declare function toSmolSentRow(row: unknown): SmolSentRow | null;
19
- /** Translate ONE SmolSent pair into SEMA facts: the two sentences are one
20
- * meaning in two languages, so bind them BOTH ways. refineItems drops the
21
- * degenerate case where src === trg. */
22
- export declare function smolSentRowToItems(row: SmolSentRow): TrainingItem[];
23
- /** One normalized Aya row. */
24
- export interface AyaRow {
25
- inputs: string;
26
- targets: string;
27
- language: string;
28
- }
29
- /** Normalize a raw datasets-server row object into an AyaRow, or null when it
30
- * lacks a usable prompt/answer or a field is implausibly large (a dump, not a
31
- * cognitive example). Trims surrounding whitespace; keeps inner text verbatim
32
- * (human prose, possibly multi-paragraph). */
33
- export declare function toAyaRow(row: unknown): AyaRow | null;
34
- /** Translate ONE Aya row into SEMA training items. A row is a single human
35
- * (question → answer) exchange — exactly one FACT, the (inputs → targets) edge.
36
- * No standalone-answer experience and no one-exchange "cumulative" walk: a lone
37
- * Q→A is not multi-turn, and both would only replicate the same edge. */
38
- export declare function ayaRowToItems(row: AyaRow): TrainingItem[];
39
- /** A single oasst2 message node (the fields we use; the tree nests via replies). */
40
- interface OasstNode {
41
- role?: string;
42
- text?: string;
43
- rank?: number | null;
44
- deleted?: boolean;
45
- replies?: OasstNode[];
46
- }
47
- /** One conversational turn extracted from a tree. */
48
- export interface OasstTurn {
49
- role: string;
50
- text: string;
51
- }
52
- /** Collapse a conversation tree to ONE linear path: at each node, descend into
53
- * its best-ranked, non-deleted reply (rank 0 preferred; unranked sorts last).
54
- * Returns the ordered turns (already strictly alternating in this corpus). */
55
- export declare function bestOasstPath(root: OasstNode): OasstTurn[];
56
- /** Translate ONE multi-turn oasst2 conversation into SEMA training items.
57
- *
58
- * This is the ONE stage where cumulative continuous context is truly necessary:
59
- * the data is a real multi-turn dialogue, and what must be learned is how each
60
- * turn follows from the WHOLE conversation so far — not from the previous turn
61
- * alone. The conversation is emitted ONLY as the accumulated walk; standalone
62
- * turn experiences and local adjacent-pair facts are NOT emitted (they are
63
- * subsumed by it and would merely replicate the content).
64
- *
65
- * The walk is the pattern proven in test/13-conversation.test.mjs
66
- * ("teachConversation"): each turn is the continuation of all prior turns,
67
- * with BARE turn text — NO "User:/Assistant:" labels. The SHAPE is identical
68
- * (cumulative context → next turn); the join string is not, and does not need
69
- * to be — that file joins with nothing and this corpus joins with "\n" (see
70
- * `accumulate`). Saying "byte-for-byte", as this comment used to, invites the
71
- * reading that the two must agree on a separator. They must not agree,
72
- * because there is nothing to agree about: turn boundaries are offsets, and
73
- * the join string is just corpus text. Roles already
74
- * alternate by position in an oasst2 best-path (the root is a prompter), so a
75
- * label adds nothing the position does not, while a clean continuation matches
76
- * the test's recall (predictNext queries bare prior turns) and lets a turn share
77
- * its gist with the same text elsewhere (e.g. an Aya question stored bare).
78
- *
79
- * Returns [] for a conversation below the multi-turn threshold, so callers can
80
- * simply skip empties. */
81
- export declare function oasstConversationToItems(turns: OasstTurn[]): TrainingItem[];
82
- /** One normalized General-Knowledge row. */
83
- export interface GenKnowRow {
84
- question: string;
85
- answer: string;
86
- }
87
- /** Normalize a raw datasets-server row into a GenKnowRow, or null when it lacks
88
- * a usable question/answer or a side is implausibly large (corruption). */
89
- export declare function toGenKnowRow(row: unknown): GenKnowRow | null;
90
- /** Translate ONE General-Knowledge row into SEMA items: exactly one
91
- * (question → answer) FACT. refineItems drops a degenerate question === answer. */
92
- export declare function genKnowRowToItems(row: GenKnowRow): TrainingItem[];
93
- export {};