@hviana/sema 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +29 -29
- package/TRADEMARKS.md +0 -1
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.js +2 -0
- package/dist/src/geometry.d.ts +21 -0
- package/dist/src/geometry.js +21 -0
- package/dist/src/meter.d.ts +51 -0
- package/dist/src/meter.js +51 -0
- package/dist/src/mind/attention.d.ts +4 -0
- package/dist/src/mind/attention.js +165 -16
- package/dist/src/mind/canonical.d.ts +16 -0
- package/dist/src/mind/canonical.js +41 -0
- package/dist/src/mind/graph-search.js +33 -14
- package/dist/src/mind/match.d.ts +1 -1
- package/dist/src/mind/match.js +5 -3
- package/dist/src/mind/mechanisms/cast.js +1 -1
- package/dist/src/mind/mechanisms/confluence.js +24 -0
- package/dist/src/mind/mechanisms/recall.js +32 -4
- package/dist/src/mind/mind.d.ts +4 -2
- package/dist/src/mind/mind.js +5 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +7 -0
- package/dist/src/mind/pipeline.js +41 -14
- package/dist/src/mind/primitives.js +9 -1
- package/dist/src/mind/rationale.d.ts +28 -1
- package/dist/src/mind/rationale.js +22 -1
- package/dist/src/mind/reasoning.d.ts +21 -3
- package/dist/src/mind/reasoning.js +73 -21
- package/dist/src/mind/recognition.js +4 -8
- package/dist/src/mind/resonance.js +20 -1
- package/dist/src/mind/trace.js +1 -0
- package/dist/src/mind/traverse.js +6 -2
- package/dist/src/mind/types.d.ts +36 -13
- package/docs/INVARIANTS.md +2 -2
- package/docs/architecture/bounded-reads.md +1 -1
- package/docs/architecture/commonality.md +2 -2
- package/docs/architecture/cost-model.md +2 -2
- package/docs/architecture/determinism.md +7 -7
- package/docs/architecture/match-project.md +2 -3
- package/docs/architecture/mechanism-market.md +10 -10
- package/docs/architecture/meter.md +5 -5
- package/docs/architecture/store.md +3 -3
- package/docs/failures/tempting-but-wrong.md +3 -4
- package/docs/harness/gates.md +2 -2
- package/docs/mechanisms/cast.md +2 -2
- package/docs/mechanisms/cover.md +2 -3
- package/docs/mechanisms/extraction.md +7 -7
- package/docs/mechanisms/recall.md +8 -9
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +11 -12
- package/src/config.ts +13 -0
- package/src/geometry.ts +21 -0
- package/src/meter.ts +51 -0
- package/src/mind/attention.ts +167 -16
- package/src/mind/canonical.ts +43 -0
- package/src/mind/graph-search.ts +39 -14
- package/src/mind/match.ts +5 -3
- package/src/mind/mechanisms/cast.ts +3 -1
- package/src/mind/mechanisms/confluence.ts +24 -0
- package/src/mind/mechanisms/recall.ts +32 -4
- package/src/mind/mind.ts +6 -4
- package/src/mind/pipeline-mechanism.ts +7 -0
- package/src/mind/pipeline.ts +49 -16
- package/src/mind/primitives.ts +9 -1
- package/src/mind/rationale.ts +35 -1
- package/src/mind/reasoning.ts +92 -15
- package/src/mind/recognition.ts +4 -8
- package/src/mind/resonance.ts +19 -1
- package/src/mind/trace.ts +1 -0
- package/src/mind/traverse.ts +7 -5
- package/src/mind/types.ts +36 -13
- package/test/105-derive-through-reports-its-refusal.test.mjs +24 -0
- package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
- package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
- package/test/120-composition-is-consequence.test.mjs +132 -0
- package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
- package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
- package/test/123-the-paired-formulas-agree.test.mjs +90 -0
- package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
- package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
- package/test/129-the-trace-payload-shape.test.mjs +164 -0
- package/test/32-confluence.test.mjs +68 -0
- package/test/38-reason-restate-guard.test.mjs +8 -2
- package/test/43-cast-analog-seat.test.mjs +10 -0
- package/test/55-cost-meter.test.mjs +859 -0
|
@@ -282,3 +282,862 @@ test("10. sumReports and formatReport aggregate a battery", async () => {
|
|
|
282
282
|
assert.match(text, /perceptions/);
|
|
283
283
|
await mind.store.close();
|
|
284
284
|
});
|
|
285
|
+
|
|
286
|
+
test("10. the recompose descent is counted, and its zero is omitted", async () => {
|
|
287
|
+
// `recompleteNode` decomposes a completion by ITS OWN kids, and that descent
|
|
288
|
+
// was invisible: a caller could read the chain's result but not whether the
|
|
289
|
+
// recomposition ran, so "the recursion stopped" and "it never ran" looked the
|
|
290
|
+
// same from the counters alone.
|
|
291
|
+
//
|
|
292
|
+
// Measured on this fixture: the simple queries deepen (recompletes = 1) while
|
|
293
|
+
// "eva director country" is answered by the JOIN and never descends — the same
|
|
294
|
+
// store, one counter, both directions.
|
|
295
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
296
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
297
|
+
const F1 = "The director of Eva is Gustaf Molander.";
|
|
298
|
+
const F2 = "The country of Gustaf Molander is Sweden.";
|
|
299
|
+
await mind.ingest([
|
|
300
|
+
["eva", F1],
|
|
301
|
+
["eva director", F1],
|
|
302
|
+
["gustaf molander", F2],
|
|
303
|
+
["gustaf molander country", F2],
|
|
304
|
+
]);
|
|
305
|
+
|
|
306
|
+
await mind.respondText("eva director");
|
|
307
|
+
const descended = mind.lastCost.counters.recompletes ?? 0;
|
|
308
|
+
|
|
309
|
+
await mind.respondText("eva director country");
|
|
310
|
+
const joined = mind.lastCost.counters;
|
|
311
|
+
|
|
312
|
+
await store.close();
|
|
313
|
+
|
|
314
|
+
assert.ok(
|
|
315
|
+
descended >= 1,
|
|
316
|
+
`the recompose descent must be counted (got ${descended}) — without it the ` +
|
|
317
|
+
`recursion is unobservable`,
|
|
318
|
+
);
|
|
319
|
+
assert.ok(
|
|
320
|
+
(joined.joinFired ?? 0) >= 1,
|
|
321
|
+
`the control query must be the join's, not the descent's (joinFired=${joined.joinFired})`,
|
|
322
|
+
);
|
|
323
|
+
assert.equal(
|
|
324
|
+
joined.recompletes,
|
|
325
|
+
undefined,
|
|
326
|
+
"a response that never descends must omit the counter (test 6's convention)",
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("11. the pivot's probe cap is visible: what it spent, and what it withheld", async () => {
|
|
331
|
+
// The cap itself is deliberate — probing every branch of a long answer made
|
|
332
|
+
// the pivot sweep the dominant ANN cost at corpus scale — but what it spent
|
|
333
|
+
// and what it withheld were both invisible. Untraced on purpose (meter.ts
|
|
334
|
+
// contract 1), so seeing them cannot perturb the search.
|
|
335
|
+
//
|
|
336
|
+
// The withheld count is a CAPACITY fact, never a verdict: the sweep is
|
|
337
|
+
// breadth-first (largest regions first) and recognition still contributes
|
|
338
|
+
// every exact containment candidate, so the answer below is asserted to be
|
|
339
|
+
// the same either way — if a future change makes the cap actually lose
|
|
340
|
+
// reach, this pair of assertions forces it to be reported as a loss.
|
|
341
|
+
const LONG =
|
|
342
|
+
"the capital of France is Paris and Paris is a city on the river Seine in " +
|
|
343
|
+
"the north of the country and the river runs through the heart of the city " +
|
|
344
|
+
"past the tower and the museums and the wide avenues of the old quarters";
|
|
345
|
+
const run = async (probeK) => {
|
|
346
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
347
|
+
const mind = new Mind({
|
|
348
|
+
seed: 7,
|
|
349
|
+
store,
|
|
350
|
+
profile: true,
|
|
351
|
+
// The pivot's budget is ITS OWN since F4: `recallQueryK` no longer widens
|
|
352
|
+
// it, which is exactly what this test now pins.
|
|
353
|
+
pivotProbeK: probeK,
|
|
354
|
+
});
|
|
355
|
+
await mind.ingest([
|
|
356
|
+
["what is the capital of France", LONG],
|
|
357
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
358
|
+
["the Eiffel Tower", "the Eiffel Tower is in Paris"],
|
|
359
|
+
]);
|
|
360
|
+
const answer = String(
|
|
361
|
+
await mind.respondText("what is the capital of France famous for"),
|
|
362
|
+
).trim();
|
|
363
|
+
const c = mind.lastCost.counters;
|
|
364
|
+
await store.close();
|
|
365
|
+
return {
|
|
366
|
+
answer,
|
|
367
|
+
probes: c.pivotProbes ?? 0,
|
|
368
|
+
// RAW on purpose: the "withholds nothing" case must be able to observe
|
|
369
|
+
// the field's ABSENCE (test 6's convention drops zeros), so coercing here
|
|
370
|
+
// would make the assertion below unable to fail.
|
|
371
|
+
withheld: c.pivotBranchesUnprobed,
|
|
372
|
+
pivots: c.pivotSteps ?? 0,
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const wide = await run(64);
|
|
377
|
+
const tight = await run(12); // the default pivot budget
|
|
378
|
+
|
|
379
|
+
assert.ok(tight.probes > 0, "the sweep really ran");
|
|
380
|
+
assert.ok(
|
|
381
|
+
tight.withheld >= 1,
|
|
382
|
+
`the capacity the cap withheld must be counted (got ${tight.withheld})`,
|
|
383
|
+
);
|
|
384
|
+
assert.equal(
|
|
385
|
+
wide.withheld,
|
|
386
|
+
undefined,
|
|
387
|
+
"an allowance that probes every branch withholds nothing (zeros are dropped)",
|
|
388
|
+
);
|
|
389
|
+
assert.equal(tight.pivots, wide.pivots, "both runs took the same hop");
|
|
390
|
+
assert.equal(
|
|
391
|
+
tight.answer,
|
|
392
|
+
wide.answer,
|
|
393
|
+
"the cap withheld capacity, not the answer",
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("12. the extension's cost obeys the ladder's own inequality", async () => {
|
|
398
|
+
// The extension (reason()) is bounded by the material gate but was never
|
|
399
|
+
// PRICED. It now reports both facts — steps taken, and the bytes of
|
|
400
|
+
// uncovered material it was justified by — so the inequality the ladder
|
|
401
|
+
// would apply is checkable:
|
|
402
|
+
//
|
|
403
|
+
// steps · STEP < PASS · carried
|
|
404
|
+
//
|
|
405
|
+
// The gate accepts whenever a step carries a W-window, so the two agree for
|
|
406
|
+
// every extension with `steps ≤ (PASS/STEP) · carried`. This pins that
|
|
407
|
+
// inequality on a fixture where the extension really runs (the anti-vacuity
|
|
408
|
+
// guard below), and derives both constants from the ladder instead of
|
|
409
|
+
// spelling them out.
|
|
410
|
+
const { PASS, STEP } = await import("../dist/src/mind/graph-search.js");
|
|
411
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
412
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
413
|
+
await mind.ingest([
|
|
414
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
415
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
416
|
+
]);
|
|
417
|
+
await mind.respondText("What is the capital of France famous for");
|
|
418
|
+
const c = mind.lastCost.counters;
|
|
419
|
+
await store.close();
|
|
420
|
+
|
|
421
|
+
const steps = c.reasonSteps ?? 0;
|
|
422
|
+
const carried = c.reasonCarriedBytes ?? 0;
|
|
423
|
+
assert.ok(
|
|
424
|
+
steps >= 1,
|
|
425
|
+
`the extension must run for this to mean anything (got ${steps})`,
|
|
426
|
+
);
|
|
427
|
+
assert.ok(
|
|
428
|
+
carried >= 1,
|
|
429
|
+
`and it must be justified by real material (got ${carried})`,
|
|
430
|
+
);
|
|
431
|
+
assert.ok(
|
|
432
|
+
steps * STEP < PASS * carried,
|
|
433
|
+
`the extension spent ${steps} step(s) on ${carried} carried byte(s) — the ` +
|
|
434
|
+
`ladder would refuse that (STEP·${steps} vs PASS·${carried})`,
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("13. the fusion is counted when it FUSES, and claimed only then", async () => {
|
|
439
|
+
// `fuseAttention` is ENTERED whenever the query has a remainder ≥ W and
|
|
440
|
+
// returns `primary` untouched when there is nothing to bridge (`pieces.length
|
|
441
|
+
// === 1`). Entering and fusing are different facts — the rationale's own
|
|
442
|
+
// `enter` step fires on every call, so before `fuseRuns` the untraced view
|
|
443
|
+
// could not tell them apart — and the pipeline's outer note claimed the fusion
|
|
444
|
+
// REGARDLESS, measured false on 4 of the 5 queries below.
|
|
445
|
+
const CORPUS = [
|
|
446
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
447
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
448
|
+
["2+2", "2+2 equals 4"],
|
|
449
|
+
[
|
|
450
|
+
"The tallest tower in Paris",
|
|
451
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
452
|
+
],
|
|
453
|
+
];
|
|
454
|
+
const run = async (q) => {
|
|
455
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
456
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
457
|
+
await mind.ingest(CORPUS);
|
|
458
|
+
const steps = [];
|
|
459
|
+
await mind.respondText(q, (s) => steps.push(s));
|
|
460
|
+
const c = mind.lastCost.counters;
|
|
461
|
+
await store.close();
|
|
462
|
+
const pipelineSteps = steps.filter((s) =>
|
|
463
|
+
String(s.note ?? "").includes("reasoned forward")
|
|
464
|
+
);
|
|
465
|
+
return {
|
|
466
|
+
fused: c.fuseRuns,
|
|
467
|
+
claims:
|
|
468
|
+
pipelineSteps.filter((s) =>
|
|
469
|
+
String(s.note ?? "").includes("fused across")
|
|
470
|
+
).length,
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// The one query that really fuses (measured: four entries, one fusion).
|
|
475
|
+
const fusing = await run("2+2 and the Eiffel Tower");
|
|
476
|
+
assert.ok(
|
|
477
|
+
(fusing.fused ?? 0) >= 1,
|
|
478
|
+
`a query that fuses must be counted (fuseRuns=${fusing.fused})`,
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// The one that enters and bails — the anti-vacuity pair.
|
|
482
|
+
const bailing = await run("What is the capital of France famous for");
|
|
483
|
+
assert.equal(
|
|
484
|
+
bailing.fused,
|
|
485
|
+
undefined,
|
|
486
|
+
"a call that fused nothing must not be counted (zeros are dropped)",
|
|
487
|
+
);
|
|
488
|
+
assert.equal(
|
|
489
|
+
bailing.claims,
|
|
490
|
+
0,
|
|
491
|
+
"and no step may CLAIM a fusion that did not happen",
|
|
492
|
+
);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test("14. the climb publishes the peak that recall's gate reads", async () => {
|
|
496
|
+
// `Attention.peak` (types.ts) is the LARGEST single-region contribution behind
|
|
497
|
+
// an anchor, and mechanisms/recall.ts gates on it (`forest[0].peak > LN2`).
|
|
498
|
+
// The climb computed it, carried it into `ranked`, and the rationale showed
|
|
499
|
+
// everything EXCEPT it — the one decision-making quantity that was invisible.
|
|
500
|
+
//
|
|
501
|
+
// FAIL BEFORE: `anchor.peak` did not exist. The assertions below are a real
|
|
502
|
+
// relation, not a magic number: one contribution cannot exceed the sum of
|
|
503
|
+
// contributions, so `peak <= pooledVote`, and at least one anchor must have
|
|
504
|
+
// peak > 0 or the fixture never reached the quantity at all.
|
|
505
|
+
const CORPUS = [
|
|
506
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
507
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
508
|
+
["2+2", "2+2 equals 4"],
|
|
509
|
+
[
|
|
510
|
+
"The tallest tower in Paris",
|
|
511
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
512
|
+
],
|
|
513
|
+
];
|
|
514
|
+
const findAnchors = (o, depth = 0) => {
|
|
515
|
+
if (o === null || typeof o !== "object" || depth > 5) return null;
|
|
516
|
+
if (Array.isArray(o.anchors) && o.anchors.length) return o;
|
|
517
|
+
for (const v of Object.values(o)) {
|
|
518
|
+
const r = findAnchors(v, depth + 1);
|
|
519
|
+
if (r) return r;
|
|
520
|
+
}
|
|
521
|
+
return null;
|
|
522
|
+
};
|
|
523
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
524
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
525
|
+
await mind.ingest(CORPUS);
|
|
526
|
+
const steps = [];
|
|
527
|
+
await mind.respondText(
|
|
528
|
+
"capital of France and the tallest tower in Paris",
|
|
529
|
+
(s) => steps.push(s),
|
|
530
|
+
);
|
|
531
|
+
await store.close();
|
|
532
|
+
const step = steps.find((s) => findAnchors(s.data ?? s) !== null);
|
|
533
|
+
const td = step ? findAnchors(step.data ?? step) : null;
|
|
534
|
+
assert.ok(td, "the climb must report its anchors");
|
|
535
|
+
assert.ok(
|
|
536
|
+
td.anchors.every((a) => typeof a.peak === "number"),
|
|
537
|
+
"every anchor must publish its peak",
|
|
538
|
+
);
|
|
539
|
+
assert.ok(
|
|
540
|
+
td.anchors.some((a) => a.peak > 0),
|
|
541
|
+
`at least one anchor must have a real peak (got ${
|
|
542
|
+
JSON.stringify(td.anchors.map((a) => a.peak))
|
|
543
|
+
})`,
|
|
544
|
+
);
|
|
545
|
+
assert.ok(
|
|
546
|
+
td.anchors.every((a) => a.peak <= a.pooledVote),
|
|
547
|
+
"one contribution cannot exceed the sum of contributions",
|
|
548
|
+
);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test("15. the live record tells the two refusal gates apart", async () => {
|
|
552
|
+
// Item 2 of the open list: `below-natural-break` and `below-consensus-floor`
|
|
553
|
+
// always travelled TOGETHER in the fixtures measured before, so the margin
|
|
554
|
+
// proved the bar was tight but not that it was the cause. Measured over 53
|
|
555
|
+
// anchors on 12 queries, they DO separate: 9 anchors have the bar refusing
|
|
556
|
+
// what the natural break would accept, and none the other way round — but in
|
|
557
|
+
// every case both `passes*` flags were false, so the reasons alone could not
|
|
558
|
+
// show WHICH gate refused.
|
|
559
|
+
//
|
|
560
|
+
// This pins the one gate that refused, from the live commit record
|
|
561
|
+
// (`recordAnchor`, spec §8: decisions recorded as the gates apply them, never
|
|
562
|
+
// reconstructed). It asserts a PROPERTY, not an anchor id: at least one
|
|
563
|
+
// anchor must be rejected with the floor failing and the break PASSING —
|
|
564
|
+
// otherwise the fixture never reached the divergence it is here to pin.
|
|
565
|
+
const CORPUS = [
|
|
566
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
567
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
568
|
+
["2+2", "2+2 equals 4"],
|
|
569
|
+
[
|
|
570
|
+
"The tallest tower in Paris",
|
|
571
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
572
|
+
],
|
|
573
|
+
["Gustaf Molander", "Gustaf Molander was a Swedish film director"],
|
|
574
|
+
["Stockholm", "Stockholm is the capital of Sweden"],
|
|
575
|
+
];
|
|
576
|
+
const findAnchors = (o, depth = 0) => {
|
|
577
|
+
if (o === null || typeof o !== "object" || depth > 5) return null;
|
|
578
|
+
if (Array.isArray(o.anchors) && o.anchors.length) return o;
|
|
579
|
+
for (const v of Object.values(o)) {
|
|
580
|
+
const r = findAnchors(v, depth + 1);
|
|
581
|
+
if (r) return r;
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
};
|
|
585
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
586
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
587
|
+
await mind.ingest(CORPUS);
|
|
588
|
+
const steps = [];
|
|
589
|
+
await mind.respondText(
|
|
590
|
+
"Gustaf Molander and the tallest tower in Paris and 2+2",
|
|
591
|
+
(s) => steps.push(s),
|
|
592
|
+
);
|
|
593
|
+
await store.close();
|
|
594
|
+
const step = steps.find((s) => findAnchors(s.data ?? s) !== null);
|
|
595
|
+
const td = step ? findAnchors(step.data ?? step) : null;
|
|
596
|
+
assert.ok(td, "the climb must report its anchors");
|
|
597
|
+
|
|
598
|
+
const barRefusedOnly = td.anchors.filter((a) =>
|
|
599
|
+
(a.commit ?? {}).passesConsensusFloor === false &&
|
|
600
|
+
(a.commit ?? {}).passesNaturalBreak === true
|
|
601
|
+
);
|
|
602
|
+
assert.ok(
|
|
603
|
+
barRefusedOnly.length >= 1,
|
|
604
|
+
`the fixture must contain a floor-only refusal (got ${
|
|
605
|
+
JSON.stringify(
|
|
606
|
+
td.anchors.map((
|
|
607
|
+
a,
|
|
608
|
+
) => [
|
|
609
|
+
a.anchor,
|
|
610
|
+
a.commit?.passesNaturalBreak,
|
|
611
|
+
a.commit?.passesConsensusFloor,
|
|
612
|
+
]),
|
|
613
|
+
)
|
|
614
|
+
})`,
|
|
615
|
+
);
|
|
616
|
+
for (const a of barRefusedOnly) {
|
|
617
|
+
assert.equal((a.commit ?? {}).status, "rejected");
|
|
618
|
+
assert.ok(
|
|
619
|
+
(a.commit ?? {}).rejectionReasons.includes("below-consensus-floor"),
|
|
620
|
+
"the reason must name the floor",
|
|
621
|
+
);
|
|
622
|
+
assert.ok(
|
|
623
|
+
!(a.commit ?? {}).rejectionReasons.includes("below-natural-break"),
|
|
624
|
+
"and must NOT name a gate that passed",
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test("16. the climb's own search is timed", async () => {
|
|
630
|
+
// Item 3 of the open list: the climb's phases were timed
|
|
631
|
+
// (`climb.voteRegions`, `climb.structuralResonance`, `climb.crossRegion`) but
|
|
632
|
+
// the pooled derivation itself was NOT — `lightestDerivation(system)` ran
|
|
633
|
+
// outside any phase, so any cost or gain inside it was invisible. It is now
|
|
634
|
+
// wrapped in `meter.timeSync` (the synchronous seam: the graph search is
|
|
635
|
+
// synchronous, and wrapping it in a promise just to measure it would make the
|
|
636
|
+
// profiled path await where the unprofiled one does not).
|
|
637
|
+
//
|
|
638
|
+
// MEASURED: climb.derivation = 0.1-0.2 ms, i.e. ~0.2% of a ~85 ms response —
|
|
639
|
+
// the climb's cost is in voteRegions (5.8 ms), not in the search. The
|
|
640
|
+
// wall-clock min over 5 runs moved 43.1 → 43.0, 84.8 → 84.6, 27.7 → 27.8 ms:
|
|
641
|
+
// the two-snapshot overhead is not measurable.
|
|
642
|
+
//
|
|
643
|
+
// FAIL BEFORE: `climb.derivation` did not exist. The sibling assertion is the
|
|
644
|
+
// anti-vacuity guard — without it this would pass on a fixture that never
|
|
645
|
+
// climbed at all.
|
|
646
|
+
const CORPUS = [
|
|
647
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
648
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
649
|
+
["2+2", "2+2 equals 4"],
|
|
650
|
+
[
|
|
651
|
+
"The tallest tower in Paris",
|
|
652
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
653
|
+
],
|
|
654
|
+
];
|
|
655
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
656
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
657
|
+
await mind.ingest(CORPUS);
|
|
658
|
+
await mind.respondText("capital of France and the tallest tower in Paris");
|
|
659
|
+
const phases = mind.lastCost.phases;
|
|
660
|
+
await store.close();
|
|
661
|
+
|
|
662
|
+
const sibling = phases["climb.voteRegions"];
|
|
663
|
+
assert.ok(
|
|
664
|
+
sibling && sibling.calls >= 1,
|
|
665
|
+
`the fixture must reach the climb (phases: ${
|
|
666
|
+
Object.keys(phases).join(", ")
|
|
667
|
+
})`,
|
|
668
|
+
);
|
|
669
|
+
const search = phases["climb.derivation"];
|
|
670
|
+
assert.ok(search, "the climb's own search must be a timed phase");
|
|
671
|
+
assert.equal(search.calls, 1);
|
|
672
|
+
assert.ok(
|
|
673
|
+
typeof search.ms === "number" && search.ms >= 0,
|
|
674
|
+
`and it must carry a real duration (got ${search.ms})`,
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test("17. the bar and the climb's vote are in one dimension", async () => {
|
|
679
|
+
// `consensusFloor(N) = ln(N) + 1/2` is the POOLED-vote significance floor
|
|
680
|
+
// (thresholds.md §2: "each region contributes at most ln(N/c) <= ln(N)"), and
|
|
681
|
+
// attention.ts builds the vote on the same scale. The comparison in recall and
|
|
682
|
+
// in cast holds because the climb WEIGHTS BY IDF: `wf` is `direct ? df :
|
|
683
|
+
// combined ? idf + df : idf`, and the engine only runs the last one (DFMode's
|
|
684
|
+
// default "inverse", the mode every non-test caller uses). There, `wf === idf`,
|
|
685
|
+
// so the pooled vote IS the per-place reading the bar prices.
|
|
686
|
+
//
|
|
687
|
+
// FAIL BEFORE: nothing pinned this. Changing the default mode, or `wf`'s
|
|
688
|
+
// formula, would silently move `vote >= consensusFloor` out of the floor's
|
|
689
|
+
// dimension — and `direct`/`combined` are real reads (test/24 pins that their
|
|
690
|
+
// votes differ), not dead code.
|
|
691
|
+
const CORPUS = [
|
|
692
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
693
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
694
|
+
["2+2", "2+2 equals 4"],
|
|
695
|
+
[
|
|
696
|
+
"The tallest tower in Paris",
|
|
697
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
698
|
+
],
|
|
699
|
+
];
|
|
700
|
+
const findAnchors = (o, depth = 0) => {
|
|
701
|
+
if (o === null || typeof o !== "object" || depth > 5) return null;
|
|
702
|
+
if (Array.isArray(o.anchors) && o.anchors.length) return o;
|
|
703
|
+
for (const v of Object.values(o)) {
|
|
704
|
+
const r = findAnchors(v, depth + 1);
|
|
705
|
+
if (r) return r;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
};
|
|
709
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
710
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
711
|
+
await mind.ingest(CORPUS);
|
|
712
|
+
const steps = [];
|
|
713
|
+
await mind.respondText(
|
|
714
|
+
"capital of France and the tallest tower in Paris",
|
|
715
|
+
(s) => steps.push(s),
|
|
716
|
+
);
|
|
717
|
+
await store.close();
|
|
718
|
+
const step = steps.find((s) => findAnchors(s.data ?? s) !== null);
|
|
719
|
+
const td = step ? findAnchors(step.data ?? step) : null;
|
|
720
|
+
assert.ok(td, "the climb must report its anchors");
|
|
721
|
+
assert.equal(
|
|
722
|
+
td.config.mode,
|
|
723
|
+
"inverse",
|
|
724
|
+
"the engine's mode is the IDF-weighted one",
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
// Anti-vacuity: at least one anchor must actually stand on contributions.
|
|
728
|
+
const real = td.anchors.filter((a) => (a.contributingVotes ?? 0) >= 1);
|
|
729
|
+
assert.ok(real.length >= 1, "the fixture must reach an anchor with evidence");
|
|
730
|
+
|
|
731
|
+
for (const a of real) {
|
|
732
|
+
assert.equal(
|
|
733
|
+
a.pooledVote,
|
|
734
|
+
a.idfVote,
|
|
735
|
+
`the pooled vote and the per-place reading must coincide under the ` +
|
|
736
|
+
`engine's mode (anchor ${a.anchor}: pooled=${a.pooledVote} idf=${a.idfVote})`,
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("18. the remainder the pipeline decides on is visible", async () => {
|
|
742
|
+
// The remainder that licenses an extension or a fusion was computed at the
|
|
743
|
+
// decision point and never published: the reasoner's own counters say what it
|
|
744
|
+
// CARRIED, not what the grounding LEFT. Two write-only counters close that,
|
|
745
|
+
// and they are the instrument the structural question about the climb needs —
|
|
746
|
+
// "does an elected anchor's span fall inside what the cover left open?".
|
|
747
|
+
//
|
|
748
|
+
// FAIL BEFORE: the counters did not exist.
|
|
749
|
+
const CORPUS = [
|
|
750
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
751
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
752
|
+
];
|
|
753
|
+
const run = async (q) => {
|
|
754
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
755
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
756
|
+
await mind.ingest(CORPUS);
|
|
757
|
+
await mind.respondText(q);
|
|
758
|
+
const c = mind.lastCost.counters;
|
|
759
|
+
await store.close();
|
|
760
|
+
return {
|
|
761
|
+
spans: c.postGroundingRemainderSpans,
|
|
762
|
+
bytes: c.postGroundingRemainderBytes,
|
|
763
|
+
steps: c.reasonSteps,
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
// The pivoting query: the reasoner runs, so the remainder is non-empty
|
|
768
|
+
// (measured: reasonSteps 1, reasonCarriedBytes 11).
|
|
769
|
+
const pivot = await run("What is the capital of France famous for");
|
|
770
|
+
assert.ok(
|
|
771
|
+
(pivot.steps ?? 0) >= 1,
|
|
772
|
+
`the fixture must reach the post-grounding stage (reasonSteps=${pivot.steps})`,
|
|
773
|
+
);
|
|
774
|
+
assert.ok(
|
|
775
|
+
(pivot.spans ?? 0) >= 1,
|
|
776
|
+
`a licence to extend needs a remainder (spans=${pivot.spans})`,
|
|
777
|
+
);
|
|
778
|
+
assert.ok(
|
|
779
|
+
(pivot.bytes ?? 0) >= 1,
|
|
780
|
+
`and it is measured in bytes (bytes=${pivot.bytes})`,
|
|
781
|
+
);
|
|
782
|
+
|
|
783
|
+
// The directly-answered query: no remainder, so the zero convention drops both.
|
|
784
|
+
const direct = await run("What is the capital of France");
|
|
785
|
+
assert.equal(direct.spans, undefined);
|
|
786
|
+
assert.equal(direct.bytes, undefined);
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
test("19. the floor is read on the pooled vote, not on one region", async () => {
|
|
790
|
+
// thresholds.md §2 derives `consensusFloor` as the POOLED-vote floor (one
|
|
791
|
+
// maximally-specific region contributes at most ln N, and ln(N)+1/2 demands
|
|
792
|
+
// corroboration BEYOND one region). The engine reads it that way in three
|
|
793
|
+
// places (`commitVotes`, `recall`, `cast`), and this pins the practice with
|
|
794
|
+
// the measurement that decided it: across 27 anchors on 6 queries, all 11
|
|
795
|
+
// admissions cleared the floor by the SUM and NONE by `peak` alone.
|
|
796
|
+
//
|
|
797
|
+
// FAIL BEFORE: `types.ts` prescribed the opposite ("must read `peak`, not
|
|
798
|
+
// `vote`"), and nothing pinned what the engine actually does. A gate reading
|
|
799
|
+
// `peak` would refuse every root the engine elects.
|
|
800
|
+
const CORPUS = [
|
|
801
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
802
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
803
|
+
["2+2", "2+2 equals 4"],
|
|
804
|
+
[
|
|
805
|
+
"The tallest tower in Paris",
|
|
806
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
807
|
+
],
|
|
808
|
+
];
|
|
809
|
+
const findAnchors = (o, depth = 0) => {
|
|
810
|
+
if (o === null || typeof o !== "object" || depth > 5) return null;
|
|
811
|
+
if (Array.isArray(o.anchors) && o.anchors.length) return o;
|
|
812
|
+
for (const v of Object.values(o)) {
|
|
813
|
+
const r = findAnchors(v, depth + 1);
|
|
814
|
+
if (r) return r;
|
|
815
|
+
}
|
|
816
|
+
return null;
|
|
817
|
+
};
|
|
818
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
819
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
820
|
+
await mind.ingest(CORPUS);
|
|
821
|
+
const steps = [];
|
|
822
|
+
await mind.respondText(
|
|
823
|
+
"capital of France and the tallest tower in Paris",
|
|
824
|
+
(s) => steps.push(s),
|
|
825
|
+
);
|
|
826
|
+
await store.close();
|
|
827
|
+
const step = steps.find((s) => findAnchors(s.data ?? s) !== null);
|
|
828
|
+
const td = step ? findAnchors(step.data ?? step) : null;
|
|
829
|
+
assert.ok(td, "the climb must report its anchors");
|
|
830
|
+
const floor = td.config.consensusFloor;
|
|
831
|
+
|
|
832
|
+
const roots = td.anchors.filter((a) => (a.commit ?? {}).status === "root");
|
|
833
|
+
assert.ok(roots.length >= 1, "the fixture must elect at least one root");
|
|
834
|
+
for (const a of roots) {
|
|
835
|
+
assert.ok(
|
|
836
|
+
a.pooledVote >= floor,
|
|
837
|
+
`a root must clear the floor by the pooled vote (${a.pooledVote} vs ${floor})`,
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
const bySumOnly = roots.filter((a) => a.peak < floor);
|
|
841
|
+
assert.ok(
|
|
842
|
+
bySumOnly.length >= 1,
|
|
843
|
+
`this fixture must contain a root that only the SUM admits (peaks: ${
|
|
844
|
+
JSON.stringify(roots.map((a) => a.peak))
|
|
845
|
+
} vs floor ${floor})`,
|
|
846
|
+
);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test("20. the non-IDF weighting modes never flip a gate verdict", async () => {
|
|
850
|
+
// `consensusFloor` is derived for the POOLED IDF-weighted vote. The other two
|
|
851
|
+
// weighting modes deviate — and the deviation is TWO-SIDED and bounded by
|
|
852
|
+
// `ln 2`: `direct` deflates a region (ln(1+c) < ln(N/c) for small c) and
|
|
853
|
+
// `combined` inflates it. MEASURED across 8 anchors in 5 queries, running the
|
|
854
|
+
// same climb in all three modes: gating on the mode-dependent `vote` DID flip a
|
|
855
|
+
// verdict — anchor 87 of the second query (inverse 2.682 admitted, direct 1.468
|
|
856
|
+
// refused, floor 2.292). The fix is to gate on the IDF sum, which is
|
|
857
|
+
// mode-independent by construction and EQUALS `vote` in the engine's own mode,
|
|
858
|
+
// so no verdict in `inverse` moves.
|
|
859
|
+
//
|
|
860
|
+
// The anchor that makes this test non-vacuous is #148 of the second query: its
|
|
861
|
+
// `combined` reading sits ABOVE the floor and its `direct` reading BELOW, while
|
|
862
|
+
// its inverse reading is above — i.e. both deviations are present in the
|
|
863
|
+
// fixture, and still neither crosses the floor in the wrong place.
|
|
864
|
+
const CORPUS = [
|
|
865
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
866
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
867
|
+
["2+2", "2+2 equals 4"],
|
|
868
|
+
[
|
|
869
|
+
"The tallest tower in Paris",
|
|
870
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
871
|
+
],
|
|
872
|
+
["Gustaf Molander", "Gustaf Molander was a Swedish film director"],
|
|
873
|
+
["Stockholm", "Stockholm is the capital of Sweden"],
|
|
874
|
+
];
|
|
875
|
+
const { corpusN } = await import("../dist/src/mind/traverse.js");
|
|
876
|
+
const { consensusFloor } = await import("../dist/src/geometry.js");
|
|
877
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
878
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
879
|
+
await mind.ingest(CORPUS);
|
|
880
|
+
const queries = [
|
|
881
|
+
"capital of France and the tallest tower in Paris",
|
|
882
|
+
"2+2 and the Eiffel Tower",
|
|
883
|
+
"Gustaf Molander and the tallest tower in Paris and 2+2",
|
|
884
|
+
];
|
|
885
|
+
let anchors = 0;
|
|
886
|
+
let straddling = 0;
|
|
887
|
+
for (const q of queries) {
|
|
888
|
+
const enc = new TextEncoder().encode(q);
|
|
889
|
+
const floor = consensusFloor(corpusN(mind));
|
|
890
|
+
const rootsOf = {};
|
|
891
|
+
const votes = {};
|
|
892
|
+
const idfOf = {};
|
|
893
|
+
for (const mode of ["inverse", "direct", "combined"]) {
|
|
894
|
+
const got = await mind.climbAttention(enc, 24, mode);
|
|
895
|
+
rootsOf[mode] = got.map((a) => a.anchor).sort((x, y) => x - y);
|
|
896
|
+
votes[mode] = new Map(got.map((a) => [a.anchor, a.vote]));
|
|
897
|
+
// `idfVote` is on Attention; the gate reads it.
|
|
898
|
+
idfOf[mode] = new Map(got.map((a) => [a.anchor, a.idfVote]));
|
|
899
|
+
}
|
|
900
|
+
// THE PROPERTY pinned here is the GATE's, per anchor: the IDF reading — the
|
|
901
|
+
// quantity `consensusFloor` is derived for — is mode-independent, so an
|
|
902
|
+
// anchor's floor verdict cannot change with the weighting. The ELECTED SET
|
|
903
|
+
// is deliberately NOT pinned: the election walks the ranked anchors in `vote`
|
|
904
|
+
// order, which is mode-dependent by design (that order is a legitimate
|
|
905
|
+
// ranking, used as an order by `confluence`), and overlap resolution can
|
|
906
|
+
// therefore elect a different anchor in another mode.
|
|
907
|
+
const first = [...votes.inverse.keys()];
|
|
908
|
+
for (const id of first) {
|
|
909
|
+
const iv = idfOf.inverse.get(id);
|
|
910
|
+
assert.equal(
|
|
911
|
+
idfOf.direct.get(id),
|
|
912
|
+
iv,
|
|
913
|
+
`the IDF reading must not depend on the mode (anchor ${id})`,
|
|
914
|
+
);
|
|
915
|
+
assert.equal(
|
|
916
|
+
idfOf.combined.get(id),
|
|
917
|
+
iv,
|
|
918
|
+
`the IDF reading must not depend on the mode (anchor ${id})`,
|
|
919
|
+
);
|
|
920
|
+
assert.equal(
|
|
921
|
+
(idfOf.direct.get(id) ?? 0) >= floor,
|
|
922
|
+
(iv ?? 0) >= floor,
|
|
923
|
+
`the floor verdict must not depend on the mode (anchor ${id})`,
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
// Anti-vacuity: the fixture must contain an anchor whose mode-dependent
|
|
927
|
+
// VOTE straddles the floor on opposite sides, so the deviations are present.
|
|
928
|
+
for (const id of rootsOf.inverse) {
|
|
929
|
+
anchors++;
|
|
930
|
+
const c = votes.combined.get(id) ?? 0;
|
|
931
|
+
const d = votes.direct.get(id) ?? 0;
|
|
932
|
+
if ((c >= floor) !== (d >= floor)) straddling++;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
await store.close();
|
|
936
|
+
assert.ok(
|
|
937
|
+
anchors >= 4,
|
|
938
|
+
`the fixture must reach several anchors (${anchors})`,
|
|
939
|
+
);
|
|
940
|
+
assert.ok(
|
|
941
|
+
straddling >= 1,
|
|
942
|
+
`the fixture must contain an anchor whose two deviations straddle the floor, ` +
|
|
943
|
+
`else this test pins nothing (straddling=${straddling})`,
|
|
944
|
+
);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
test("21. the price's second term has one definition, and it is the complement", async () => {
|
|
948
|
+
// `unaccountedBytes` collapsed four copies of the same `reduce` (audit, etapa 6).
|
|
949
|
+
// Since the value did NOT change, the lot is only pinned if something would
|
|
950
|
+
// fail when the helper sums the wrong thing — so this test uses an input where
|
|
951
|
+
// the two candidate readings DIFFER.
|
|
952
|
+
const { unexplainedSpans, unaccountedBytes } = await import(
|
|
953
|
+
"../dist/src/mind/rationale.js"
|
|
954
|
+
);
|
|
955
|
+
// Empty accounted ⇒ the whole query is unaccounted.
|
|
956
|
+
assert.equal(unaccountedBytes(unexplainedSpans(10, [])), 10);
|
|
957
|
+
// Full cover ⇒ nothing is unaccounted (this is the honest-silence end).
|
|
958
|
+
assert.equal(unaccountedBytes(unexplainedSpans(10, [[0, 10]])), 0);
|
|
959
|
+
// THE DISCRIMINATING CASE — OVERLAPPING accounted spans:
|
|
960
|
+
// [[0,6],[4,10]] covers the union [0,10) = 10 bytes, while summing their
|
|
961
|
+
// EXTENSIONS gives 6 + 6 = 12. The price's term is the COMPLEMENT, so it
|
|
962
|
+
// must be 0 here; a helper that summed extensions or accounted lengths
|
|
963
|
+
// would answer 12 (or 6+6) and fail.
|
|
964
|
+
const overlapping = [[0, 6], [4, 10]];
|
|
965
|
+
const gaps = unexplainedSpans(10, overlapping);
|
|
966
|
+
assert.equal(
|
|
967
|
+
gaps.length,
|
|
968
|
+
0,
|
|
969
|
+
`the union fully covers: gaps=${JSON.stringify(gaps)}`,
|
|
970
|
+
);
|
|
971
|
+
assert.equal(unaccountedBytes(gaps), 0);
|
|
972
|
+
// And the complement is additive against the covered union: half-covered.
|
|
973
|
+
assert.equal(unaccountedBytes(unexplainedSpans(10, [[0, 4], [2, 6]])), 4);
|
|
974
|
+
// Nothing is ever negative or double-counted on a mixed input.
|
|
975
|
+
assert.equal(
|
|
976
|
+
unaccountedBytes(unexplainedSpans(20, [[5, 8], [5, 8], [12, 15]])),
|
|
977
|
+
14,
|
|
978
|
+
);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("22. the trace publishes the bar the margin gate ACTUALLY applied", async () => {
|
|
982
|
+
// The margin gate scales its bar by what the region does NOT address:
|
|
983
|
+
// `estimatorNoise(D) * (1 - cov)`. The REJECTION path recorded that scaled
|
|
984
|
+
// bar; the "voted" path recorded the RAW `estimatorNoise(D)` — so a region
|
|
985
|
+
// that PASSED was reported closer to its limit than it was. The field lives
|
|
986
|
+
// on the REGION trace (`recordRegion`), which is where the gate runs.
|
|
987
|
+
//
|
|
988
|
+
// ANTI-VACUITY, two guards: (1) the fixture must contain at least one region
|
|
989
|
+
// that reached the margin gate at all — on a corpus where no approximate
|
|
990
|
+
// region gets there, this test would pin nothing; (2) among those, at least
|
|
991
|
+
// one VOTED region must have been judged with a bar BELOW the raw one
|
|
992
|
+
// (cov > 0 on the passing side). On the old code every voted floor was raw,
|
|
993
|
+
// so guard (2) fails — which is what makes this test discriminating.
|
|
994
|
+
const { Mind, SQliteStore } = await import("../dist/src/index.js");
|
|
995
|
+
const { estimatorNoise } = await import("../dist/src/geometry.js");
|
|
996
|
+
const achar = (o, d = 0) => {
|
|
997
|
+
if (o === null || typeof o !== "object" || d > 5) return null;
|
|
998
|
+
if (Array.isArray(o.regions) && o.regions.length) return o;
|
|
999
|
+
for (const v of Object.values(o)) {
|
|
1000
|
+
const r = achar(v, d + 1);
|
|
1001
|
+
if (r) return r;
|
|
1002
|
+
}
|
|
1003
|
+
return null;
|
|
1004
|
+
};
|
|
1005
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
1006
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
1007
|
+
const P = [
|
|
1008
|
+
"France",
|
|
1009
|
+
"Sweden",
|
|
1010
|
+
"Japan",
|
|
1011
|
+
"Peru",
|
|
1012
|
+
"Kenya",
|
|
1013
|
+
"Nepal",
|
|
1014
|
+
"Chile",
|
|
1015
|
+
"Oman",
|
|
1016
|
+
"Fiji",
|
|
1017
|
+
"Malta",
|
|
1018
|
+
"Cuba",
|
|
1019
|
+
"Laos",
|
|
1020
|
+
];
|
|
1021
|
+
const CORPUS = [];
|
|
1022
|
+
for (const p of P) {
|
|
1023
|
+
CORPUS.push([
|
|
1024
|
+
`What is the capital of ${p}`,
|
|
1025
|
+
`The capital of ${p} is the city of ${p}town`,
|
|
1026
|
+
]);
|
|
1027
|
+
CORPUS.push([
|
|
1028
|
+
`The capital of ${p} is the city of ${p}town`,
|
|
1029
|
+
`The ${p}town parliament sits by the river`,
|
|
1030
|
+
]);
|
|
1031
|
+
}
|
|
1032
|
+
CORPUS.push([
|
|
1033
|
+
"What is the tallest tower in Paris",
|
|
1034
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
1035
|
+
]);
|
|
1036
|
+
CORPUS.push(["2+2", "2+2 equals 4"]);
|
|
1037
|
+
await mind.ingest(CORPUS);
|
|
1038
|
+
const q =
|
|
1039
|
+
"how do you say thank you in the language of Nepal and in the language of Oman";
|
|
1040
|
+
const steps = [];
|
|
1041
|
+
await mind.respondText(q, (x) => steps.push(x));
|
|
1042
|
+
const st = steps.find((x) => achar(x.data ?? x) !== null);
|
|
1043
|
+
const td = st ? achar(st.data ?? st) : null;
|
|
1044
|
+
const raw = estimatorNoise(store.D);
|
|
1045
|
+
const regions = td?.regions ?? [];
|
|
1046
|
+
const withFloor = regions.filter((r) =>
|
|
1047
|
+
r.contrastiveNoiseFloor !== undefined
|
|
1048
|
+
);
|
|
1049
|
+
assert.ok(
|
|
1050
|
+
withFloor.length >= 1,
|
|
1051
|
+
`the fixture must reach the margin gate at all (regions=${regions.length}, ` +
|
|
1052
|
+
`withFloor=${withFloor.length})`,
|
|
1053
|
+
);
|
|
1054
|
+
for (const r of withFloor) {
|
|
1055
|
+
assert.ok(
|
|
1056
|
+
r.contrastiveNoiseFloor <= raw + 1e-12,
|
|
1057
|
+
`the applied bar never exceeds the raw one (${r.contrastiveNoiseFloor} > ${raw})`,
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
// guard (2): the outcome field names which regions PASSED.
|
|
1061
|
+
const voted = withFloor.filter((r) => r.ordinaryVoteProduced === true);
|
|
1062
|
+
assert.ok(
|
|
1063
|
+
voted.length >= 1,
|
|
1064
|
+
`the fixture must contain a region that PASSED the gate and reached the margin ` +
|
|
1065
|
+
`(withFloor=${withFloor.length}); fields present: ${
|
|
1066
|
+
JSON.stringify(Object.keys(withFloor[0] ?? {}))
|
|
1067
|
+
}`,
|
|
1068
|
+
);
|
|
1069
|
+
assert.ok(
|
|
1070
|
+
voted.some((r) => r.contrastiveNoiseFloor < raw - 1e-12),
|
|
1071
|
+
`at least one VOTED region must carry a bar BELOW the raw one (cov > 0 on the ` +
|
|
1072
|
+
`passing side); otherwise this test pins nothing ` +
|
|
1073
|
+
`(voted=${voted.length}, floors=${
|
|
1074
|
+
voted.map((r) => r.contrastiveNoiseFloor).join(",")
|
|
1075
|
+
} raw=${raw})`,
|
|
1076
|
+
);
|
|
1077
|
+
await store.close();
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
test("23. the payload carries every section the draft supplies", async () => {
|
|
1081
|
+
// `ClimbConsensusData` is assembled by hand from `TraceDraft` (see
|
|
1082
|
+
// traceAttention): four sections are plain pass-throughs (`regions`,
|
|
1083
|
+
// `saturation`, `pooling`, `anchors`), one comes from the config
|
|
1084
|
+
// (`reaches`), and one is REBUILT from the summary plus two draft arrays
|
|
1085
|
+
// (`crossRegion`). A section added to the draft and missed in that assembly
|
|
1086
|
+
// vanishes from the payload SILENTLY — which is the drift `visited?` records
|
|
1087
|
+
// ("Absent on payloads recorded before this field existed"). The draft
|
|
1088
|
+
// itself is not observable from outside, but the PAYLOAD is: this test runs a
|
|
1089
|
+
// climb that produces each pass-through section and asserts it arrives.
|
|
1090
|
+
const { Mind, SQliteStore } = await import("../dist/src/index.js");
|
|
1091
|
+
const achar = (o, d = 0) => {
|
|
1092
|
+
if (o === null || typeof o !== "object" || d > 5) return null;
|
|
1093
|
+
if (o.config && Array.isArray(o.anchors)) return o;
|
|
1094
|
+
for (const v of Object.values(o)) {
|
|
1095
|
+
const r = achar(v, d + 1);
|
|
1096
|
+
if (r) return r;
|
|
1097
|
+
}
|
|
1098
|
+
return null;
|
|
1099
|
+
};
|
|
1100
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
1101
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
1102
|
+
await mind.ingest([
|
|
1103
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
1104
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
1105
|
+
[
|
|
1106
|
+
"The tallest tower in Paris",
|
|
1107
|
+
"The tallest tower in Paris is the Eiffel Tower",
|
|
1108
|
+
],
|
|
1109
|
+
["2+2", "2+2 equals 4"],
|
|
1110
|
+
["Gustaf Molander", "Gustaf Molander was a Swedish film director"],
|
|
1111
|
+
["Stockholm", "Stockholm is the capital of Sweden"],
|
|
1112
|
+
]);
|
|
1113
|
+
const q = "the capital of France and the tallest tower in Paris and 2+2";
|
|
1114
|
+
const steps = [];
|
|
1115
|
+
await mind.respondText(q, (x) => steps.push(x));
|
|
1116
|
+
const st = steps.find((x) => achar(x.data ?? x) !== null);
|
|
1117
|
+
const td = st ? achar(st.data ?? st) : null;
|
|
1118
|
+
await store.close();
|
|
1119
|
+
assert.ok(td !== null, "the traced climb must emit a payload at all");
|
|
1120
|
+
for (const k of ["config", "candidates", "result"]) {
|
|
1121
|
+
assert.ok(td[k] !== undefined, `the payload must always carry \`${k}\``);
|
|
1122
|
+
}
|
|
1123
|
+
// The four pass-through sections, plus the one the cfg supplies.
|
|
1124
|
+
const missing = [];
|
|
1125
|
+
for (const k of ["regions", "saturation", "pooling", "anchors", "reaches"]) {
|
|
1126
|
+
if (td[k] === undefined) missing.push(k);
|
|
1127
|
+
}
|
|
1128
|
+
assert.deepEqual(
|
|
1129
|
+
missing,
|
|
1130
|
+
[],
|
|
1131
|
+
`the payload is missing section(s) the draft/config supplied: ${
|
|
1132
|
+
missing.join(", ")
|
|
1133
|
+
} ` +
|
|
1134
|
+
`(present: ${Object.keys(td).join(", ")})`,
|
|
1135
|
+
);
|
|
1136
|
+
assert.ok(
|
|
1137
|
+
(td.regions ?? []).length >= 1 && (td.anchors ?? []).length >= 1,
|
|
1138
|
+
`the fixture must produce regions and anchors (regions=${
|
|
1139
|
+
(td.regions ?? []).length
|
|
1140
|
+
}, ` +
|
|
1141
|
+
`anchors=${(td.anchors ?? []).length})`,
|
|
1142
|
+
);
|
|
1143
|
+
});
|