@hviana/sema 0.8.0 → 0.8.2
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 +22 -1
- package/DATASETS.md +1 -1
- package/dist/example/train_base/config.js +2 -2
- package/dist/example/train_base/corpora/massive.js +1 -1
- package/dist/example/train_base/readers.js +1 -1
- package/dist/src/config.d.ts +17 -0
- package/dist/src/config.js +18 -0
- package/dist/src/geometry.d.ts +10 -10
- package/dist/src/geometry.js +25 -24
- package/dist/src/meter.d.ts +29 -12
- package/dist/src/meter.js +58 -14
- package/dist/src/mind/attention.js +12 -12
- package/dist/src/mind/bridge.d.ts +8 -8
- package/dist/src/mind/bridge.js +33 -32
- package/dist/src/mind/corpus.d.ts +40 -0
- package/dist/src/mind/corpus.js +149 -0
- package/dist/src/mind/graph-search.d.ts +7 -8
- package/dist/src/mind/graph-search.js +244 -32
- package/dist/src/mind/index.d.ts +3 -1
- package/dist/src/mind/index.js +1 -0
- package/dist/src/mind/junction.d.ts +1 -1
- package/dist/src/mind/junction.js +8 -8
- package/dist/src/mind/learning.js +36 -35
- package/dist/src/mind/match.d.ts +8 -3
- package/dist/src/mind/match.js +156 -71
- package/dist/src/mind/mechanisms/cast.js +18 -2
- package/dist/src/mind/mechanisms/cover.js +19 -12
- package/dist/src/mind/mechanisms/prefix-completion.js +24 -24
- package/dist/src/mind/mechanisms/recall.js +38 -40
- package/dist/src/mind/mechanisms/reference.js +16 -16
- package/dist/src/mind/mind.d.ts +61 -7
- package/dist/src/mind/mind.js +72 -2
- package/dist/src/mind/pipeline-mechanism.d.ts +10 -8
- package/dist/src/mind/pipeline-mechanism.js +25 -21
- package/dist/src/mind/pipeline.d.ts +9 -9
- package/dist/src/mind/pipeline.js +49 -29
- package/dist/src/mind/primitives.d.ts +5 -5
- package/dist/src/mind/primitives.js +5 -5
- package/dist/src/mind/reasoning.d.ts +5 -1
- package/dist/src/mind/reasoning.js +54 -1
- package/dist/src/mind/recognition.d.ts +14 -13
- package/dist/src/mind/recognition.js +23 -23
- package/dist/src/mind/resonance.js +21 -21
- package/dist/src/mind/traverse.d.ts +54 -52
- package/dist/src/mind/traverse.js +83 -73
- package/dist/src/mind/types.d.ts +26 -4
- package/dist/src/store.d.ts +12 -12
- package/dist/src/store.js +12 -12
- package/docs/INDEX.md +2 -2
- package/docs/architecture/exact-vs-approximate.md +2 -1
- package/docs/architecture/fold-contract.md +1 -1
- package/docs/failures/tempting-but-wrong.md +33 -5
- package/docs/harness/gates.md +7 -7
- package/example/train_base/config.ts +2 -2
- package/example/train_base/corpora/massive.ts +1 -1
- package/example/train_base/readers.ts +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/config.ts +35 -0
- package/src/geometry.ts +25 -24
- package/src/meter.ts +61 -14
- package/src/mind/attention.ts +12 -12
- package/src/mind/bridge.ts +33 -32
- package/src/mind/corpus.ts +202 -0
- package/src/mind/graph-search.ts +261 -31
- package/src/mind/index.ts +8 -1
- package/src/mind/junction.ts +8 -8
- package/src/mind/learning.ts +36 -35
- package/src/mind/match.ts +163 -73
- package/src/mind/mechanisms/cast.ts +17 -1
- package/src/mind/mechanisms/cover.ts +18 -12
- package/src/mind/mechanisms/prefix-completion.ts +24 -24
- package/src/mind/mechanisms/recall.ts +38 -40
- package/src/mind/mechanisms/reference.ts +16 -16
- package/src/mind/mind.ts +129 -7
- package/src/mind/pipeline-mechanism.ts +25 -21
- package/src/mind/pipeline.ts +63 -38
- package/src/mind/primitives.ts +5 -5
- package/src/mind/reasoning.ts +55 -0
- package/src/mind/recognition.ts +23 -23
- package/src/mind/resonance.ts +21 -21
- package/src/mind/traverse.ts +83 -73
- package/src/mind/types.ts +30 -4
- package/src/store.ts +20 -20
- package/test/08-storage.test.mjs +1 -1
- package/test/100-complete-grounding-trace.test.mjs +109 -0
- package/test/101-alignment-gap-bound.test.mjs +106 -0
- package/test/102-production-composes-at-scale.test.mjs +110 -0
- package/test/103-alignment-gap-budget.test.mjs +89 -0
- package/test/104-composition-is-reported.test.mjs +90 -0
- package/test/105-derive-through-reports-its-refusal.test.mjs +113 -0
- package/test/106-the-join-fires.test.mjs +94 -0
- package/test/107-the-join-is-counted.test.mjs +81 -0
- package/test/108-the-join-chains.test.mjs +78 -0
- package/test/109-the-pivot-is-counted.test.mjs +60 -0
- package/test/110-the-reasoner-stops-when-the-question-is-answered.test.mjs +91 -0
- package/test/111-the-cover-assembly-is-counted.test.mjs +74 -0
- package/test/112-the-exploration-does-not-grow-with-the-hub.test.mjs +89 -0
- package/test/113-the-rationale-payload-is-bounded.test.mjs +84 -0
- package/test/114-alignment-budget-is-per-sweep.test.mjs +93 -0
- package/test/116-the-extension-is-gated-by-the-pipelines-own-remainder.test.mjs +100 -0
- package/test/117-corpus-search.test.mjs +171 -0
- package/test/14-scaling.test.mjs +10 -7
- package/test/35-prefix-edge.test.mjs +1 -1
- package/test/40-choosenext-scale-guard.test.mjs +16 -17
- package/test/56-bridge-identity-admission.test.mjs +6 -6
- package/test/70-prefix-completion.test.mjs +4 -3
- package/test/72-prefix-candidate-supply.test.mjs +3 -3
- package/test/73-scaffolding-only-bridge-abstains.test.mjs +6 -6
- package/test/75-multiturn-context-optimisation.test.mjs +5 -5
- package/test/76-reference-binding.test.mjs +6 -1
- package/test/84-composed-answer-honesty.test.mjs +5 -6
- package/test/88-dependency-footprint.test.mjs +1 -1
- package/test/89-completion-recursion.test.mjs +47 -19
- package/test/90-connector-read-cap.test.mjs +10 -8
- package/test/93-regime-prediction.test.mjs +10 -10
- package/test/94-cross-region-budget.test.mjs +2 -2
- package/test/95-wide-resonance-removed.test.mjs +8 -7
- package/test/96-bytes-walk-termination.test.mjs +3 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// 117-corpus-search.test.mjs — reading the trained memory back out of the DAG.
|
|
2
|
+
//
|
|
3
|
+
// WHAT IS PINNED. Two methods and their division of labour:
|
|
4
|
+
// • `searchCorpus(bytes, limit?)` — MULTIMODAL: bytes in, bytes out, no notion
|
|
5
|
+
// of text or encoding anywhere in it;
|
|
6
|
+
// • `searchCorpusText(text, limit?)` — the text case, which encodes, calls the
|
|
7
|
+
// multimodal one, and decodes. The search itself exists ONCE (src/mind/
|
|
8
|
+
// corpus.ts, over the machinery an answer already uses: `recognise` for the
|
|
9
|
+
// resolved subtrees, `edgeAncestors` for the climb, `nextFirst` for the
|
|
10
|
+
// continuation).
|
|
11
|
+
//
|
|
12
|
+
// Both are deterministic (same seed, same order, same query ⇒ byte-identical
|
|
13
|
+
// results), both report a miss as a STATE in the byte layer and as prose only in
|
|
14
|
+
// the text layer, and browsing takes the caller's own offset instead of a random
|
|
15
|
+
// draw.
|
|
16
|
+
|
|
17
|
+
import { test } from "node:test";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
import { Mind, SQliteStore } from "../dist/src/index.js";
|
|
20
|
+
|
|
21
|
+
const enc = new TextEncoder();
|
|
22
|
+
const dec = new TextDecoder();
|
|
23
|
+
|
|
24
|
+
/** A small deposited corpus: three experience pairs, no trained store needed. */
|
|
25
|
+
async function fixture() {
|
|
26
|
+
const mind = new Mind({
|
|
27
|
+
seed: 7,
|
|
28
|
+
store: new SQliteStore({ path: ":memory:" }),
|
|
29
|
+
});
|
|
30
|
+
await mind.ingest([
|
|
31
|
+
["the capital of France", "Paris is the capital of France."],
|
|
32
|
+
["the capital of Portugal", "Lisbon is the capital of Portugal."],
|
|
33
|
+
["who wrote Hamlet", "Shakespeare wrote Hamlet."],
|
|
34
|
+
]);
|
|
35
|
+
return mind;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const asText = (b) => dec.decode(b).replace(/\0+/g, "").trim();
|
|
39
|
+
|
|
40
|
+
test("the multimodal search takes bytes and returns bytes", async () => {
|
|
41
|
+
const mind = await fixture();
|
|
42
|
+
const result = mind.searchCorpus(enc.encode("the capital of France"));
|
|
43
|
+
assert.ok(result.pairs.length > 0, "the deposited pair must be found");
|
|
44
|
+
const pair = result.pairs[0];
|
|
45
|
+
assert.ok(pair.context instanceof Uint8Array, "context is BYTES, not text");
|
|
46
|
+
assert.ok(pair.continuation instanceof Uint8Array);
|
|
47
|
+
assert.ok(typeof pair.contextId === "number");
|
|
48
|
+
assert.ok(asText(pair.context).includes("capital"));
|
|
49
|
+
assert.equal(result.browsed, false);
|
|
50
|
+
assert.equal(result.miss, "matched");
|
|
51
|
+
assert.ok(result.totalContexts > 0, "the store's own context count is read");
|
|
52
|
+
await mind.store.close();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("the text helper is the SAME search, converted", async () => {
|
|
56
|
+
const mind = await fixture();
|
|
57
|
+
const bytes = mind.searchCorpus(enc.encode("the capital of France"));
|
|
58
|
+
const text = mind.searchCorpusText("the capital of France");
|
|
59
|
+
assert.equal(
|
|
60
|
+
text.pairs.length,
|
|
61
|
+
bytes.pairs.length,
|
|
62
|
+
"one search, two views — the helper must not run a second one",
|
|
63
|
+
);
|
|
64
|
+
assert.equal(text.pairs[0].contextId, bytes.pairs[0].contextId);
|
|
65
|
+
assert.equal(typeof text.pairs[0].context, "string");
|
|
66
|
+
assert.ok(text.pairs[0].context.includes("capital"));
|
|
67
|
+
assert.equal(text.note, undefined, "a match needs no note");
|
|
68
|
+
await mind.store.close();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("both are deterministic across identical calls", async () => {
|
|
72
|
+
const mind = await fixture();
|
|
73
|
+
const a = mind.searchCorpus(enc.encode("the capital of France"));
|
|
74
|
+
const b = mind.searchCorpus(enc.encode("the capital of France"));
|
|
75
|
+
assert.deepEqual(
|
|
76
|
+
a.pairs.map((p) => [p.contextId, p.continuationId, asText(p.context)]),
|
|
77
|
+
b.pairs.map((p) => [p.contextId, p.continuationId, asText(p.context)]),
|
|
78
|
+
"same store + same query ⇒ the same pairs in the same order",
|
|
79
|
+
);
|
|
80
|
+
assert.deepEqual(
|
|
81
|
+
mind.searchCorpusText("the capital of France").pairs,
|
|
82
|
+
mind.searchCorpusText("the capital of France").pairs,
|
|
83
|
+
);
|
|
84
|
+
await mind.store.close();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("a miss is a state in bytes and prose only in text", async () => {
|
|
88
|
+
const mind = await fixture();
|
|
89
|
+
const bytes = mind.searchCorpus(enc.encode("zzzq nothing at all"));
|
|
90
|
+
assert.equal(bytes.pairs.length, 0);
|
|
91
|
+
assert.ok(
|
|
92
|
+
bytes.miss === "nothing-resolved" || bytes.miss === "no-continuations",
|
|
93
|
+
`the byte layer reports a STATE, got ${bytes.miss}`,
|
|
94
|
+
);
|
|
95
|
+
assert.equal(bytes.note, undefined, "no prose in the byte layer");
|
|
96
|
+
const text = mind.searchCorpusText("zzzq nothing at all");
|
|
97
|
+
assert.equal(text.pairs.length, 0);
|
|
98
|
+
assert.equal(typeof text.note, "string", "the text layer says it in words");
|
|
99
|
+
await mind.store.close();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("determinism holds ACROSS instances, not just across calls", async () => {
|
|
103
|
+
// The invariant is about the engine, not about one object: the same seed,
|
|
104
|
+
// the same deposit order and the same query must give byte-identical results
|
|
105
|
+
// from a FRESH Mind over a fresh store built the same way — both for a search
|
|
106
|
+
// (whose ids come from the store's intern order) and for a browse.
|
|
107
|
+
const a = await fixture();
|
|
108
|
+
const b = await fixture();
|
|
109
|
+
const shape = (r) =>
|
|
110
|
+
r.pairs.map((p) => [
|
|
111
|
+
p.contextId,
|
|
112
|
+
p.continuationId,
|
|
113
|
+
p.matchedBytes,
|
|
114
|
+
Array.from(p.context),
|
|
115
|
+
Array.from(p.continuation),
|
|
116
|
+
]);
|
|
117
|
+
assert.deepEqual(
|
|
118
|
+
shape(a.searchCorpus(enc.encode("the capital of France"))),
|
|
119
|
+
shape(b.searchCorpus(enc.encode("the capital of France"))),
|
|
120
|
+
"two fresh minds over identically-built stores must agree byte for byte",
|
|
121
|
+
);
|
|
122
|
+
assert.deepEqual(
|
|
123
|
+
shape(a.sampleCorpus(2)),
|
|
124
|
+
shape(b.sampleCorpus(2)),
|
|
125
|
+
"and browsing must agree too — no draw from outside the seed",
|
|
126
|
+
);
|
|
127
|
+
assert.deepEqual(
|
|
128
|
+
shape(a.sampleCorpus(2, 0.25)),
|
|
129
|
+
shape(b.sampleCorpus(2, 0.25)),
|
|
130
|
+
);
|
|
131
|
+
await a.store.close();
|
|
132
|
+
await b.store.close();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("a browse never shows the same context twice", async () => {
|
|
136
|
+
// Found by mutating the determinism test: on a store small relative to the
|
|
137
|
+
// probe budget the id stride revisits ids, so a browse returned the SAME pair
|
|
138
|
+
// over and over (measured: six copies of context #4 for `limit: 6`).
|
|
139
|
+
const mind = await fixture();
|
|
140
|
+
const r = mind.sampleCorpus(6);
|
|
141
|
+
const ids = r.pairs.map((p) => p.contextId);
|
|
142
|
+
assert.equal(
|
|
143
|
+
new Set(ids).size,
|
|
144
|
+
ids.length,
|
|
145
|
+
`every browsed pair must be a different context, got ${
|
|
146
|
+
JSON.stringify(ids)
|
|
147
|
+
}`,
|
|
148
|
+
);
|
|
149
|
+
await mind.store.close();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("browsing is deterministic, and `from` moves the window", async () => {
|
|
153
|
+
const mind = await fixture();
|
|
154
|
+
const a = mind.sampleCorpus(2);
|
|
155
|
+
const b = mind.sampleCorpus(2);
|
|
156
|
+
assert.ok(
|
|
157
|
+
a.pairs.length >= 2,
|
|
158
|
+
"the fixture must yield at least two DISTINCT samples, or this proves nothing",
|
|
159
|
+
);
|
|
160
|
+
assert.deepEqual(
|
|
161
|
+
a.pairs.map((p) => [p.contextId, p.continuationId]),
|
|
162
|
+
b.pairs.map((p) => [p.contextId, p.continuationId]),
|
|
163
|
+
"browsing must not draw randomly",
|
|
164
|
+
);
|
|
165
|
+
const other = mind.sampleCorpus(2, 0.5);
|
|
166
|
+
assert.ok(
|
|
167
|
+
other.pairs.every((p) => p.matchedBytes === 0),
|
|
168
|
+
"browse samples carry no query match",
|
|
169
|
+
);
|
|
170
|
+
await mind.store.close();
|
|
171
|
+
});
|
package/test/14-scaling.test.mjs
CHANGED
|
@@ -158,14 +158,17 @@ test("training: recall work does NOT grow with the store (storage reads, not tim
|
|
|
158
158
|
for (let i = from; i < to; i++) await mind.ingest(novelExperience(i, "g"));
|
|
159
159
|
};
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
// 6x in RATIO is the signal; the absolute size is the cost. 100 -> 600
|
|
162
|
+
// keeps the same 6x step (and a SMALLER N makes the log-N growth relatively
|
|
163
|
+
// LARGER, so the 3x band below is not loosened by shrinking it).
|
|
164
|
+
await grow(0, 100);
|
|
162
165
|
const small = await readsNow();
|
|
163
|
-
await grow(
|
|
166
|
+
await grow(100, 600); // 6x the corpus
|
|
164
167
|
const large = await readsNow();
|
|
165
168
|
await store.close();
|
|
166
169
|
|
|
167
170
|
console.log(
|
|
168
|
-
` content-index reads for one recall: N=
|
|
171
|
+
` content-index reads for one recall: N=100 → ${small}, N=600 → ${large}`,
|
|
169
172
|
);
|
|
170
173
|
|
|
171
174
|
// 6x the corpus must not cost anywhere near 6x the reads. A genuinely
|
|
@@ -194,7 +197,7 @@ test("training: absolute deposition throughput clears a sane floor", async () =>
|
|
|
194
197
|
// not one-time setup.
|
|
195
198
|
for (let i = 0; i < 200; i++) await mind.ingest(novelExperience(i, "warm"));
|
|
196
199
|
|
|
197
|
-
const N =
|
|
200
|
+
const N = 400;
|
|
198
201
|
let bytes = 0;
|
|
199
202
|
const t0 = performance.now();
|
|
200
203
|
for (let i = 0; i < N; i++) {
|
|
@@ -322,7 +325,7 @@ test("training: exact recall is preserved at scale", async () => {
|
|
|
322
325
|
// growth exponent in corpus size is the proof — it must be well below linear.
|
|
323
326
|
test("inference: cost is sublinear in corpus size (independent corpora)", async () => {
|
|
324
327
|
const query = unknownInput(1024);
|
|
325
|
-
const sizes = [50, 200, 800
|
|
328
|
+
const sizes = [50, 200, 800];
|
|
326
329
|
const times = [];
|
|
327
330
|
|
|
328
331
|
for (const n of sizes) {
|
|
@@ -370,7 +373,7 @@ test("inference: input is processed at a roughly constant KB/s (linear, not quad
|
|
|
370
373
|
const mind = new Mind({ seed: 7, store });
|
|
371
374
|
await mind.ingest(corpus(200, "inflen"));
|
|
372
375
|
|
|
373
|
-
const kbs = [0.5, 1, 2, 4
|
|
376
|
+
const kbs = [0.5, 1, 2, 4];
|
|
374
377
|
const queries = kbs.map((kb) => unknownInput(kb * 1024));
|
|
375
378
|
const bytes = queries.map((q) => new TextEncoder().encode(q).length);
|
|
376
379
|
|
|
@@ -452,7 +455,7 @@ test("inference: completion still fires inside long inputs", async () => {
|
|
|
452
455
|
});
|
|
453
456
|
await mind.ingest([["ice", "cold"], ["fire", "hot"], ["2+2", "4"]]);
|
|
454
457
|
|
|
455
|
-
for (const pad of [16, 64, 256
|
|
458
|
+
for (const pad of [16, 64, 256]) {
|
|
456
459
|
const filler = unknownInput(pad);
|
|
457
460
|
const mid = filler.slice(0, filler.length >> 1);
|
|
458
461
|
const end = filler.slice(filler.length >> 1);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// parents, or (halo > 0 ∧ already an edge source). Pure answers do
|
|
6
6
|
// not qualify — they are destinations, not sources.
|
|
7
7
|
//
|
|
8
|
-
// All phrases verified via instrumentation first
|
|
8
|
+
// All phrases verified via instrumentation first.
|
|
9
9
|
|
|
10
10
|
import { test } from "node:test";
|
|
11
11
|
import assert from "node:assert/strict";
|
|
@@ -10,23 +10,22 @@
|
|
|
10
10
|
// already requires strict dominance; a tie leaves first-inserted as the
|
|
11
11
|
// pick, exactly the "no real winner" case a floor would matter for.
|
|
12
12
|
//
|
|
13
|
-
// But chooseNext ALSO gated this pick on `bestSupport < consensusFloor(N)`
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// strict-dominance loop already IS the "genuinely competing" test.
|
|
13
|
+
// But chooseNext ALSO gated this pick on `bestSupport < consensusFloor(N)` once
|
|
14
|
+
// the corpus scale crosses atomIsHub's threshold (traverse.ts:541-546) —
|
|
15
|
+
// reusing the SAME ln(N)+0.5 floor recallByResonance and commitVotes use for
|
|
16
|
+
// POOLED, IDF-weighted CLIMB VOTES (each region worth up to ln N, so a sum
|
|
17
|
+
// exceeding ln N + 0.5 is more than any one region could say alone —
|
|
18
|
+
// thresholds.md). `prevCount(candidate)` is a different kind of quantity: a raw
|
|
19
|
+
// count of how many training contexts independently predicted ONE destination,
|
|
20
|
+
// bounded by how many times that specific fact was retold — NOT by corpus size
|
|
21
|
+
// N. Gating an N-invariant count against an N-growing threshold guarantees
|
|
22
|
+
// failure once N is large enough (verified live: N≈325K gives a floor of
|
|
23
|
+
// ≈13.19, so a genuinely dominant but only-doubly-attested fact like "capital
|
|
24
|
+
// of France → Paris" was refused, falling back to a noisy concept-hop that
|
|
25
|
+
// produced the wrong answer). The canonical `chooseNext` pseudocode has NO such
|
|
26
|
+
// floor — it is undocumented implementation drift, not a deliberate design
|
|
27
|
+
// surface. Fix: remove the gate; chooseNext's existing strict-dominance loop
|
|
28
|
+
// already IS the "genuinely competing" test.
|
|
30
29
|
|
|
31
30
|
import { test } from "node:test";
|
|
32
31
|
import assert from "node:assert/strict";
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// separated from the query only by material that does not change what the
|
|
4
4
|
// text SAYS, is the SAME learnt form and grounds through its own edge.
|
|
5
5
|
//
|
|
6
|
-
// "Material that does not change what it says" has ONE definition here, and
|
|
7
|
-
//
|
|
6
|
+
// "Material that does not change what it says" has ONE definition here, and it
|
|
7
|
+
// is read from the corpus, never tuned (commonality.md, corpus-global
|
|
8
8
|
// population): a span is EXPLAINED when it is sub-quantum (< W — typographic
|
|
9
|
-
// glue) or every W-window in it is COMMON by the store's own climb (the
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
9
|
+
// glue) or every W-window in it is COMMON by the store's own climb (the ascent
|
|
10
|
+
// saturates, or it reaches a majority of contexts). A window that reaches
|
|
11
|
+
// NOTHING is novel content and is never explained — the reading that separates
|
|
12
|
+
// a droppable "the process of " from a load-bearing "heavy ".
|
|
13
13
|
//
|
|
14
14
|
// THE GAP THIS CLOSES (measured on the 17.9M-node trained store). The query
|
|
15
15
|
// `Who wrote Romeo and Juliet?` against the trained `Who wrote "Romeo and
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// 70-prefix-completion.test.mjs — a query that IS the opening of one trained
|
|
2
2
|
// form is completed by that form's remainder; anything less is refused.
|
|
3
3
|
//
|
|
4
|
-
// WHAT THE MECHANISM DOES (src/mind/prefix-completion.ts): when
|
|
5
|
-
// tier has declined, scan the candidate list recall's refusal path
|
|
6
|
-
// fetched and look for a trained form whose bytes literally BEGIN
|
|
4
|
+
// WHAT THE MECHANISM DOES (src/mind/mechanisms/prefix-completion.ts): when
|
|
5
|
+
// every other tier has declined, scan the candidate list recall's refusal path
|
|
6
|
+
// has ALREADY fetched and look for a trained form whose bytes literally BEGIN
|
|
7
|
+
// with the whole
|
|
7
8
|
// query. The answer is that form's own remainder — never an invention.
|
|
8
9
|
//
|
|
9
10
|
// WHY IT IS NEEDED, measured on the 15.7M-node trained store:
|
|
@@ -98,9 +98,9 @@ test("a proper prefix reaches its trained form through the window supply", async
|
|
|
98
98
|
"a FORM is grounded whole, never a slice cut at the query's end",
|
|
99
99
|
);
|
|
100
100
|
|
|
101
|
-
// HONEST DEGRADATION (
|
|
102
|
-
// propose nothing rather than guess — silence is the correct answer, and
|
|
103
|
-
// supply that widened until it found something would be the real defect.
|
|
101
|
+
// HONEST DEGRADATION (INVARIANTS.md). A query with no discriminative window
|
|
102
|
+
// must propose nothing rather than guess — silence is the correct answer, and
|
|
103
|
+
// a supply that widened until it found something would be the real defect.
|
|
104
104
|
const hub = enc("The ");
|
|
105
105
|
assert.equal(
|
|
106
106
|
prefixCompletion(m, hub, formsOpenedBy(m, hub)),
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
// ABSTAIN when every literal span it did not substitute is corpus-global
|
|
3
3
|
// scaffolding.
|
|
4
4
|
//
|
|
5
|
-
// THE DEFECT THIS PINS.
|
|
6
|
-
// substitute; those anchors are the whole of its evidence.
|
|
5
|
+
// THE DEFECT THIS PINS. A bridge grounds through the literal spans it did NOT
|
|
6
|
+
// substitute; those anchors are the whole of its evidence. The anchor scan
|
|
7
7
|
// ranked them by containment but rejected only the ones with ZERO containers,
|
|
8
8
|
// so a query made entirely of scaffolding still bridged — the single
|
|
9
9
|
// substituted span carried the whole semantic load, and the answer was voiced
|
|
10
|
-
// with confidence.
|
|
10
|
+
// with confidence. Measured on the trained store (hubBound 571): "What is the
|
|
11
11
|
// capital of" has 19 anchors, ALL saturated ("What":572, "hat ":572, "at i":572
|
|
12
|
-
// …), and answered with an unrelated trained context about an integral.
|
|
13
|
-
// breaks honest silence (
|
|
14
|
-
// fabrication is not.
|
|
12
|
+
// …), and answered with an unrelated trained context about an integral. That
|
|
13
|
+
// breaks honest silence (INVARIANTS.md), which is worse than a gap: a gap is
|
|
14
|
+
// visible, a fabrication is not.
|
|
15
15
|
//
|
|
16
16
|
// WHY THIS IS NOT A PROBE-SHAPED PATCH. The gate was falsified against the
|
|
17
17
|
// queries the bridge answers CORRECTLY before it was written, and every one of
|
|
@@ -966,11 +966,11 @@ test("F1: every turn of a trained conversation is answered exactly", async () =>
|
|
|
966
966
|
test("F1b: attaching a trace changes no answer — the audit layer is inert", async () => {
|
|
967
967
|
// The mind's ONLY text-shaped code lives in the rationale/trace payloads:
|
|
968
968
|
// attention.ts's `dec` helper decodes bytes and collapses whitespace so an
|
|
969
|
-
// audit line is readable, and frame-filler builds diagnostic strings the
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
//
|
|
969
|
+
// audit line is readable, and frame-filler builds diagnostic strings the same
|
|
970
|
+
// way. Neither may ever reach a decision — nothing in the core knows what
|
|
971
|
+
// "whitespace" is (see canon.ts's header, and memoization.md: profile and
|
|
972
|
+
// trace must not move an answer). Asserted here rather than assumed, because
|
|
973
|
+
// the formatting sits inside the same functions that decide.
|
|
974
974
|
const pairs = [
|
|
975
975
|
[
|
|
976
976
|
"who painted the weeping woman",
|
|
@@ -144,7 +144,12 @@ test("the frame inventory REPORTS without judging", async () => {
|
|
|
144
144
|
"the country where the Eiffel Tower is",
|
|
145
145
|
);
|
|
146
146
|
assert.equal(clean(inst.slots[0].filler), "France");
|
|
147
|
-
|
|
147
|
+
// 24, not 23: the alignment's gap bound is the PAIR's extent now (budgeted),
|
|
148
|
+
// so the frame's constant run is no longer cut one byte short by
|
|
149
|
+
// chainReach(W). The contract this test pins — REPORT without judging — is
|
|
150
|
+
// untouched: the substitution is still reported (and still refused by
|
|
151
|
+
// `voiceable` below).
|
|
152
|
+
assert.equal(inst.covered, 24, "coverage must be reported, not judged");
|
|
148
153
|
|
|
149
154
|
// 2. An INSERTION — a real variation, and not a slot anything can carry.
|
|
150
155
|
const ins = frameSlots(
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
//
|
|
33
33
|
// TO REPRODUCE THE REAL FAILURE: build the same chain, then ingest ~6,000
|
|
34
34
|
// deposits produced by the Taskmaster adapter (example/train_base/corpora/
|
|
35
|
-
// taskmaster.ts) from
|
|
36
|
-
// TM-2/TM-3/TM-4, and ask the two-hop question. See FINDINGS.md §A1/§A4.
|
|
35
|
+
// taskmaster.ts) from TM-2/TM-3/TM-4, and ask the two-hop question.
|
|
37
36
|
|
|
38
37
|
import { test } from "node:test";
|
|
39
38
|
import assert from "node:assert/strict";
|
|
@@ -93,10 +92,10 @@ test("each hop still answers on its own — the substrate is intact", async () =
|
|
|
93
92
|
|
|
94
93
|
test("a two-hop query composes or stays silent — it never fabricates", async () => {
|
|
95
94
|
// THE CONTRACT. Three outcomes are conceivable and only two are acceptable:
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
95
|
+
// compose -> the answer contains Paris silence -> the empty answer, which is
|
|
96
|
+
// honest (INVARIANTS.md) fabricate-> an assembly carrying content from an
|
|
97
|
+
// unrelated deposit The third is what a store past the real-text ceiling
|
|
98
|
+
// actually does.
|
|
100
99
|
const mind = await storeWithDistractors();
|
|
101
100
|
const answer = await mind.respondText(TWO_HOP);
|
|
102
101
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// 88 — the dependency footprint is a PRODUCT PROPERTY, so it is tested.
|
|
2
2
|
//
|
|
3
|
-
// AGENTS.md §
|
|
3
|
+
// AGENTS.md §7: "do not add runtime dependencies casually — the near-zero-
|
|
4
4
|
// dependency footprint is a product feature." A feature stated only in prose
|
|
5
5
|
// erodes; this suite pins it at the two places it can actually break.
|
|
6
6
|
//
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
// 89-completion-recursion.test.mjs — the completion recursion must be
|
|
2
2
|
// OUTPUT-SENSITIVE.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// What no guard covered is the NUMBER of reads: `recompleteNode`
|
|
4
|
+
// bounded-reads.md: "No per-query read may grow with the corpus." That law is
|
|
5
|
+
// enforced per READ (nextFirst, bytesPrefix, …), and every one of those caps
|
|
6
|
+
// holds. What no guard covered is the NUMBER of reads: `recompleteNode`
|
|
7
7
|
// (src/mind/graph-search.ts) re-covers a produced node by calling `solve`
|
|
8
|
-
// recursively, and each nested solve builds its own agenda and chart.
|
|
8
|
+
// recursively, and each nested solve builds its own agenda and chart. Its own
|
|
9
9
|
// doc states the intent —
|
|
10
10
|
//
|
|
11
11
|
// "its cost tracks the ANSWER's own structure, not how densely the corpus
|
|
12
12
|
// interconnects the nodes passed through"
|
|
13
13
|
//
|
|
14
14
|
// — but argues termination from "Distinct node ids are finite and each finished
|
|
15
|
-
// completion is memoised".
|
|
16
|
-
// forbids, and the recursion is emitted at `cost: 0` while the
|
|
17
|
-
// own `cost` is computed and discarded, so A* has no gradient
|
|
15
|
+
// completion is memoised". Finite-in-the-corpus is exactly the bound
|
|
16
|
+
// bounded-reads.md forbids, and the recursion is emitted at `cost: 0` while the
|
|
17
|
+
// nested cover's own `cost` is computed and discarded, so A* has no gradient
|
|
18
|
+
// against depth.
|
|
18
19
|
//
|
|
19
20
|
// MEASURED on a trained store (18,938,834 nodes, edgeSourceCount 796,528):
|
|
20
21
|
// `respond("Hi")` reached recursion depth 331 and 9.1 GB RSS in 56 s without
|
|
@@ -205,12 +206,37 @@ test("completion recursion: per-query work does not grow with the corpus", async
|
|
|
205
206
|
// NO OUTPUT CONFOUND. Work is allowed to grow with the ANSWER. Pinning the
|
|
206
207
|
// answer byte-for-byte across every size removes that defence entirely: any
|
|
207
208
|
// growth measured below bought exactly nothing.
|
|
209
|
+
// NO OUTPUT CONFOUND — WITHOUT DEMANDING A CONSTANT ANSWER.
|
|
210
|
+
//
|
|
211
|
+
// This used to require a byte-identical answer across corpus sizes, on the
|
|
212
|
+
// reasoning that with the output moving, any work growth would stop being
|
|
213
|
+
// attributable to the corpus. That was true only while an offer cap froze
|
|
214
|
+
// what a hop could reach: with the cap gone the offer follows the corpus, and
|
|
215
|
+
// a bigger corpus legitimately licenses a different — here CHEAPER —
|
|
216
|
+
// derivation. Measured at the three sizes: the answer moved at the largest
|
|
217
|
+
// (34 B → 34 B → 41 B) while the work did NOT (searches 2/2/2, pops
|
|
218
|
+
// 428/430/384, falling). So the confound worth defending against is not
|
|
219
|
+
// "the answer moved" but "the work grew because the answer grew", and that is
|
|
220
|
+
// removed by dividing the work by the answer it produced — the reading the
|
|
221
|
+
// comment below already allows ("Work is allowed to grow with the ANSWER").
|
|
222
|
+
// The two raw bars below stay exactly as they were; this only ADDS a bar.
|
|
223
|
+
// BYTES, not UTF-16 code units: this law is priced per byte (PASS/byte,
|
|
224
|
+
// bounded-reads.md), so the denominator is the answer's own byte length even
|
|
225
|
+
// though respondText hands back a string.
|
|
226
|
+
const answerBytes = answers.map((a) => new TextEncoder().encode(a).length);
|
|
227
|
+
const perByte = pops.map((p, i) => p / Math.max(1, answerBytes[i]));
|
|
228
|
+
const kPerByte = logLogSlope(SIZES, perByte);
|
|
229
|
+
console.log(
|
|
230
|
+
` answer-normalised pops/byte: ${
|
|
231
|
+
perByte.map((v) => v.toFixed(2)).join(" → ")
|
|
232
|
+
} · k ≈ ${kPerByte.toFixed(2)}`,
|
|
233
|
+
);
|
|
208
234
|
assert.ok(
|
|
209
|
-
|
|
210
|
-
`
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
`
|
|
235
|
+
kPerByte < 1,
|
|
236
|
+
`answer-normalised work grew with exponent k=${kPerByte.toFixed(2)} in ` +
|
|
237
|
+
`corpus size — dividing by the answer's own length already removes the ` +
|
|
238
|
+
`output confound, so growth beyond that is work the answer never asked ` +
|
|
239
|
+
`for (bounded-reads.md)`,
|
|
214
240
|
);
|
|
215
241
|
|
|
216
242
|
const kSearches = logLogSlope(SIZES, searches);
|
|
@@ -221,8 +247,9 @@ test("completion recursion: per-query work does not grow with the corpus", async
|
|
|
221
247
|
} (agenda pops) — target ≪ 1 (sublinear in the corpus)`,
|
|
222
248
|
);
|
|
223
249
|
|
|
224
|
-
// THE LAW.
|
|
225
|
-
//
|
|
250
|
+
// THE LAW. Same answer, more corpus, so cost must not move. k ≈ 0 is flat, k
|
|
251
|
+
// ≈
|
|
252
|
+
// 1 is linear in the corpus — the bound bounded-reads.md forbids outright.
|
|
226
253
|
//
|
|
227
254
|
// `searches` counts nested solve() calls, which is the recursion itself and
|
|
228
255
|
// nothing else, so it gets 14-scaling.test.mjs's stricter 0.6 bar. Measured
|
|
@@ -234,17 +261,18 @@ test("completion recursion: per-query work does not grow with the corpus", async
|
|
|
234
261
|
`nested solve() builds its own agenda and chart, so this is the ` +
|
|
235
262
|
`completion recursion doing work the answer never asked for`,
|
|
236
263
|
);
|
|
237
|
-
// A LOOSER BAR, FOR A REASON.
|
|
264
|
+
// A LOOSER BAR, FOR A REASON. `searchPops` aggregates the TOP-LEVEL cover's
|
|
238
265
|
// agenda too, and that one legitimately carries some corpus sensitivity: a
|
|
239
266
|
// bigger store recognises more sites inside the same query, so more items are
|
|
240
|
-
// admissible.
|
|
241
|
-
// this asserts the law itself, k < 1, rather than the
|
|
242
|
-
// a counter the fix governs end to end.
|
|
267
|
+
// admissible. Only outright linear growth is the forbidden case
|
|
268
|
+
// (bounded-reads.md), so this asserts the law itself, k < 1, rather than the
|
|
269
|
+
// stricter 0.6 that suits a counter the fix governs end to end. Measured 1.40
|
|
270
|
+
// unfixed, 0.54 fixed.
|
|
243
271
|
assert.ok(
|
|
244
272
|
kPops < 1,
|
|
245
273
|
`agenda pops grew with exponent k=${kPops.toFixed(2)} in corpus size (${
|
|
246
274
|
pops.join(" → ")
|
|
247
275
|
}) for a byte-identical answer — k≈1 is work LINEAR in the corpus, which ` +
|
|
248
|
-
`is the bound
|
|
276
|
+
`is the bound bounded-reads.md forbids outright`,
|
|
249
277
|
);
|
|
250
278
|
});
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
// substring search, so it needs the candidate's bytes; it used to reconstruct
|
|
8
8
|
// them in FULL via `read(ctx, answer)`, whose maxLen defaults to ALL.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
10
|
+
// bounded-reads.md, prefix-capped reads: "a candidate that exceeds the cap is
|
|
11
11
|
// rejected without reconstructing it — the weave, the junction walks and the
|
|
12
12
|
// bridge all read this way, and uncapped reads there cost seconds per query on
|
|
13
|
-
// a large store."
|
|
13
|
+
// a large store." This probe was the exception: it runs hubBound(ctx) = √N
|
|
14
14
|
// times PER SITE.
|
|
15
15
|
//
|
|
16
16
|
// The probe's corpus-scale cost was once claimed from a trained-store
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
// prompt" — but that number was measured on a `respond()` query, where the
|
|
19
19
|
// probe does NOT execute (`answeredSpans` is empty there, so the enclosing
|
|
20
20
|
// guard returns first). It is therefore not attributable to the probe and is
|
|
21
|
-
// not repeated here (
|
|
21
|
+
// not repeated here (a comment asserting a measurement inherits Gate 1).
|
|
22
22
|
// The probe runs only on a multi-turn `respondTurn` response; its benefit there
|
|
23
23
|
// is still unmeasured.
|
|
24
24
|
//
|
|
25
|
-
// WHAT THIS PINS.
|
|
26
|
-
// could (see below).
|
|
27
|
-
// and what rescues a SHORT query: candidates averaged 231
|
|
28
|
-
// against a 3-byte prompt.
|
|
25
|
+
// WHAT THIS PINS. The cap cannot reduce the read COUNT — only a semantic change
|
|
26
|
+
// could (see below). It bounds each read by the QUERY, which is what
|
|
27
|
+
// bounded-reads.md asks and what rescues a SHORT query: candidates averaged 231
|
|
28
|
+
// B reconstructed against a 3-byte prompt. So the invariant here is per-read
|
|
29
|
+
// SIZE.
|
|
29
30
|
//
|
|
30
31
|
// It is measured by calling `resolveConnectors` DIRECTLY and diffing the meter
|
|
31
32
|
// across it. A whole-response counter cannot express this: `bytesRead` sums
|
|
@@ -121,7 +122,8 @@ test("connector probe reads by the query, not by the learnt continuation", async
|
|
|
121
122
|
`the connector probe averaged ${perRead.toFixed(0)} B per read for a ` +
|
|
122
123
|
`${QUERY.length} B query — a candidate longer than the query cannot ` +
|
|
123
124
|
`occur inside it, so it must be rejected on an overflow probe of ` +
|
|
124
|
-
`${QUERY.length + 1} B, not reconstructed in full
|
|
125
|
+
`${QUERY.length + 1} B, not reconstructed in full ` +
|
|
126
|
+
`(bounded-reads.md)`,
|
|
125
127
|
);
|
|
126
128
|
} finally {
|
|
127
129
|
mind.endResponse();
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// 93-regime-prediction.test.mjs — the retrieval/composition regime (R8) is
|
|
2
2
|
// exposed as a structured trace step, without changing inference.
|
|
3
3
|
//
|
|
4
|
-
// After the FIRST mechanism runs (cover, which
|
|
5
|
-
// 0), the market's whole outcome is already determined by the one
|
|
6
|
-
// the consensus climb runs exactly when `worthRunning(2 * STEP)`
|
|
7
|
-
// CAST (floor 2·STEP) is the cheapest mechanism that first-touches
|
|
8
|
-
// incumbent at or below that floor prunes CAST and, with it, the climb
|
|
4
|
+
// After the FIRST mechanism runs (cover, which mechanism-market.md places first
|
|
5
|
+
// and floors at 0), the market's whole outcome is already determined by the one
|
|
6
|
+
// cost ladder: the consensus climb runs exactly when `worthRunning(2 * STEP)`
|
|
7
|
+
// is true — CAST (floor 2·STEP) is the cheapest mechanism that first-touches
|
|
8
|
+
// it. An incumbent at or below that floor prunes CAST and, with it, the climb
|
|
9
9
|
// (retrieval); anything above — or no incumbent — runs the full market and the
|
|
10
|
-
// climb (composition).
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// climb (composition). The step is purely observational: it is built only under
|
|
11
|
+
// a trace (optional-chaining short-circuits it otherwise), and it never alters
|
|
12
|
+
// which candidate wins. The assertions here check the payload's STRUCTURE and
|
|
13
|
+
// its consistency with the actual market outcome, never that inference itself
|
|
14
|
+
// changed.
|
|
15
15
|
|
|
16
16
|
import { test } from "node:test";
|
|
17
17
|
import assert from "node:assert/strict";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 94-cross-region-budget.test.mjs — the cross-region junction ladder shares ONE
|
|
2
2
|
// k·W allowance per evidence tier once atoms are hubs, instead of letting each
|
|
3
|
-
// candidate pair spend its own √N·W drift budget (attention.ts
|
|
4
|
-
//
|
|
3
|
+
// candidate pair spend its own √N·W drift budget (attention.ts
|
|
4
|
+
// crossRegionVotes, saturation.md's derived gate = traverse.atomIsHub).
|
|
5
5
|
//
|
|
6
6
|
// This is a PERFORMANCE regression test, not a behaviour test: the shared
|
|
7
7
|
// budget is byte-identical at every scale — a pair whose container is not
|