@hviana/sema 0.2.5 → 0.2.7
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.
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// 53-cross-region-probe-instrumentation.test.mjs — extends the climbConsensus
|
|
2
|
+
// instrumentation (test/52) to cover the ordinary-region contrastive rival,
|
|
3
|
+
// the cross-region junction probe ladder, and structural-resonance. Purely
|
|
4
|
+
// additive: every assertion checks the STRUCTURE of the `data` payload's new
|
|
5
|
+
// fields — never that inference itself changed.
|
|
6
|
+
|
|
7
|
+
import { test } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { Mind } from "../dist/src/index.js";
|
|
10
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
11
|
+
|
|
12
|
+
const mk = (seed = 1) =>
|
|
13
|
+
new Mind({ seed, store: new SQliteStore({ path: ":memory:" }) });
|
|
14
|
+
|
|
15
|
+
async function trace(mind, q) {
|
|
16
|
+
const steps = [];
|
|
17
|
+
const ans = await mind.respondText(q, (s) => steps.push(s));
|
|
18
|
+
return { steps, ans };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function climbStep(steps) {
|
|
22
|
+
return steps.find((s) => s.mechanism.at(-1) === "climbConsensus");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Reused verbatim from test/51's proven fixtures.
|
|
26
|
+
const ATTR_CORPUS = [
|
|
27
|
+
["red", "is a color"],
|
|
28
|
+
["blue", "is a color"],
|
|
29
|
+
["circle", "is a shape"],
|
|
30
|
+
["square", "is a shape"],
|
|
31
|
+
["red circle", "answer alpha"],
|
|
32
|
+
["red square", "answer beta"],
|
|
33
|
+
["blue circle", "answer gamma"],
|
|
34
|
+
["blue square", "answer delta"],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const SYN_ATTR_CORPUS = [
|
|
38
|
+
["red", "is a color"],
|
|
39
|
+
["crimson", "is a color"],
|
|
40
|
+
["blue", "is a color"],
|
|
41
|
+
["circle", "is a shape"],
|
|
42
|
+
["square", "is a shape"],
|
|
43
|
+
["red circle", "answer alpha"],
|
|
44
|
+
["crimson circle", "answer alpha2"],
|
|
45
|
+
["red square", "answer beta"],
|
|
46
|
+
["blue circle", "answer gamma"],
|
|
47
|
+
["blue square", "answer delta"],
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
+
// 1. ordinary-region contrastive rival.
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
|
|
54
|
+
test("1. an approximate region with a genuine rival reports contrastiveRival, and the margin matches score - rival.score", async () => {
|
|
55
|
+
const m = mk(1);
|
|
56
|
+
await m.ingest(ATTR_CORPUS);
|
|
57
|
+
const { steps } = await trace(m, "blue then circle then square");
|
|
58
|
+
await m.store.close();
|
|
59
|
+
|
|
60
|
+
const step = climbStep(steps);
|
|
61
|
+
assert.ok(step?.data, "expected a traced climbConsensus step");
|
|
62
|
+
const regions = step.data.regions ?? [];
|
|
63
|
+
const withRival = regions.filter((r) => r.contrastiveRival);
|
|
64
|
+
assert.ok(withRival.length > 0, "expected at least one region with a rival");
|
|
65
|
+
for (const r of withRival) {
|
|
66
|
+
assert.equal(typeof r.contrastiveRival.node, "number");
|
|
67
|
+
assert.equal(typeof r.contrastiveRival.rank, "number");
|
|
68
|
+
assert.equal(typeof r.contrastiveRival.score, "number");
|
|
69
|
+
assert.ok(
|
|
70
|
+
Math.abs(
|
|
71
|
+
r.contrastiveMargin - (r.selected.score - r.contrastiveRival.score),
|
|
72
|
+
) < 1e-9,
|
|
73
|
+
"contrastiveMargin must equal selected.score - contrastiveRival.score",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("1b. a known region never carries a contrastiveRival (the contrast only applies to approximate regions)", async () => {
|
|
79
|
+
const m = mk(1);
|
|
80
|
+
await m.ingest(ATTR_CORPUS);
|
|
81
|
+
const { steps } = await trace(m, "red then circle");
|
|
82
|
+
await m.store.close();
|
|
83
|
+
|
|
84
|
+
const step = climbStep(steps);
|
|
85
|
+
const regions = step.data.regions ?? [];
|
|
86
|
+
for (const r of regions) {
|
|
87
|
+
if (r.known) {
|
|
88
|
+
assert.equal(r.contrastiveRival, undefined);
|
|
89
|
+
assert.equal(r.contrastiveMargin, undefined);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// And when a voted approximate region has NO rival at all, contrastiveMargin
|
|
93
|
+
// stays exactly selected.score, with no contrastiveRival field.
|
|
94
|
+
for (const r of regions) {
|
|
95
|
+
if (r.outcome === "voted" && !r.known && r.contrastiveRival === undefined) {
|
|
96
|
+
assert.equal(r.contrastiveMargin, r.selected.score);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
102
|
+
// 2. probe accounting invariant.
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
104
|
+
|
|
105
|
+
test("2. crossRegion.probes.length === crossRegion.probesAttempted", async () => {
|
|
106
|
+
const m = mk(1);
|
|
107
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
108
|
+
const { steps } = await trace(m, "the crimson near the square");
|
|
109
|
+
await m.store.close();
|
|
110
|
+
|
|
111
|
+
const step = climbStep(steps);
|
|
112
|
+
const cr = step.data.crossRegion;
|
|
113
|
+
assert.ok(cr, "expected a crossRegion summary");
|
|
114
|
+
assert.equal(cr.probes.length, cr.probesAttempted);
|
|
115
|
+
assert.ok(
|
|
116
|
+
["insufficient-regions", "probe-limit", "pairs-exhausted"].includes(
|
|
117
|
+
cr.stopReason,
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
123
|
+
// 3. exact junction: attempt count, accepted structural outcome, vote-probe
|
|
124
|
+
// link.
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
+
|
|
127
|
+
test("3. exact junction: probe.exact attempted with candidates, accepted structural outcome, and vote links back to its probe", async () => {
|
|
128
|
+
const m = mk(3);
|
|
129
|
+
await m.ingest(ATTR_CORPUS);
|
|
130
|
+
const { steps } = await trace(m, "red then circle");
|
|
131
|
+
await m.store.close();
|
|
132
|
+
|
|
133
|
+
const step = climbStep(steps);
|
|
134
|
+
const cr = step.data.crossRegion;
|
|
135
|
+
assert.ok(cr.junctionVotes.length > 0);
|
|
136
|
+
const jv = cr.junctionVotes[0];
|
|
137
|
+
assert.equal(jv.tier, "exact");
|
|
138
|
+
assert.equal(typeof jv.probe, "number");
|
|
139
|
+
const probe = cr.probes[jv.probe];
|
|
140
|
+
assert.ok(probe, "vote's probe index must resolve into crossRegion.probes");
|
|
141
|
+
assert.equal(probe.exact.attempted, true);
|
|
142
|
+
assert.ok(probe.exact.candidatesReturned > 0);
|
|
143
|
+
assert.ok(probe.structural, "expected a structural trace on this probe");
|
|
144
|
+
assert.equal(probe.structural.outcome, "accepted");
|
|
145
|
+
assert.equal(probe.outcome, "accepted");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
149
|
+
// 4. single-synonym junction.
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
test("4. single-synonym junction: exact returns zero, single attempted and returns candidates, selected single-synonym tier", async () => {
|
|
153
|
+
const m = mk(1);
|
|
154
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
155
|
+
// "square then crimson" — order-free single-sibling substitution recovers
|
|
156
|
+
// 'red square' via crimson's sibling 'red' (mirrors test/51's synRight).
|
|
157
|
+
const { steps } = await trace(m, "square then crimson");
|
|
158
|
+
await m.store.close();
|
|
159
|
+
|
|
160
|
+
const step = climbStep(steps);
|
|
161
|
+
const cr = step.data.crossRegion;
|
|
162
|
+
assert.ok(cr.junctionVotes.length > 0);
|
|
163
|
+
const jv = cr.junctionVotes.find((v) => v.tier === "single-synonym");
|
|
164
|
+
assert.ok(jv, "expected a single-synonym junction vote");
|
|
165
|
+
const probe = cr.probes[jv.probe];
|
|
166
|
+
assert.equal(probe.exact.candidatesReturned, 0);
|
|
167
|
+
assert.equal(probe.singleSynonym.attempted, true);
|
|
168
|
+
assert.ok(probe.singleSynonym.candidatesReturned > 0);
|
|
169
|
+
assert.equal(probe.structural.tier, "single-synonym");
|
|
170
|
+
assert.equal(probe.structural.outcome, "accepted");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
174
|
+
// 5. double-synonym junction: verify the ladder position and fields.
|
|
175
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
176
|
+
|
|
177
|
+
test("5. double-synonym tier: probe fields report single returned zero and double attempted when only the double tier can fire", async () => {
|
|
178
|
+
const m = mk(1);
|
|
179
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
180
|
+
const { steps } = await trace(m, "crimson then crimson");
|
|
181
|
+
await m.store.close();
|
|
182
|
+
|
|
183
|
+
const step = climbStep(steps);
|
|
184
|
+
const cr = step.data.crossRegion ?? { probes: [] };
|
|
185
|
+
// Whether or not a double-synonym container was actually found (fixture-
|
|
186
|
+
// sensitive per test/51's own test 4), every probe that attempted the
|
|
187
|
+
// synonym ladder must report single/double consistently: double is only
|
|
188
|
+
// ever attempted when single returned zero.
|
|
189
|
+
for (const p of cr.probes) {
|
|
190
|
+
if (p.doubleSynonym.attempted) {
|
|
191
|
+
assert.equal(p.singleSynonym.candidatesReturned, 0);
|
|
192
|
+
assert.equal(p.singleSynonym.attempted, true);
|
|
193
|
+
}
|
|
194
|
+
if (p.singleSynonym.candidatesReturned > 0) {
|
|
195
|
+
assert.equal(p.doubleSynonym.attempted, false);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
201
|
+
// 6. structural resonance: variants, budget, hits, merged proposals,
|
|
202
|
+
// examined sequence, margin, noise floor, outcome.
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
|
+
|
|
205
|
+
test("6. structural-resonance: eligible probes report variants, merged proposals and an examined sequence with effectiveScore = annScore * semanticConfidence", async () => {
|
|
206
|
+
const m = mk(1);
|
|
207
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
208
|
+
const { steps } = await trace(m, "crimson then square");
|
|
209
|
+
await m.store.close();
|
|
210
|
+
|
|
211
|
+
const step = climbStep(steps);
|
|
212
|
+
const cr = step.data.crossRegion;
|
|
213
|
+
const eligible = cr.probes.filter((p) =>
|
|
214
|
+
p.resonance && p.resonance.outcome !== "ineligible"
|
|
215
|
+
);
|
|
216
|
+
assert.ok(
|
|
217
|
+
eligible.length > 0,
|
|
218
|
+
"expected at least one eligible resonance probe",
|
|
219
|
+
);
|
|
220
|
+
for (const p of eligible) {
|
|
221
|
+
const res = p.resonance;
|
|
222
|
+
assert.equal(res.variantBudget, m.cfg.haloQueryK);
|
|
223
|
+
assert.ok(Array.isArray(res.variants));
|
|
224
|
+
assert.ok(res.variants.length > 0);
|
|
225
|
+
for (const v of res.variants) {
|
|
226
|
+
assert.equal(typeof v.semanticConfidence, "number");
|
|
227
|
+
assert.equal(typeof v.annHitsReturned, "number");
|
|
228
|
+
}
|
|
229
|
+
assert.equal(typeof res.mergedProposals, "number");
|
|
230
|
+
assert.ok(Array.isArray(res.examined));
|
|
231
|
+
for (const c of res.examined) {
|
|
232
|
+
assert.ok(
|
|
233
|
+
Math.abs(c.effectiveScore - c.annScore * c.semanticConfidence) < 1e-9,
|
|
234
|
+
);
|
|
235
|
+
assert.ok(
|
|
236
|
+
[
|
|
237
|
+
"saturated",
|
|
238
|
+
"no-roots",
|
|
239
|
+
"nonpositive-idf",
|
|
240
|
+
"same-as-endpoint",
|
|
241
|
+
"same-as-selected",
|
|
242
|
+
"selected",
|
|
243
|
+
"contrastive-rival",
|
|
244
|
+
].includes(c.outcome),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
assert.equal(typeof res.noiseFloor, "number");
|
|
248
|
+
assert.ok(
|
|
249
|
+
["empty", "no-valid-proposal", "margin-rejected", "accepted"].includes(
|
|
250
|
+
res.outcome,
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
if (res.outcome === "accepted" || res.outcome === "margin-rejected") {
|
|
254
|
+
assert.equal(typeof res.contrastiveMargin, "number");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
260
|
+
// 7. resonance rejection outcomes.
|
|
261
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
262
|
+
|
|
263
|
+
test("7. resonance outcomes: ineligible reasons are named and a margin-rejected probe reports a sub-noise-floor margin", async () => {
|
|
264
|
+
const mIneligible = mk(1);
|
|
265
|
+
await mIneligible.ingest(SYN_ATTR_CORPUS);
|
|
266
|
+
const { steps: ineligibleSteps } = await trace(
|
|
267
|
+
mIneligible,
|
|
268
|
+
"crimson beside square",
|
|
269
|
+
);
|
|
270
|
+
await mIneligible.store.close();
|
|
271
|
+
|
|
272
|
+
const ineligibleStep = climbStep(ineligibleSteps);
|
|
273
|
+
const ineligible = (ineligibleStep.data.crossRegion?.probes ?? []).filter(
|
|
274
|
+
(p) => p.resonance?.outcome === "ineligible",
|
|
275
|
+
);
|
|
276
|
+
assert.ok(ineligible.length > 0, "expected at least one ineligible probe");
|
|
277
|
+
for (const p of ineligible) {
|
|
278
|
+
assert.ok(Array.isArray(p.resonance.ineligibleReasons));
|
|
279
|
+
assert.ok(p.resonance.ineligibleReasons.length > 0);
|
|
280
|
+
for (const r of p.resonance.ineligibleReasons) {
|
|
281
|
+
assert.ok(
|
|
282
|
+
["between-region", "not-both-strong", "not-both-known", "gap-too-large"]
|
|
283
|
+
.includes(r),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
assert.equal(p.outcome, "resonance-ineligible");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const mMargin = mk(1);
|
|
290
|
+
await mMargin.ingest(SYN_ATTR_CORPUS);
|
|
291
|
+
const { steps: marginSteps } = await trace(mMargin, "crimson then square");
|
|
292
|
+
await mMargin.store.close();
|
|
293
|
+
const marginStep = climbStep(marginSteps);
|
|
294
|
+
const marginRejected = (marginStep.data.crossRegion?.probes ?? []).filter(
|
|
295
|
+
(p) => p.resonance?.outcome === "margin-rejected",
|
|
296
|
+
);
|
|
297
|
+
assert.ok(marginRejected.length > 0, "expected a margin-rejected probe");
|
|
298
|
+
for (const p of marginRejected) {
|
|
299
|
+
assert.ok(p.resonance.contrastiveMargin <= p.resonance.noiseFloor);
|
|
300
|
+
assert.equal(p.outcome, "resonance-rejected");
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
305
|
+
// 8. cross-region vote weight fields.
|
|
306
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
307
|
+
|
|
308
|
+
test("8. a linked cross-region vote reports confidence, evidenceBytes, mutualWeight, voteWeightPerRoot as numbers", async () => {
|
|
309
|
+
const m = mk(3);
|
|
310
|
+
await m.ingest(ATTR_CORPUS);
|
|
311
|
+
const { steps } = await trace(m, "red then circle");
|
|
312
|
+
await m.store.close();
|
|
313
|
+
|
|
314
|
+
const step = climbStep(steps);
|
|
315
|
+
const jv = step.data.crossRegion.junctionVotes[0];
|
|
316
|
+
assert.equal(typeof jv.confidence, "number");
|
|
317
|
+
assert.equal(typeof jv.evidenceBytes, "number");
|
|
318
|
+
assert.equal(typeof jv.mutualWeight, "number");
|
|
319
|
+
assert.equal(typeof jv.voteWeightPerRoot, "number");
|
|
320
|
+
assert.equal(jv.confidence, 1); // exact tier: confidence collapses to 1
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
324
|
+
// 9. additive behavior — the new fields never change roots/ranked/answer.
|
|
325
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
326
|
+
|
|
327
|
+
test("9. enabling the extended instrumentation never changes roots, ranked, or the answer", async () => {
|
|
328
|
+
const queries = [
|
|
329
|
+
"red then circle",
|
|
330
|
+
"square then crimson",
|
|
331
|
+
"crimson then square",
|
|
332
|
+
"crimson then crimson",
|
|
333
|
+
"the crimson near the square",
|
|
334
|
+
];
|
|
335
|
+
for (const q of queries) {
|
|
336
|
+
const plain = mk(9);
|
|
337
|
+
await plain.ingest(SYN_ATTR_CORPUS);
|
|
338
|
+
const plainAns = await plain.respondText(q);
|
|
339
|
+
await plain.store.close();
|
|
340
|
+
|
|
341
|
+
const traced = mk(9);
|
|
342
|
+
await traced.ingest(SYN_ATTR_CORPUS);
|
|
343
|
+
const { ans: tracedAns, steps } = await trace(traced, q);
|
|
344
|
+
await traced.store.close();
|
|
345
|
+
|
|
346
|
+
assert.equal(tracedAns, plainAns, `answer differs for "${q}"`);
|
|
347
|
+
|
|
348
|
+
const step = climbStep(steps);
|
|
349
|
+
if (step?.data?.crossRegion) {
|
|
350
|
+
// The probe/resonance instrumentation is present but does not affect
|
|
351
|
+
// the pooled result: roots/ranked come from the SAME vote objects the
|
|
352
|
+
// probes were recorded from.
|
|
353
|
+
assert.ok(Array.isArray(step.data.crossRegion.probes));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|