@hviana/sema 0.4.7 → 0.5.1
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 +290 -77
- package/HOW_IT_WORKS.md +2170 -735
- package/dist/example/train_base.d.ts +9 -3
- package/dist/example/train_base.js +21 -4
- package/dist/src/canon.d.ts +19 -0
- package/dist/src/canon.js +28 -0
- package/dist/src/geometry.d.ts +52 -0
- package/dist/src/geometry.js +87 -1
- package/dist/src/mind/attention.d.ts +15 -10
- package/dist/src/mind/attention.js +15 -10
- package/dist/src/mind/bridge.js +27 -1
- package/dist/src/mind/frame-filler.d.ts +15 -0
- package/dist/src/mind/frame-filler.js +535 -0
- package/dist/src/mind/learning.js +6 -11
- package/dist/src/mind/mechanisms/cast.js +72 -2
- package/dist/src/mind/mechanisms/cover.js +6 -1
- package/dist/src/mind/mechanisms/extraction.js +27 -0
- package/dist/src/mind/mechanisms/recall.js +214 -34
- package/dist/src/mind/mind.d.ts +52 -3
- package/dist/src/mind/mind.js +140 -12
- package/dist/src/mind/pipeline-mechanism.d.ts +7 -0
- package/dist/src/mind/pipeline.js +29 -1
- package/dist/src/mind/prefix-completion.d.ts +59 -0
- package/dist/src/mind/prefix-completion.js +270 -0
- package/dist/src/mind/primitives.d.ts +29 -10
- package/dist/src/mind/primitives.js +98 -71
- package/dist/src/mind/recognition.js +153 -26
- package/dist/src/mind/traverse.d.ts +32 -0
- package/dist/src/mind/traverse.js +52 -0
- package/dist/src/mind/types.d.ts +61 -18
- package/dist/src/mind/types.js +68 -19
- package/dist/src/store.d.ts +21 -0
- package/dist/src/store.js +21 -0
- package/example/train_base.ts +21 -4
- package/package.json +1 -1
- package/src/canon.ts +28 -0
- package/src/geometry.ts +100 -1
- package/src/mind/attention.ts +15 -10
- package/src/mind/bridge.ts +34 -0
- package/src/mind/frame-filler.ts +604 -0
- package/src/mind/learning.ts +5 -9
- package/src/mind/mechanisms/cast.ts +70 -2
- package/src/mind/mechanisms/cover.ts +6 -1
- package/src/mind/mechanisms/extraction.ts +27 -0
- package/src/mind/mechanisms/recall.ts +236 -37
- package/src/mind/mind.ts +166 -18
- package/src/mind/pipeline-mechanism.ts +7 -0
- package/src/mind/pipeline.ts +33 -1
- package/src/mind/prefix-completion.ts +314 -0
- package/src/mind/primitives.ts +105 -80
- package/src/mind/recognition.ts +151 -23
- package/src/mind/traverse.ts +52 -0
- package/src/mind/types.ts +104 -44
- package/src/store.ts +25 -0
- package/test/13-conversation.test.mjs +13 -0
- package/test/57-fusion-order.test.mjs +65 -0
- package/test/66-query-edge-whitespace.test.mjs +99 -0
- package/test/67-climb-anchor-breadth.test.mjs +113 -0
- package/test/68-extraction-unanchored.test.mjs +79 -0
- package/test/69-frame-filler.test.mjs +115 -0
- package/test/70-prefix-completion.test.mjs +170 -0
- package/test/71-embedded-canon-equivalence.test.mjs +121 -0
- package/test/72-prefix-candidate-supply.test.mjs +114 -0
- package/test/73-scaffolding-only-bridge-abstains.test.mjs +178 -0
- package/test/74-prefix-trap-not-sprung-early.test.mjs +114 -0
- package/test/75-multiturn-context-optimisation.test.mjs +1334 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// 69-frame-filler.test.mjs — the frame-filler tier must not fire without its
|
|
2
|
+
// evidence, and must refuse an ambiguous subject.
|
|
3
|
+
//
|
|
4
|
+
// WHAT THE MECHANISM DOES (src/mind/frame-filler.ts): when every other tier has
|
|
5
|
+
// declined, take the query's described span, put a corroborated filler from a
|
|
6
|
+
// trained context in its place, and require the STORE to already hold that key
|
|
7
|
+
// byte-exactly. "We invent a lookup KEY, never an answer" — the answer is the
|
|
8
|
+
// trained continuation of a form the store verifiably has.
|
|
9
|
+
//
|
|
10
|
+
// THE WIN IS REAL-STORE EVIDENCE, NOT A FIXTURE. On the 15.7M-node trained
|
|
11
|
+
// store, `What is the capital of the country where the Eiffel Tower is?` goes
|
|
12
|
+
// from silence to "The capital of France is Paris." (provenance `recall`), taking
|
|
13
|
+
// analyze_training.ts's section G from 33.3% to 66.7% and the battery from 73.8%
|
|
14
|
+
// to 76.2% with `0 weak` intact. A/B on warm caches: +50 ms on that query,
|
|
15
|
+
// +16 ms on a refusing query that runs 480 probes, and +0 ms on
|
|
16
|
+
// `What is the capital of Zamunda?` (guard 1 exits before probing) and on every
|
|
17
|
+
// query that answers earlier.
|
|
18
|
+
//
|
|
19
|
+
// WHY NO POSITIVE FIXTURE TEST EXISTS — measured, not assumed. The mechanism
|
|
20
|
+
// keys on corpus RARITY (container counts, the same reading the bridge's anchor
|
|
21
|
+
// picking uses). At fixture scale that signal is absent and even inverted: in a
|
|
22
|
+
// 21-deposit store `capital` reports 1 container and `landmark` reports 9, while
|
|
23
|
+
// on the trained store `Eiffel` is 54 against `What` at 1,586. So a fixture
|
|
24
|
+
// cannot make the query's rarest word land inside the description, which guard 1
|
|
25
|
+
// requires — three fixture shapes were tried (bare, 21-deposit, and one with an
|
|
26
|
+
// attested long word in the description) and none reached the positive path.
|
|
27
|
+
// What a fixture CAN pin is the refusal side, which is what this file does.
|
|
28
|
+
|
|
29
|
+
import { test } from "node:test";
|
|
30
|
+
import assert from "node:assert/strict";
|
|
31
|
+
import { Mind } from "../dist/src/index.js";
|
|
32
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
33
|
+
import { frameFillerSubstitution } from "../dist/src/mind/frame-filler.js";
|
|
34
|
+
|
|
35
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
36
|
+
const FRAME = [
|
|
37
|
+
["What is the capital of France?", "Paris is the capital of France."],
|
|
38
|
+
["What is the capital of Spain?", "Madrid is the capital of Spain."],
|
|
39
|
+
["What is the capital of Italy?", "Rome is the capital of Italy."],
|
|
40
|
+
["What is the capital of Japan?", "Tokyo is the capital of Japan."],
|
|
41
|
+
];
|
|
42
|
+
const LINK = [
|
|
43
|
+
"What is the most famous landmark in France?",
|
|
44
|
+
"The most famous landmark in France is the Eiffel Tower.",
|
|
45
|
+
];
|
|
46
|
+
const LINK2 = [
|
|
47
|
+
"What is the tallest structure in Spain?",
|
|
48
|
+
"The tallest structure in Spain is the Eiffel Tower.",
|
|
49
|
+
];
|
|
50
|
+
const Q = "What is the capital of the country where the Eiffel Tower is?";
|
|
51
|
+
|
|
52
|
+
async function fixture(train) {
|
|
53
|
+
const mind = new Mind({
|
|
54
|
+
seed: 1,
|
|
55
|
+
store: new SQliteStore({ path: ":memory:" }),
|
|
56
|
+
});
|
|
57
|
+
await mind.ingest(train);
|
|
58
|
+
return mind;
|
|
59
|
+
}
|
|
60
|
+
/** Call the tier the way recall does: the query plus recall's ranked hit ids. */
|
|
61
|
+
async function tier(mind, q) {
|
|
62
|
+
const hits = await mind.store.resonate(mind.perceive(q).v, 24);
|
|
63
|
+
return frameFillerSubstitution(mind, enc(q), hits.map((h) => h.id));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test("1. no linking evidence — the tier does not fire", async () => {
|
|
67
|
+
// The frame is attested, but nothing in the store ties the description's
|
|
68
|
+
// content to any filler. Guard 1 has nothing to qualify.
|
|
69
|
+
const m = await fixture(FRAME);
|
|
70
|
+
assert.equal(await tier(m, Q), null);
|
|
71
|
+
await m.store.close();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("2. an AMBIGUOUS subject is refused", async () => {
|
|
75
|
+
// Two trained contexts hold the description's content and each proposes a
|
|
76
|
+
// different filler. Guard 4: neither is licensed.
|
|
77
|
+
const m = await fixture([...FRAME, LINK, LINK2]);
|
|
78
|
+
assert.equal(await tier(m, Q), null);
|
|
79
|
+
await m.store.close();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("3. an unrelated query never grounds a frame neighbour", async () => {
|
|
83
|
+
// The fabrication shape: a fictional filler in an attested frame. On the real
|
|
84
|
+
// store this query resolves 24 keys (Chile, India, Japan, Italy …) once the
|
|
85
|
+
// guards are weakened, so it is the case that most needs pinning.
|
|
86
|
+
const m = await fixture([...FRAME, LINK]);
|
|
87
|
+
for (
|
|
88
|
+
const q of [
|
|
89
|
+
"What is the capital of Zamunda?",
|
|
90
|
+
"xyzzy plugh quux baz?",
|
|
91
|
+
"qq8f3kz9 vv2m1x7w?",
|
|
92
|
+
]
|
|
93
|
+
) {
|
|
94
|
+
assert.equal(await tier(m, q), null, `expected refusal for ${q}`);
|
|
95
|
+
}
|
|
96
|
+
await m.store.close();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("4. the tier is deterministic and side-effect free", async () => {
|
|
100
|
+
const m = await fixture([...FRAME, LINK]);
|
|
101
|
+
const a = await tier(m, Q);
|
|
102
|
+
const b = await tier(m, Q);
|
|
103
|
+
assert.deepEqual(a, b);
|
|
104
|
+
// Running it must not disturb the answers of forms that ground normally.
|
|
105
|
+
assert.match(await m.respondText("What is the capital of France?"), /Paris/);
|
|
106
|
+
await m.store.close();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("5. end to end, the fixture's own trained forms still answer", async () => {
|
|
110
|
+
const m = await fixture([...FRAME, LINK]);
|
|
111
|
+
assert.match(await m.respondText("What is the capital of Spain?"), /Madrid/);
|
|
112
|
+
assert.match(await m.respondText("What is the capital of Japan?"), /Tokyo/);
|
|
113
|
+
assert.equal(await m.respondText("qq8f3kz9 vv2m1x7w?"), "");
|
|
114
|
+
await m.store.close();
|
|
115
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// 70-prefix-completion.test.mjs — a query that IS the opening of one trained
|
|
2
|
+
// form is completed by that form's remainder; anything less is refused.
|
|
3
|
+
//
|
|
4
|
+
// WHAT THE MECHANISM DOES (src/mind/prefix-completion.ts): when every other
|
|
5
|
+
// tier has declined, scan the candidate list recall's refusal path has ALREADY
|
|
6
|
+
// fetched and look for a trained form whose bytes literally BEGIN with the whole
|
|
7
|
+
// query. The answer is that form's own remainder — never an invention.
|
|
8
|
+
//
|
|
9
|
+
// WHY IT IS NEEDED, measured on the 15.7M-node trained store:
|
|
10
|
+
// `The capital of France is` grounded nothing, while
|
|
11
|
+
// `The capital of France is Paris.` is trained and reads back byte-exact. Two
|
|
12
|
+
// independent reasons the earlier tiers cannot reach it:
|
|
13
|
+
// * `resolve(prefix)` is null — a proper prefix has no branch of its own.
|
|
14
|
+
// * the form is absent from `resonate(k)` at k = 24, 256 AND 2048, while forms
|
|
15
|
+
// scoring LOWER are returned (cos 0.5752 for the target against Germany
|
|
16
|
+
// 0.5670, Yemen 0.5591). `k` only reorders within the IVF clusters already
|
|
17
|
+
// probed, so no k recovers it; with `exhaustive` it ranks 8.
|
|
18
|
+
// It is a RETRIEVABILITY gap, not a semantic one.
|
|
19
|
+
//
|
|
20
|
+
// COST — A/B on the trained store, counting resonate calls directly: the tier
|
|
21
|
+
// adds ZERO exhaustive calls. F-prefix already made exactly one (for the
|
|
22
|
+
// substitution bridge) and returned silence; with the tier it makes the same one
|
|
23
|
+
// and answers. Battery: F 0% → 25%, overall 76.2% → 78.6%, `0 weak`, all three
|
|
24
|
+
// honest-silence probes still silent, median latency 0.63s → 0.57s.
|
|
25
|
+
//
|
|
26
|
+
// Unlike test/69, a POSITIVE fixture IS constructible here: the mechanism keys
|
|
27
|
+
// on literal byte containment, not on corpus rarity, and rarity is the signal
|
|
28
|
+
// that collapses at fixture scale.
|
|
29
|
+
//
|
|
30
|
+
// The mechanism has NO notion of text: no separator, no character class, no
|
|
31
|
+
// "word". Its only structural quantity is W, the river's grouping window. So
|
|
32
|
+
// these fixtures are readable prose only for the reader's benefit -- every
|
|
33
|
+
// assertion below is about bytes and geometry.
|
|
34
|
+
|
|
35
|
+
import { test } from "node:test";
|
|
36
|
+
import assert from "node:assert/strict";
|
|
37
|
+
import { Mind } from "../dist/src/index.js";
|
|
38
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
39
|
+
import { prefixCompletion } from "../dist/src/mind/prefix-completion.js";
|
|
40
|
+
|
|
41
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
42
|
+
const dec = new TextDecoder();
|
|
43
|
+
|
|
44
|
+
async function fixture(train) {
|
|
45
|
+
const mind = new Mind({
|
|
46
|
+
seed: 1,
|
|
47
|
+
store: new SQliteStore({ path: ":memory:" }),
|
|
48
|
+
});
|
|
49
|
+
await mind.ingest(train);
|
|
50
|
+
return mind;
|
|
51
|
+
}
|
|
52
|
+
/** Call the tier the way recall does: the query plus a ranked hit list. */
|
|
53
|
+
async function tier(mind, q, k = 64) {
|
|
54
|
+
const hits = await mind.store.resonate(mind.perceive(q).v, k);
|
|
55
|
+
return prefixCompletion(mind, enc(q), hits.map((h) => h.id));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const FACTS = [
|
|
59
|
+
"The capital of France is Paris.",
|
|
60
|
+
"The capital of Japan is Tokyo.",
|
|
61
|
+
"The capital of Germany is Berlin.",
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
test("1. the sole form opening with the query is completed by its remainder", async () => {
|
|
65
|
+
const m = await fixture(FACTS);
|
|
66
|
+
const hit = await tier(m, "The capital of France is");
|
|
67
|
+
assert.notEqual(hit, null, "the trained form opens with the query");
|
|
68
|
+
assert.equal(dec.decode(hit.form), "The capital of France is Paris.");
|
|
69
|
+
await m.store.close();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("2. AMBIGUITY is refused — the prefix trap", async () => {
|
|
73
|
+
// Two trained forms open with the same words and continue differently. The
|
|
74
|
+
// corpus does not say which completion the asker means, so neither is
|
|
75
|
+
// licensed. This is the documented prefix trap, and it is real — it simply
|
|
76
|
+
// does not hold for EVERY prefix, which is what test 1 pins.
|
|
77
|
+
const m = await fixture([
|
|
78
|
+
"The capital city is Paris.",
|
|
79
|
+
"The capital city is Berlin.",
|
|
80
|
+
]);
|
|
81
|
+
assert.equal(await tier(m, "The capital city is"), null);
|
|
82
|
+
await m.store.close();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("3. the same completion via two forms is ONE answer, not an ambiguity", async () => {
|
|
86
|
+
// Uniqueness is judged on the remainder BYTES, not on the candidate id.
|
|
87
|
+
const m = await fixture([
|
|
88
|
+
"The capital of France is Paris.",
|
|
89
|
+
"The capital of France is Paris.",
|
|
90
|
+
]);
|
|
91
|
+
const hit = await tier(m, "The capital of France is");
|
|
92
|
+
assert.notEqual(hit, null);
|
|
93
|
+
assert.equal(dec.decode(hit.form), "The capital of France is Paris.");
|
|
94
|
+
await m.store.close();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("4. a SUB-QUANTUM continuation is refused", async () => {
|
|
98
|
+
// Observed on the trained store: `What is the capital of France?` opens a
|
|
99
|
+
// trained `What is the capital of France??`, which continues by ONE byte.
|
|
100
|
+
// Below W the continuation is sub-quantum -- the fold groups nothing from it
|
|
101
|
+
// -- and voicing it is the degenerate reply of the battery's section M.
|
|
102
|
+
// The bar is the grouping window, not a punctuation class.
|
|
103
|
+
const m = await fixture(["What is the capital of France??"]);
|
|
104
|
+
assert.equal(await tier(m, "What is the capital of France?"), null);
|
|
105
|
+
await m.store.close();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("5. a query no trained form opens with is refused", async () => {
|
|
109
|
+
const m = await fixture(FACTS);
|
|
110
|
+
for (
|
|
111
|
+
const q of [
|
|
112
|
+
"The capital of Zamunda is",
|
|
113
|
+
"xyzzy plugh quux",
|
|
114
|
+
"Paris is the capital of",
|
|
115
|
+
]
|
|
116
|
+
) {
|
|
117
|
+
assert.equal(await tier(m, q), null, q);
|
|
118
|
+
}
|
|
119
|
+
await m.store.close();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("6. a query that is a whole trained form is not 'completed' by itself", async () => {
|
|
123
|
+
// An exact form has an EMPTY remainder, which no guard should let through —
|
|
124
|
+
// and the exact tiers own that query anyway.
|
|
125
|
+
const m = await fixture(FACTS);
|
|
126
|
+
assert.equal(await tier(m, "The capital of France is Paris."), null);
|
|
127
|
+
await m.store.close();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("7. the tier is deterministic and side-effect free", async () => {
|
|
131
|
+
const m = await fixture(FACTS);
|
|
132
|
+
const q = "The capital of France is";
|
|
133
|
+
const before = m.store.nodeCount();
|
|
134
|
+
const a = await tier(m, q);
|
|
135
|
+
const b = await tier(m, q);
|
|
136
|
+
assert.equal(dec.decode(a.form), dec.decode(b.form));
|
|
137
|
+
assert.equal(a.id, b.id);
|
|
138
|
+
assert.equal(
|
|
139
|
+
m.store.nodeCount(),
|
|
140
|
+
before,
|
|
141
|
+
"the tier must not intern anything",
|
|
142
|
+
);
|
|
143
|
+
await m.store.close();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("9. a form that continues past the read bound vetoes", async () => {
|
|
147
|
+
// Reads are bounded (query.length * W). A candidate that opens with the
|
|
148
|
+
// query but SATURATES the read continues out of sight, so it is a standing
|
|
149
|
+
// disagreement -- NOT something to skip. Skipping it is what manufactures a
|
|
150
|
+
// fragment: it removes the only evidence contradicting an interior fold node
|
|
151
|
+
// that happens to fit under the cap.
|
|
152
|
+
const q = "The capital of France is";
|
|
153
|
+
const long = q + " Paris, and the country's largest city by a wide margin, " +
|
|
154
|
+
"a global centre for art, fashion, gastronomy and culture.";
|
|
155
|
+
assert.ok(long.length > q.length * 4, "the fixture must exceed the cap");
|
|
156
|
+
const m = await fixture([long]);
|
|
157
|
+
assert.equal(await tier(m, q), null);
|
|
158
|
+
await m.store.close();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("8. end to end, the fixture's own trained forms still answer", async () => {
|
|
162
|
+
// The tier sits on the refusal path; it must not disturb normal grounding.
|
|
163
|
+
const m = await fixture([
|
|
164
|
+
"What is the capital of France?",
|
|
165
|
+
"The capital of France is Paris.",
|
|
166
|
+
]);
|
|
167
|
+
const r = await m.respond("What is the capital of France?");
|
|
168
|
+
assert.ok(r.bytes.length > 0, "a trained question still answers");
|
|
169
|
+
await m.store.close();
|
|
170
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// 71-embedded-canon-equivalence.test.mjs — an EMBEDDED trained form must be
|
|
2
|
+
// recognised under the response's canonical equivalence, not only byte-exactly.
|
|
3
|
+
//
|
|
4
|
+
// The contract: `recognise` decomposes a query into stored forms that lead
|
|
5
|
+
// somewhere. A trained form sitting at an interior offset of a larger query is
|
|
6
|
+
// found by the scale-gated "exact query-edge forms" tier in recognition.ts.
|
|
7
|
+
// That tier admits candidates with a BYTE-EXACT `store.findBranch` probe over
|
|
8
|
+
// the query's leaf-id run, then calls the canon-capable `resolveSpan`. The
|
|
9
|
+
// prefilter is therefore strictly narrower than its own resolver: a form whose
|
|
10
|
+
// deposit differs from the query only by the response canonicalizer's
|
|
11
|
+
// equivalence (case, width) can never reach `resolveSpan`, because a
|
|
12
|
+
// differently-cased deposit's branch kid-ids are not the query's leaf-id run
|
|
13
|
+
// under ANY canonicalization of the query. Measured on a 15.7M-node store:
|
|
14
|
+
// "Hey, What is the process of photosynthesis?" recognises the trained form,
|
|
15
|
+
// "Hey, what is …" recognises nothing, though the lowercased form resolves
|
|
16
|
+
// exactly at offset 0.
|
|
17
|
+
//
|
|
18
|
+
// WHY THE 4.3k-FACT FIXTURE IS NOT OPTIONAL: the tier runs only when
|
|
19
|
+
// `atomIsHub(ctx, corpusN)` is true, i.e. N > ~4096 contexts at maxGroup=4.
|
|
20
|
+
// Every small-store suite exercises the OTHER branch of recognition, so a
|
|
21
|
+
// conventional fixture would pass while the defect is fully present. The
|
|
22
|
+
// atomIsHub assertion below fails loudly if that crossover ever moves, so this
|
|
23
|
+
// test can never silently stop covering the branch it exists for.
|
|
24
|
+
import { test } from "node:test";
|
|
25
|
+
import assert from "node:assert/strict";
|
|
26
|
+
import { Mind } from "../dist/src/index.js";
|
|
27
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
28
|
+
import { recognise } from "../dist/src/mind/recognition.js";
|
|
29
|
+
import { resolve } from "../dist/src/mind/primitives.js";
|
|
30
|
+
import { atomIsHub, corpusN } from "../dist/src/mind/traverse.js";
|
|
31
|
+
import { textCanon } from "../dist/src/canon.js";
|
|
32
|
+
|
|
33
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
34
|
+
|
|
35
|
+
// Longer than chainReach(W)=W²=16 bytes, or the tier's own size gate skips it.
|
|
36
|
+
const FORM = "Madam Your Glasses Are Fogged";
|
|
37
|
+
const PREFIX = "Hey, ";
|
|
38
|
+
|
|
39
|
+
/** Recognise with the canonicalizer a TEXT response would carry. `ctx.canon`
|
|
40
|
+
* is per-response state that `respond()` injects; calling `recognise`
|
|
41
|
+
* directly would otherwise run with canon disabled and test nothing. */
|
|
42
|
+
function recogniseAsText(mind, text) {
|
|
43
|
+
mind.canon = textCanon;
|
|
44
|
+
mind.canonMemo = new Map();
|
|
45
|
+
try {
|
|
46
|
+
return recognise(mind, enc(text));
|
|
47
|
+
} finally {
|
|
48
|
+
mind.canon = null;
|
|
49
|
+
mind.canonMemo = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sitesFor = (rec, id) => rec.sites.filter((s) => s.payload === id);
|
|
54
|
+
|
|
55
|
+
test("an embedded trained form is recognised under canonical equivalence", async () => {
|
|
56
|
+
const m = new Mind({ seed: 7, store: new SQliteStore({ path: ":memory:" }) });
|
|
57
|
+
|
|
58
|
+
const filler = [];
|
|
59
|
+
for (let i = 0; i < 4300; i++) filler.push([`filler-${i}`, `f${i}`]);
|
|
60
|
+
await m.ingest(filler);
|
|
61
|
+
await m.ingest([[FORM, "a stored continuation for the form"]]);
|
|
62
|
+
// Canonical resolution reads a store-side index that training builds
|
|
63
|
+
// explicitly (meta `canon.upto`); without it `canonResolve` has no
|
|
64
|
+
// candidates and the equivalence under test does not exist at all.
|
|
65
|
+
await m.buildCanonIndex();
|
|
66
|
+
|
|
67
|
+
const N = corpusN(m);
|
|
68
|
+
assert.ok(
|
|
69
|
+
atomIsHub(m, N),
|
|
70
|
+
`fixture must cross atomIsHub (N=${N}); the tier under test is ` +
|
|
71
|
+
`scale-gated and this suite would otherwise assert nothing`,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const formId = resolve(m, enc(FORM));
|
|
75
|
+
assert.ok(formId !== null, "the trained form must resolve exactly");
|
|
76
|
+
|
|
77
|
+
// Control 1 — the form standalone, in its deposited case.
|
|
78
|
+
assert.ok(
|
|
79
|
+
sitesFor(recogniseAsText(m, FORM), formId).length > 0,
|
|
80
|
+
"standalone deposited-case form must be recognised",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Control 2 — the form standalone, lowercased. This is what proves the
|
|
84
|
+
// canonical equivalence is REAL and reachable, so the embedded assertion
|
|
85
|
+
// below is about placement, not about the canonicalizer.
|
|
86
|
+
assert.ok(
|
|
87
|
+
sitesFor(recogniseAsText(m, FORM.toLowerCase()), formId).length > 0,
|
|
88
|
+
"lowercased form must resolve to the same node at offset 0",
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Control 3 — embedded, deposited case. The tier's byte-exact route.
|
|
92
|
+
assert.ok(
|
|
93
|
+
sitesFor(recogniseAsText(m, PREFIX + FORM), formId).length > 0,
|
|
94
|
+
"embedded deposited-case form must be recognised",
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// THE CONTRACT — embedded AND canonically equivalent. Controls 2 and 3 each
|
|
98
|
+
// hold, so anything that fails here is the prefilter being narrower than its
|
|
99
|
+
// resolver, which is the defect this test exists to prevent.
|
|
100
|
+
const rec = recogniseAsText(m, PREFIX + FORM.toLowerCase());
|
|
101
|
+
const hit = sitesFor(rec, formId);
|
|
102
|
+
assert.ok(
|
|
103
|
+
hit.length > 0,
|
|
104
|
+
`embedded lowercased form must be recognised: standalone-lowercased and ` +
|
|
105
|
+
`embedded-exact both are, so the byte-exact admission gate is the only ` +
|
|
106
|
+
`thing rejecting it (sites found: ${
|
|
107
|
+
rec.sites.map((s) => `${s.start}-${s.end}`).join(",") || "none"
|
|
108
|
+
})`,
|
|
109
|
+
);
|
|
110
|
+
// It must be found AT its true offset, not as some other coincidental span.
|
|
111
|
+
assert.ok(
|
|
112
|
+
hit.some((s) =>
|
|
113
|
+
s.start === PREFIX.length && s.end === PREFIX.length + FORM.length
|
|
114
|
+
),
|
|
115
|
+
`the form must be recognised at its own span [${PREFIX.length},${
|
|
116
|
+
PREFIX.length + FORM.length
|
|
117
|
+
}], got ${hit.map((s) => `${s.start}-${s.end}`).join(",")}`,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
await m.store.close();
|
|
121
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// 72-prefix-candidate-supply.test.mjs — a query that is a proper PREFIX of a
|
|
2
|
+
// trained form must be able to reach that form even when resonance cannot rank
|
|
3
|
+
// it.
|
|
4
|
+
//
|
|
5
|
+
// THE GAP THIS PINS. `prefixCompletion` consumes a ranked list the caller
|
|
6
|
+
// already fetched, and that list comes from resonance. Resonance cannot rank a
|
|
7
|
+
// proper prefix: measured on a 15.7M-node store, cos(prefix, form) falls from
|
|
8
|
+
// 0.9629 at a one-byte truncation to 0.6206 at three bytes, against a
|
|
9
|
+
// reachThreshold of 0.8750. The mechanism's guards were therefore never
|
|
10
|
+
// reached — the trace read `candidates: 24, opened: 0` — and the query answered
|
|
11
|
+
// nothing. `prefixCandidates` is the second SUPPLY that closes it, reading the
|
|
12
|
+
// leaf-id WINDOW index `indexSubSpans` already writes at deposit time. No
|
|
13
|
+
// ingestion, storage or fold change is involved: this test would pass on a
|
|
14
|
+
// store trained before the supply existed.
|
|
15
|
+
//
|
|
16
|
+
// WHY THE ASSERTIONS ARE SHAPED THIS WAY. On a small fixture resonance may
|
|
17
|
+
// well return the form by luck, and then an end-to-end "does it answer?" test
|
|
18
|
+
// would pass with the supply deleted — pinning nothing. So the contract is
|
|
19
|
+
// asserted on `prefixCandidates` DIRECTLY, and the resonance list is asserted
|
|
20
|
+
// to lack the form, which is what makes the supply load-bearing rather than
|
|
21
|
+
// redundant.
|
|
22
|
+
//
|
|
23
|
+
// The 4.3k-fact fixture is not decoration: window containment is judged against
|
|
24
|
+
// `hubBound` = √N, so a toy store makes every window look saturated and the
|
|
25
|
+
// supply would correctly return nothing, testing nothing.
|
|
26
|
+
import { test } from "node:test";
|
|
27
|
+
import assert from "node:assert/strict";
|
|
28
|
+
import { Mind } from "../dist/src/index.js";
|
|
29
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
30
|
+
import {
|
|
31
|
+
prefixCandidates,
|
|
32
|
+
prefixCompletion,
|
|
33
|
+
} from "../dist/src/mind/prefix-completion.js";
|
|
34
|
+
import { gistOf, resolve } from "../dist/src/mind/primitives.js";
|
|
35
|
+
|
|
36
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
37
|
+
const dec = new TextDecoder();
|
|
38
|
+
|
|
39
|
+
const FORM = "The chief export of the northern province is powdered basalt.";
|
|
40
|
+
|
|
41
|
+
test("a proper prefix reaches its trained form through the window supply", async () => {
|
|
42
|
+
const m = new Mind({ seed: 7, store: new SQliteStore({ path: ":memory:" }) });
|
|
43
|
+
|
|
44
|
+
const filler = [];
|
|
45
|
+
for (let i = 0; i < 4300; i++) filler.push([`filler-${i}`, `f${i}`]);
|
|
46
|
+
await m.ingest(filler);
|
|
47
|
+
await m.ingest([[FORM, "acknowledged"]]);
|
|
48
|
+
|
|
49
|
+
const formId = resolve(m, enc(FORM));
|
|
50
|
+
assert.ok(formId !== null, "the trained form must resolve exactly");
|
|
51
|
+
|
|
52
|
+
// Truncate by more than one grouping window, so the continuation clears
|
|
53
|
+
// prefixCompletion's sub-quantum guard and the tier can actually fire.
|
|
54
|
+
const W = m.space.maxGroup;
|
|
55
|
+
const query = enc(FORM.slice(0, FORM.length - 3 * W));
|
|
56
|
+
|
|
57
|
+
// Premise 1 — the exact tiers genuinely cannot serve this query.
|
|
58
|
+
assert.equal(
|
|
59
|
+
resolve(m, query),
|
|
60
|
+
null,
|
|
61
|
+
"a proper prefix must have no branch of its own, or the gap is not real",
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Premise 2 — resonance does not supply the form, so anything that works
|
|
65
|
+
// below is the new supply and not the ranked list in disguise.
|
|
66
|
+
// Premise 2 — the ranked list is NOT what is under test. At fixture scale
|
|
67
|
+
// resonance does rank the form (it is the only content among 4,300 fillers
|
|
68
|
+
// that resembles the query), and that is measured, not assumed: the
|
|
69
|
+
// assertion below records it, so if fixture geometry ever changes the reader
|
|
70
|
+
// is told rather than misled. Resonance's real-world inability to rank a
|
|
71
|
+
// prefix is a LARGE-CORPUS property — cos falls to 0.6206 at a three-byte
|
|
72
|
+
// truncation against a 0.8750 bar, on a 15.7M-node store — and cannot be
|
|
73
|
+
// reproduced at this scale. That is why the contract below is asserted on
|
|
74
|
+
// `prefixCandidates` DIRECTLY: deleting or emptying the supply fails this
|
|
75
|
+
// test regardless of what resonance happens to return.
|
|
76
|
+
const ranked = (await m.store.resonate(gistOf(m, query), 64)).map((h) =>
|
|
77
|
+
h.id
|
|
78
|
+
);
|
|
79
|
+
assert.ok(
|
|
80
|
+
ranked.includes(formId),
|
|
81
|
+
"fixture note: at this scale resonance is expected to rank the form; " +
|
|
82
|
+
"if it no longer does, the end-to-end path below became the load-" +
|
|
83
|
+
"bearing assertion and this comment must be revisited",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// THE CONTRACT — the write side's own window index proposes the form.
|
|
87
|
+
const proposed = prefixCandidates(m, query);
|
|
88
|
+
assert.ok(
|
|
89
|
+
proposed.includes(formId),
|
|
90
|
+
`the window supply must propose the trained form the query opens ` +
|
|
91
|
+
`(proposed ${proposed.length} candidate(s))`,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// …and the mechanism, unchanged, grounds it whole through that supply.
|
|
95
|
+
const completed = prefixCompletion(m, query, proposed);
|
|
96
|
+
assert.ok(completed !== null, "the supplied form must complete the query");
|
|
97
|
+
assert.equal(
|
|
98
|
+
dec.decode(completed.form),
|
|
99
|
+
FORM,
|
|
100
|
+
"a FORM is grounded whole, never a slice cut at the query's end",
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// HONEST DEGRADATION (§2.13). A query with no discriminative window must
|
|
104
|
+
// propose nothing rather than guess — silence is the correct answer, and a
|
|
105
|
+
// supply that widened until it found something would be the real defect.
|
|
106
|
+
const hub = enc("The ");
|
|
107
|
+
assert.equal(
|
|
108
|
+
prefixCompletion(m, hub, prefixCandidates(m, hub)),
|
|
109
|
+
null,
|
|
110
|
+
"a query carrying no discriminative window must stay silent",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
await m.store.close();
|
|
114
|
+
});
|