@hviana/sema 0.1.0
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 +470 -0
- package/AUTHORS.md +12 -0
- package/COMMERCIAL-LICENSE.md +19 -0
- package/CONTRIBUTING.md +15 -0
- package/HOW_IT_WORKS.md +4210 -0
- package/LICENSE.md +117 -0
- package/README.md +290 -0
- package/TRADEMARKS.md +19 -0
- package/example/demo.ts +46 -0
- package/example/train_base.ts +2675 -0
- package/index.html +893 -0
- package/package.json +25 -0
- package/src/alphabet.ts +34 -0
- package/src/alu/README.md +332 -0
- package/src/alu/src/alu.ts +541 -0
- package/src/alu/src/expr.ts +339 -0
- package/src/alu/src/index.ts +115 -0
- package/src/alu/src/kernel-arith.ts +377 -0
- package/src/alu/src/kernel-bits.ts +199 -0
- package/src/alu/src/kernel-logic.ts +102 -0
- package/src/alu/src/kernel-nd.ts +235 -0
- package/src/alu/src/kernel-numeric.ts +466 -0
- package/src/alu/src/operation.ts +344 -0
- package/src/alu/src/parser.ts +574 -0
- package/src/alu/src/resonance.ts +161 -0
- package/src/alu/src/text.ts +83 -0
- package/src/alu/src/value.ts +327 -0
- package/src/alu/test/alu.test.ts +1004 -0
- package/src/bytes.ts +62 -0
- package/src/config.ts +227 -0
- package/src/derive/README.md +295 -0
- package/src/derive/src/deduction.ts +263 -0
- package/src/derive/src/index.ts +24 -0
- package/src/derive/src/priority-queue.ts +70 -0
- package/src/derive/src/rewrite.ts +127 -0
- package/src/derive/src/trie.ts +242 -0
- package/src/derive/test/derive.test.ts +137 -0
- package/src/extension.ts +42 -0
- package/src/geometry.ts +511 -0
- package/src/index.ts +38 -0
- package/src/ingest-cache.ts +224 -0
- package/src/mind/articulation.ts +137 -0
- package/src/mind/attention.ts +1061 -0
- package/src/mind/canonical.ts +107 -0
- package/src/mind/graph-search.ts +1100 -0
- package/src/mind/index.ts +18 -0
- package/src/mind/junction.ts +347 -0
- package/src/mind/learning.ts +246 -0
- package/src/mind/match.ts +507 -0
- package/src/mind/mechanisms/alu.ts +33 -0
- package/src/mind/mechanisms/cast.ts +568 -0
- package/src/mind/mechanisms/confluence.ts +268 -0
- package/src/mind/mechanisms/cover.ts +248 -0
- package/src/mind/mechanisms/extraction.ts +422 -0
- package/src/mind/mechanisms/recall.ts +241 -0
- package/src/mind/mind.ts +483 -0
- package/src/mind/pipeline-mechanism.ts +326 -0
- package/src/mind/pipeline.ts +300 -0
- package/src/mind/primitives.ts +201 -0
- package/src/mind/rationale.ts +275 -0
- package/src/mind/reasoning.ts +198 -0
- package/src/mind/recognition.ts +234 -0
- package/src/mind/resonance.ts +0 -0
- package/src/mind/trace.ts +108 -0
- package/src/mind/traverse.ts +556 -0
- package/src/mind/types.ts +281 -0
- package/src/rabitq-hnsw/README.md +303 -0
- package/src/rabitq-hnsw/src/database.ts +492 -0
- package/src/rabitq-hnsw/src/heap.ts +90 -0
- package/src/rabitq-hnsw/src/hnsw.ts +514 -0
- package/src/rabitq-hnsw/src/index.ts +15 -0
- package/src/rabitq-hnsw/src/prng.ts +39 -0
- package/src/rabitq-hnsw/src/rabitq.ts +308 -0
- package/src/rabitq-hnsw/src/store.ts +994 -0
- package/src/rabitq-hnsw/test/hnsw.test.ts +1213 -0
- package/src/store-sqlite.ts +928 -0
- package/src/store.ts +2148 -0
- package/src/vec.ts +124 -0
- package/test/00-extract.test.mjs +151 -0
- package/test/01-floor.test.mjs +20 -0
- package/test/02-roundtrip.test.mjs +83 -0
- package/test/03-recall.test.mjs +98 -0
- package/test/04-think.test.mjs +627 -0
- package/test/05-concepts.test.mjs +84 -0
- package/test/08-storage.test.mjs +948 -0
- package/test/09-edges.test.mjs +65 -0
- package/test/11-universality.test.mjs +180 -0
- package/test/13-conversation.test.mjs +228 -0
- package/test/14-scaling.test.mjs +503 -0
- package/test/15-decomposition-gap.test.mjs +0 -0
- package/test/16-bridge.test.mjs +250 -0
- package/test/17-intelligence.test.mjs +240 -0
- package/test/18-alu.test.mjs +209 -0
- package/test/19-nd.test.mjs +254 -0
- package/test/20-stability.test.mjs +489 -0
- package/test/21-partial.test.mjs +158 -0
- package/test/22-multihop.test.mjs +130 -0
- package/test/23-rationale.test.mjs +316 -0
- package/test/24-generalization.test.mjs +416 -0
- package/test/25-embedding.test.mjs +75 -0
- package/test/26-cooperative.test.mjs +89 -0
- package/test/27-saturation-drop.test.mjs +637 -0
- package/test/28-unknowable.test.mjs +88 -0
- package/test/29-counterfactual.test.mjs +476 -0
- package/test/30-conflict-resolution.test.mjs +166 -0
- package/test/31-audit.test.mjs +288 -0
- package/test/32-confluence.test.mjs +173 -0
- package/test/33-multi-candidate.test.mjs +222 -0
- package/test/34-cross-region.test.mjs +252 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
// 27-saturation-drop.test.mjs — SATURATION NOISE DROP + FLAT-BRANCH
|
|
2
|
+
// CANONICALIZATION: IRREVERSIBLE SAFEGUARDS.
|
|
3
|
+
//
|
|
4
|
+
// The six pre-existing failures (11-universality, 13-conversation, 17-intelligence,
|
|
5
|
+
// 21-partial, 24-generalization) share one root cause: position-dependent perception
|
|
6
|
+
// — the same bytes at different byte offsets produce different perceived trees →
|
|
7
|
+
// different node ids → the DAG treats them as different entities. Single-byte
|
|
8
|
+
// leaves are immune (findLeaf always returns the same id), but multi-byte sequences
|
|
9
|
+
// lose this property because bytesToTree groups bytes into leaf-parent chunks based
|
|
10
|
+
// on surrounding context.
|
|
11
|
+
//
|
|
12
|
+
// The solution has two CONCEPTUALLY DISTINCT layers, both derived purely from DAG
|
|
13
|
+
// geometry with no weights, no learning, and no byte scanning:
|
|
14
|
+
//
|
|
15
|
+
// LAYER 1 — IDENTITY INFRASTRUCTURE (position-independent content identity):
|
|
16
|
+
// A. FLAT-BRANCH STORAGE — during deposit, store flat branches (keyed by
|
|
17
|
+
// single-byte leaf ids) for sub-spans of lengths W-1 and W. Single-byte
|
|
18
|
+
// leaf ids are position-independent, so a flat branch is the SAME node
|
|
19
|
+
// regardless of where those bytes appear.
|
|
20
|
+
// B. FLAT-BRANCH CANONICALIZATION — during the vote loop, each query region
|
|
21
|
+
// resolves through canonicalChunkId to a position-independent flat branch
|
|
22
|
+
// (content-addressed hash-consing via findBranch). The flat branch's
|
|
23
|
+
// edgeAncestors give the position-independent parent count, letting IDF
|
|
24
|
+
// measure corpus distinctiveness honestly.
|
|
25
|
+
// C. flatLeafIds HASH-CONSING — the store's intern() flatLeafIds check
|
|
26
|
+
// causes structural leaf-parent nodes to reuse pre-stored flat branch ids.
|
|
27
|
+
// A feeds this by storing flat branches BEFORE internTreeIds, so common
|
|
28
|
+
// words in multiple contexts share one node id → correct parent count.
|
|
29
|
+
//
|
|
30
|
+
// LAYER 2 — SATURATION NOISE DROP (reads a geometric fact layer 1
|
|
31
|
+
// already computed, never assumes a probability distribution over position):
|
|
32
|
+
// During the vote loop (layer 1), each region records `canonicalFailed` —
|
|
33
|
+
// whether `canonicalChunkId` could NOT resolve those bytes to a
|
|
34
|
+
// position-independent flat branch. This is the honest signal: if
|
|
35
|
+
// `findBranch` (content-addressed hash-consing) failed, the region's
|
|
36
|
+
// identity is position-dependent — the same content at a different offset
|
|
37
|
+
// would get a different node id with an artificially low parent count,
|
|
38
|
+
// making the DAG's IDF read unreliable. If `canonicalChunkId` SUCCEEDED,
|
|
39
|
+
// the IDF is already honest (corpus-wide parent count) and no adjustment
|
|
40
|
+
// is needed: saturated regions were already skipped by `reach.saturated`,
|
|
41
|
+
// unsaturated ones carry the correct IDF.
|
|
42
|
+
//
|
|
43
|
+
// After structural saturated-interval detection (`detectSaturated`), votes are
|
|
44
|
+
// committed: any region whose bytes sit entirely within the detected
|
|
45
|
+
// saturated interval AND whose `canonicalFailed` is true is DROPPED — before
|
|
46
|
+
// it is ever summed into a context's total. Dropping at the REGION level
|
|
47
|
+
// (not the context level) means a position-fragile region's contribution
|
|
48
|
+
// can never hide inside a legitimate context's accumulated vote.
|
|
49
|
+
//
|
|
50
|
+
// Layer 1 is ARCHITECTURAL — content identity should not depend on byte position.
|
|
51
|
+
// Layer 2 is GEOMETRIC — it reads a fact layer 1 already computed; no assumed
|
|
52
|
+
// distribution, no tuned constants, no byte scanning.
|
|
53
|
+
//
|
|
54
|
+
// REMOVAL INDICATORS (what breaks if each mechanism is removed):
|
|
55
|
+
// A removed → findBranch returns null for KNOWN leaf-parent sub-span ids
|
|
56
|
+
// B removed → a common word at a new position doesn't saturate
|
|
57
|
+
// C removed → flat branch has 0 structural parents after single deposit
|
|
58
|
+
// (drop) removed → saturated-interval canonicalFailed-region votes are not
|
|
59
|
+
// suppressed, and can fragment the consensus or inflate a context's total
|
|
60
|
+
//
|
|
61
|
+
// These tests are the IRREVERSIBLE SAFEGUARD. A build that removes or neuters
|
|
62
|
+
// any mechanism must FAIL here — even if end-to-end answers still pass through
|
|
63
|
+
// fallback routes. The existing test suite tests answers, not geometric invariants.
|
|
64
|
+
|
|
65
|
+
import { test } from "node:test";
|
|
66
|
+
import assert from "node:assert/strict";
|
|
67
|
+
import { Mind } from "../dist/src/index.js";
|
|
68
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
69
|
+
|
|
70
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
71
|
+
const dec = (b) => new TextDecoder().decode(b);
|
|
72
|
+
|
|
73
|
+
const mk = () =>
|
|
74
|
+
new Mind({ seed: 7, store: new SQliteStore({ path: ":memory:" }) });
|
|
75
|
+
|
|
76
|
+
// Shared SYS opening — long enough for hasLeading detection (≥ maxGroup = 4 bytes).
|
|
77
|
+
const SYS =
|
|
78
|
+
"You are a helpful and harmless assistant.\n\nYou are not allowed to use any tools.\n";
|
|
79
|
+
const SYSLen = enc(SYS).length; // 81 bytes on the reference seed
|
|
80
|
+
|
|
81
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
/** Build a leaf-id chain for a byte sequence and return its flat branch id, or
|
|
84
|
+
* null if any byte is unknown or no flat branch exists. */
|
|
85
|
+
function flatBranchFor(store, bytes) {
|
|
86
|
+
const ids = [];
|
|
87
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
88
|
+
const lid = store.findLeaf(bytes.subarray(i, i + 1));
|
|
89
|
+
if (lid === null) return null;
|
|
90
|
+
ids.push(lid);
|
|
91
|
+
}
|
|
92
|
+
return store.findBranch(ids);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Walk the perceived tree and return every leaf-parent chunk (node where all
|
|
96
|
+
* kids are leaves) with its byte span and the leaf-id chain. */
|
|
97
|
+
function leafParentChunks(mind, input) {
|
|
98
|
+
const tree = mind.perceive(input);
|
|
99
|
+
const chunks = [];
|
|
100
|
+
const walk = (n, start) => {
|
|
101
|
+
if (n.kids === null) return start + (n.leaf?.length ?? 0);
|
|
102
|
+
let pos = start;
|
|
103
|
+
for (const c of n.kids) pos = walk(c, pos);
|
|
104
|
+
if (n.kids !== null && n.kids.every((k) => k.kids === null)) {
|
|
105
|
+
const bytes = [];
|
|
106
|
+
for (const k of n.kids) {
|
|
107
|
+
if (k.leaf) bytes.push(...k.leaf);
|
|
108
|
+
}
|
|
109
|
+
const leafIds = [];
|
|
110
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
111
|
+
const lid = mind.store.findLeaf(new Uint8Array([bytes[i]]));
|
|
112
|
+
if (lid !== null) leafIds.push(lid);
|
|
113
|
+
}
|
|
114
|
+
chunks.push({ start, end: pos, bytes: new Uint8Array(bytes), leafIds });
|
|
115
|
+
}
|
|
116
|
+
return pos;
|
|
117
|
+
};
|
|
118
|
+
walk(tree, 0);
|
|
119
|
+
return chunks;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
123
|
+
// A — FLAT-BRANCH STORAGE (identity infrastructure, layer 1)
|
|
124
|
+
//
|
|
125
|
+
// After deposit, EVERY leaf-parent chunk's sub-spans (lengths W-1 and W)
|
|
126
|
+
// must have flat branches findable via findBranch. These flat branches are
|
|
127
|
+
// the position-independent nodes that the canonicalization and hash-consing
|
|
128
|
+
// mechanisms consume.
|
|
129
|
+
//
|
|
130
|
+
// REMOVAL INDICATOR: after deposit, a leaf-parent chunk of length ≥ 2 has
|
|
131
|
+
// no findBranch result for its exact leaf-id chain.
|
|
132
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
133
|
+
|
|
134
|
+
test("A — every leaf-parent chunk has a flat branch for its exact bytes after deposit", async () => {
|
|
135
|
+
const m = mk();
|
|
136
|
+
await m.ingest([[
|
|
137
|
+
"The Moon landing was in 1969.",
|
|
138
|
+
"Neil Armstrong landed on the Moon.",
|
|
139
|
+
]]);
|
|
140
|
+
|
|
141
|
+
const chunks = leafParentChunks(m, "The Moon landing was in 1969.");
|
|
142
|
+
// Filter to chunks with ≥ 2 leaves (flat branches require at least 2 kids)
|
|
143
|
+
const multiLeaf = chunks.filter((c) => c.bytes.length >= 2);
|
|
144
|
+
|
|
145
|
+
assert.ok(multiLeaf.length >= 2, "must have multiple multi-leaf chunks");
|
|
146
|
+
|
|
147
|
+
for (const c of multiLeaf) {
|
|
148
|
+
const fb = m.store.findBranch(c.leafIds);
|
|
149
|
+
assert.notEqual(
|
|
150
|
+
fb,
|
|
151
|
+
null,
|
|
152
|
+
`flat branch must exist for chunk [${c.start}-${c.end}] ` +
|
|
153
|
+
`"${dec(c.bytes)}" (${c.leafIds.length} leaves). ` +
|
|
154
|
+
"storeNgrams must pre-store flat branches for every leaf-parent chunk.",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await m.store.close();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("A — sub-span flat branches exist for canonicalization-relevant lengths (W-1 and W)", async () => {
|
|
162
|
+
const m = mk();
|
|
163
|
+
await m.ingest([["abcd efgh ijkl", "mnop qrst uvwx"]]);
|
|
164
|
+
|
|
165
|
+
const W = m.space.maxGroup;
|
|
166
|
+
const chunks = leafParentChunks(m, "abcd efgh ijkl");
|
|
167
|
+
|
|
168
|
+
// For chunks with W leaves (maxGroup), the (W-1)-byte sub-spans must exist
|
|
169
|
+
// — these are the 1-byte-shorter matches the canonicalization checks.
|
|
170
|
+
// 2-byte sub-spans within W-byte chunks are NOT stored (only lengths W-1
|
|
171
|
+
// and W are needed for the canonicalization probes).
|
|
172
|
+
for (const c of chunks) {
|
|
173
|
+
if (c.leafIds.length < W) continue;
|
|
174
|
+
// (W-1)-byte sub-spans within a W-byte chunk must exist
|
|
175
|
+
for (let off = 0; off + W - 1 <= c.leafIds.length; off++) {
|
|
176
|
+
const sub = c.leafIds.slice(off, off + W - 1);
|
|
177
|
+
const fb = m.store.findBranch(sub);
|
|
178
|
+
assert.notEqual(
|
|
179
|
+
fb,
|
|
180
|
+
null,
|
|
181
|
+
`${W - 1}-byte sub-span at offset ${off} of chunk "${dec(c.bytes)}" ` +
|
|
182
|
+
"must have a flat branch — indexSubSpans stores lengths W-1 and W",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// For ALL leaf-parent chunks, the full chunk's flat branch must exist.
|
|
188
|
+
for (const c of chunks) {
|
|
189
|
+
if (c.leafIds.length < 2) continue;
|
|
190
|
+
const fb = m.store.findBranch(c.leafIds);
|
|
191
|
+
assert.notEqual(
|
|
192
|
+
fb,
|
|
193
|
+
null,
|
|
194
|
+
`full chunk "${dec(c.bytes)}" must have a flat branch`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await m.store.close();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
202
|
+
// C — flatLeafIds HASH-CONSING (ordering proof, identity infrastructure layer 1)
|
|
203
|
+
//
|
|
204
|
+
// Flat-branch storage runs BEFORE internTreeIds. This ordering is load-bearing:
|
|
205
|
+
// the store's intern() flatLeafIds check finds the pre-stored flat branch and
|
|
206
|
+
// hash-conses the structural node to it. If flat branches were stored AFTER
|
|
207
|
+
// interning, the flatLeafIds check would miss them and the flat branch would
|
|
208
|
+
// have 0 structural parents — useless for edgeAncestors.
|
|
209
|
+
//
|
|
210
|
+
// We prove the ordering is correct: after a SINGLE deposit, a leaf-parent
|
|
211
|
+
// chunk's flat branch must have ≥ 1 structural parent. This parent came from
|
|
212
|
+
// the SAME deposit's internTreeIds hash-consing to the pre-stored flat branch.
|
|
213
|
+
//
|
|
214
|
+
// REMOVAL INDICATOR: after single deposit, flat branch for a leaf-parent chunk
|
|
215
|
+
// has parents().length === 0.
|
|
216
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
217
|
+
|
|
218
|
+
test("C — flat branch has structural parent after single deposit (ordering proof)", async () => {
|
|
219
|
+
const m = mk();
|
|
220
|
+
await m.ingest([[
|
|
221
|
+
"The Moon landing was in 1969.",
|
|
222
|
+
"Neil Armstrong landed on the Moon.",
|
|
223
|
+
]]);
|
|
224
|
+
|
|
225
|
+
// Find all leaf-parent chunks and check each one's flat branch has parents
|
|
226
|
+
const chunks = leafParentChunks(m, "The Moon landing was in 1969.");
|
|
227
|
+
const multiLeaf = chunks.filter((c) => c.bytes.length >= 2);
|
|
228
|
+
|
|
229
|
+
let withParents = 0;
|
|
230
|
+
for (const c of multiLeaf) {
|
|
231
|
+
const fb = m.store.findBranch(c.leafIds);
|
|
232
|
+
if (fb === null) continue;
|
|
233
|
+
const parents = m.store.parents(fb);
|
|
234
|
+
if (parents.length >= 1) withParents++;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
assert.ok(
|
|
238
|
+
withParents >= 1,
|
|
239
|
+
`at least one leaf-parent chunk's flat branch must have ≥ 1 structural ` +
|
|
240
|
+
`parent after single deposit, got ${withParents}/${multiLeaf.length}. ` +
|
|
241
|
+
"Without flat-branch storage running BEFORE internTreeIds, the flatLeafIds check in " +
|
|
242
|
+
"intern() would miss the pre-stored flat branch and the structural " +
|
|
243
|
+
"node would mint a fresh id — leaving the flat branch with 0 parents.",
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
await m.store.close();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("C — repeated deposit of same content reuses the same flat branch ids", async () => {
|
|
250
|
+
const m = mk();
|
|
251
|
+
|
|
252
|
+
// Two deposits of IDENTICAL content. The second deposit must reuse ALL
|
|
253
|
+
// flat branch ids from the first — the content hasn't changed, so the
|
|
254
|
+
// leaf-parent chunks are identical.
|
|
255
|
+
const input = "The quick brown fox jumps over the lazy dog.";
|
|
256
|
+
await m.ingest([[input, "answer 1"]]);
|
|
257
|
+
|
|
258
|
+
const chunks = leafParentChunks(m, input).filter((c) => c.bytes.length >= 2);
|
|
259
|
+
const ids1 = [];
|
|
260
|
+
for (const c of chunks) {
|
|
261
|
+
const fb = m.store.findBranch(c.leafIds);
|
|
262
|
+
if (fb !== null) ids1.push(fb);
|
|
263
|
+
}
|
|
264
|
+
assert.ok(ids1.length >= 2, "first deposit must produce flat branch ids");
|
|
265
|
+
|
|
266
|
+
await m.ingest([[input, "answer 2"]]);
|
|
267
|
+
const ids2 = [];
|
|
268
|
+
for (const c of chunks) {
|
|
269
|
+
const fb = m.store.findBranch(c.leafIds);
|
|
270
|
+
if (fb !== null) ids2.push(fb);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Every flat branch id from the first deposit must be reused.
|
|
274
|
+
// Identical content → identical leaf ids → identical flat branches.
|
|
275
|
+
for (let i = 0; i < ids1.length; i++) {
|
|
276
|
+
assert.equal(
|
|
277
|
+
ids2[i],
|
|
278
|
+
ids1[i],
|
|
279
|
+
`flat branch id for chunk ${i} must be identical across deposits. ` +
|
|
280
|
+
"Second deposit of same content must reuse existing flat branches.",
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
await m.store.close();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
288
|
+
// B — FLAT-BRANCH CANONICALIZATION (identity infrastructure, layer 1)
|
|
289
|
+
//
|
|
290
|
+
// The vote loop canonicalizes query regions through the LONGEST matching flat
|
|
291
|
+
// branch. This gives the position-independent parent count for honest IDF —
|
|
292
|
+
// without it, the same bytes at different positions get artificially low parent
|
|
293
|
+
// counts and don't saturate, contributing noise.
|
|
294
|
+
//
|
|
295
|
+
// We test the EFFECT: a single-topic query must not fragment. If
|
|
296
|
+
// canonicalization were removed, saturated-interval noise from SYS regions (which
|
|
297
|
+
// don't saturate due to position-dependent perception) would fragment the
|
|
298
|
+
// single topic. If the match-length guard were removed, short coincidental
|
|
299
|
+
// matches within distinctive regions would cause false saturation.
|
|
300
|
+
//
|
|
301
|
+
// REMOVAL INDICATOR: a scaffolding-dominated single-topic query returns > 1 root
|
|
302
|
+
// from climbAttention.
|
|
303
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
304
|
+
|
|
305
|
+
test("B — single-topic query is not fragmented", async () => {
|
|
306
|
+
const m = mk();
|
|
307
|
+
await m.ingest([
|
|
308
|
+
[
|
|
309
|
+
SYS + "Describe the importance of gender equality in the workplace.",
|
|
310
|
+
"Gender equality means equal chances to be hired and promoted.",
|
|
311
|
+
],
|
|
312
|
+
[
|
|
313
|
+
SYS + "Provide a summary of the 1992 Dream Team basketball squad.",
|
|
314
|
+
"The Dream Team won gold in Barcelona in 1992.",
|
|
315
|
+
],
|
|
316
|
+
[
|
|
317
|
+
SYS + "Explain how photosynthesis converts sunlight into energy.",
|
|
318
|
+
"Photosynthesis binds carbon dioxide and water into sugar.",
|
|
319
|
+
],
|
|
320
|
+
[
|
|
321
|
+
SYS + "Summarize the causes of the fall of the Western Roman Empire.",
|
|
322
|
+
"The Western Roman Empire fell from overreach and migration.",
|
|
323
|
+
],
|
|
324
|
+
]);
|
|
325
|
+
|
|
326
|
+
const q = SYS + "the importance of gender equality in the workplace";
|
|
327
|
+
const forest = await m.climbAttention(enc(q), 16, "inverse");
|
|
328
|
+
|
|
329
|
+
assert.equal(
|
|
330
|
+
forest.length,
|
|
331
|
+
1,
|
|
332
|
+
`single-topic query must yield exactly 1 root, got ${forest.length}. ` +
|
|
333
|
+
"Without flat-branch canonicalization, saturated-interval noise " +
|
|
334
|
+
"would fragment the single topic into multiple roots. This is the " +
|
|
335
|
+
"irreversible safeguard for 24-generalization single-topic test.",
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
const rootBytes = dec(m.store.bytesPrefix(forest[0].anchor, 200));
|
|
339
|
+
assert.ok(
|
|
340
|
+
rootBytes.includes("Describe the importance of gender"),
|
|
341
|
+
`root must be the gender equality context, got: "${
|
|
342
|
+
rootBytes.slice(0, 80)
|
|
343
|
+
}..."`,
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
await m.store.close();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
350
|
+
// SATURATED-PREFIX NOISE DROP (layer 2 — reads a fact layer 1 already computed)
|
|
351
|
+
//
|
|
352
|
+
// During the vote loop, each chunk region records `canonicalFailed` — whether
|
|
353
|
+
// canonicalChunkId (content-addressed hash-consing via findBranch) could NOT
|
|
354
|
+
// resolve its bytes to a position-independent flat branch. After detectSaturated
|
|
355
|
+
// finds the saturated-interval boundary, the vote commit drops any region whose
|
|
356
|
+
// bytes sit entirely inside that interval AND whose canonicalFailed is true.
|
|
357
|
+
//
|
|
358
|
+
// `canonicalFailed` is the honest signal: if findBranch failed, the region's
|
|
359
|
+
// identity is position-dependent — the same content at a different offset
|
|
360
|
+
// would get a different node id with an artificially low parent count, so the
|
|
361
|
+
// DAG's IDF read is unreliable. If canonicalChunkId SUCCEEDED, the IDF is
|
|
362
|
+
// already honest (corpus-wide parent count) and needs no adjustment: saturated
|
|
363
|
+
// regions were already skipped by reach.saturated, unsaturated ones carry the
|
|
364
|
+
// correct IDF.
|
|
365
|
+
//
|
|
366
|
+
// We test the EFFECT: a query with a shared SYS opening must return roots
|
|
367
|
+
// whose focus is PAST the saturated interval (in distinctive content). If the
|
|
368
|
+
// noise drop were removed, saturated-interval foci would not be suppressed and
|
|
369
|
+
// could out-vote genuine distinctive foci.
|
|
370
|
+
//
|
|
371
|
+
// REMOVAL INDICATOR: a query with a saturated interval returns a root whose
|
|
372
|
+
// focus is deep within the SYS opening.
|
|
373
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
374
|
+
|
|
375
|
+
test("noise drop — saturated-interval foci are suppressed", async () => {
|
|
376
|
+
const m = mk();
|
|
377
|
+
await m.ingest([
|
|
378
|
+
[
|
|
379
|
+
SYS + "Describe gender equality in the workplace.",
|
|
380
|
+
"Gender equality means equal chances for all.",
|
|
381
|
+
],
|
|
382
|
+
[
|
|
383
|
+
SYS + "Summarize the causes of the fall of Rome.",
|
|
384
|
+
"Rome fell from overreach and migration.",
|
|
385
|
+
],
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
// Query with SYS + a distinctive word. The distinctive word ("gender")
|
|
389
|
+
// must have its focus PAST the SYS opening.
|
|
390
|
+
const q = SYS + "gender equality";
|
|
391
|
+
const forest = await m.climbAttention(enc(q), 16, "inverse");
|
|
392
|
+
|
|
393
|
+
assert.equal(forest.length, 1, "must return 1 root for single-topic query");
|
|
394
|
+
|
|
395
|
+
// The root's focus must be AT OR PAST leadingEnd (the saturated-interval
|
|
396
|
+
// boundary). leadingEnd is computed from per-region saturation of leading
|
|
397
|
+
// contiguous regions. The focus must not be deep within the SYS — if the
|
|
398
|
+
// noise drop were removed, a saturated-interval region's focus could out-vote
|
|
399
|
+
// the distinctive one. `canonicalFailed` regions inside the SYS opening are
|
|
400
|
+
// dropped per-region during vote commit; regions that canonicalized
|
|
401
|
+
// successfully have honest IDF and either saturated out (reach.saturated)
|
|
402
|
+
// or carry correct weight.
|
|
403
|
+
//
|
|
404
|
+
// leadingEnd ≥ maxGroup (4) for hasLeading to be true. The focus position
|
|
405
|
+
// should be past at least this minimum — if it were at byte 0-3 (deep in
|
|
406
|
+
// the SYS), the noise drop is broken.
|
|
407
|
+
const focusStart = forest[0].start;
|
|
408
|
+
assert.ok(
|
|
409
|
+
focusStart >= 4,
|
|
410
|
+
`root focus at byte ${focusStart} must be ≥ maxGroup (4). ` +
|
|
411
|
+
"If the saturated-interval noise drop were removed, interval foci would not be suppressed and " +
|
|
412
|
+
"could appear as the top root's focus even for distinctive queries.",
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
await m.store.close();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("noise drop — query without leading interval (no hasLeading) does not drop votes", async () => {
|
|
419
|
+
const m = mk();
|
|
420
|
+
// No shared SYS opening — the query starts with distinctive content.
|
|
421
|
+
// hasLeading should be false, so the noise drop should NOT suppress any votes.
|
|
422
|
+
await m.ingest([
|
|
423
|
+
[
|
|
424
|
+
"Neil Armstrong and Buzz Aldrin landed on the Moon in 1969.",
|
|
425
|
+
"The Apollo 11 mission was historic.",
|
|
426
|
+
],
|
|
427
|
+
[
|
|
428
|
+
"Photosynthesis converts sunlight into chemical energy in plants.",
|
|
429
|
+
"It binds carbon dioxide and water into sugar.",
|
|
430
|
+
],
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
// Query with distinctive content at byte 0 — no saturated interval
|
|
434
|
+
const q = "Neil Armstrong and Buzz Aldrin";
|
|
435
|
+
const forest = await m.climbAttention(enc(q), 16, "inverse");
|
|
436
|
+
|
|
437
|
+
assert.equal(forest.length, 1, "must return 1 root");
|
|
438
|
+
|
|
439
|
+
// With no saturated interval, the focus CAN be at byte 0 (the query starts
|
|
440
|
+
// with distinctive content). If the noise drop incorrectly fired without
|
|
441
|
+
// hasLeading, it would suppress this legitimate focus.
|
|
442
|
+
const focusStart = forest[0].start;
|
|
443
|
+
assert.ok(
|
|
444
|
+
focusStart < SYSLen,
|
|
445
|
+
`without leading interval, focus at byte ${focusStart} can be early — ` +
|
|
446
|
+
"the noise drop must gate on hasLeading and not suppress votes when no saturated interval is detected",
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
await m.store.close();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
453
|
+
// INTEGRATION — identity infra + saturated-interval noise drop compose to fix
|
|
454
|
+
// position-dependent perception failures
|
|
455
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
456
|
+
|
|
457
|
+
test("integration — edgeAncestors connects flat branches to edge-bearing contexts", async () => {
|
|
458
|
+
// The critical integration of flat-branch storage + hash-consing: a flat
|
|
459
|
+
// branch stored during deposit must be reachable via edgeAncestors — proof
|
|
460
|
+
// that the structural tree hash-consed to it and that the parent chain
|
|
461
|
+
// connects to edge-bearing context roots. Without both mechanisms, the
|
|
462
|
+
// flat branch has 0 structural parents and edgeAncestors returns
|
|
463
|
+
// contextsReached = 0.
|
|
464
|
+
const m = mk();
|
|
465
|
+
|
|
466
|
+
await m.ingest([[SYS + "context one.", "answer one."]]);
|
|
467
|
+
|
|
468
|
+
const chunks = leafParentChunks(m, SYS + "context one.");
|
|
469
|
+
const chunk = chunks.find((c) =>
|
|
470
|
+
c.bytes.length >= 3 && c.leafIds.length >= 3
|
|
471
|
+
);
|
|
472
|
+
assert.ok(chunk, "need a chunk with ≥ 3 leaf ids");
|
|
473
|
+
|
|
474
|
+
const fb = m.store.findBranch(chunk.leafIds);
|
|
475
|
+
assert.notEqual(fb, null, "flat branch must exist after first deposit");
|
|
476
|
+
|
|
477
|
+
// edgeAncestors must find edge-bearing contexts through the structural
|
|
478
|
+
// parent chain. The flat branch was hash-consed to by the structural
|
|
479
|
+
// leaf-parent chunk, whose ancestors include the edge-bearing context root.
|
|
480
|
+
const N = m.store.edgeSourceCount();
|
|
481
|
+
const reach = m.edgeAncestors(fb, N);
|
|
482
|
+
assert.ok(
|
|
483
|
+
reach.contextsReached >= 1 || reach.roots.length >= 1,
|
|
484
|
+
`edgeAncestors must reach contexts or roots: ` +
|
|
485
|
+
`contextsReached=${reach.contextsReached} roots=${reach.roots.length}. ` +
|
|
486
|
+
"Without flat-branch storage + hash-consing, the flat branch would have 0 structural parents " +
|
|
487
|
+
"and edgeAncestors would find nothing — the flat branch created by " +
|
|
488
|
+
"storeNgrams is orphaned (no parent in the kid table).",
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
// The flat branch must have ≥ 1 immediate parent, proving hash-consing works.
|
|
492
|
+
const parents = m.store.parents(fb);
|
|
493
|
+
assert.ok(
|
|
494
|
+
parents.length >= 1,
|
|
495
|
+
`flat branch must have ≥ 1 structural parent, got ${parents.length}. ` +
|
|
496
|
+
"Without hash-consing, storeNgrams creates the flat branch but internTreeIds " +
|
|
497
|
+
"mints a fresh id for the structural chunk.",
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
await m.store.close();
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test("integration — root count from vote distribution, not a constant (24-gen safeguard)", async () => {
|
|
504
|
+
const m = mk();
|
|
505
|
+
await m.ingest([
|
|
506
|
+
[
|
|
507
|
+
SYS + "Describe the importance of gender equality.",
|
|
508
|
+
"Gender equality matters.",
|
|
509
|
+
],
|
|
510
|
+
[SYS + "Summarize the Roman Empire.", "The Empire fell."],
|
|
511
|
+
]);
|
|
512
|
+
|
|
513
|
+
// Single-topic → exactly 1 root from the vote distribution
|
|
514
|
+
const f1 = await m.climbAttention(
|
|
515
|
+
enc(SYS + "gender equality"),
|
|
516
|
+
16,
|
|
517
|
+
"inverse",
|
|
518
|
+
);
|
|
519
|
+
assert.equal(f1.length, 1, "single-topic must yield exactly 1 root");
|
|
520
|
+
|
|
521
|
+
await m.store.close();
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test("integration — combined DF modes work correctly (24-gen combined DF safeguard)", async () => {
|
|
525
|
+
// The three DF modes (inverse, direct, combined) must all resolve the same
|
|
526
|
+
// forest structure — the weighting changes but the underlying evidence doesn't.
|
|
527
|
+
const m = mk();
|
|
528
|
+
await m.ingest([
|
|
529
|
+
[SYS + "Describe gender equality at work.", "Equal chances for all."],
|
|
530
|
+
[SYS + "Summarize the fall of Rome.", "Overreach and migration."],
|
|
531
|
+
]);
|
|
532
|
+
|
|
533
|
+
const q = SYS + "gender equality at work";
|
|
534
|
+
const inv = await m.climbAttention(enc(q), 16, "inverse");
|
|
535
|
+
const dir = await m.climbAttention(enc(q), 16, "direct");
|
|
536
|
+
const comb = await m.climbAttention(enc(q), 16, "combined");
|
|
537
|
+
|
|
538
|
+
// All three modes must return the SAME number of roots. The weighting
|
|
539
|
+
// changes the vote VALUES but not the forest STRUCTURE — a single-topic
|
|
540
|
+
// query is one root regardless of how it's weighted.
|
|
541
|
+
assert.equal(inv.length, 1, "inverse DF must yield 1 root");
|
|
542
|
+
assert.equal(
|
|
543
|
+
dir.length,
|
|
544
|
+
inv.length,
|
|
545
|
+
"direct DF must yield same root count as inverse",
|
|
546
|
+
);
|
|
547
|
+
assert.equal(
|
|
548
|
+
comb.length,
|
|
549
|
+
inv.length,
|
|
550
|
+
"combined DF must yield same root count as inverse",
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
await m.store.close();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
557
|
+
// FUNDAMENTAL GAP — POSITION-INDEPENDENT IDENTITY
|
|
558
|
+
//
|
|
559
|
+
// The gap: "They require deeper structural improvements to how short-byte
|
|
560
|
+
// sequences hash-cons across positions — not more gating."
|
|
561
|
+
//
|
|
562
|
+
// The pre-existing failures share ONE root cause: the same bytes at different
|
|
563
|
+
// byte offsets produce different perceived trees → different node ids → the
|
|
564
|
+
// DAG treats them as different entities. This is not a bug in the consensus
|
|
565
|
+
// climb, the forest assembly, or the IDF weighting — those are all geometrically
|
|
566
|
+
// correct. The bug is that the DAG's content-addressable identity is POSITION-
|
|
567
|
+
// DEPENDENT rather than CONTENT-DEPENDENT for multi-byte sequences.
|
|
568
|
+
//
|
|
569
|
+
// Layer 1 (identity infrastructure) resolves this by deriving a position-
|
|
570
|
+
// independent identity from the only position-independent primitive:
|
|
571
|
+
// single-byte leaves. Flat branches keyed by those leaf ids are the same node
|
|
572
|
+
// regardless of where the bytes appear. Layer 2 (saturated-interval noise drop)
|
|
573
|
+
// reads whether layer 1 succeeded (`canonicalFailed`) and drops
|
|
574
|
+
// position-fragile regions before they reach any context's vote total —
|
|
575
|
+
// reading a geometric fact the identity layer already computed, never
|
|
576
|
+
// assuming a probability distribution over byte position.
|
|
577
|
+
//
|
|
578
|
+
// This test is the CANONICAL PROOF: deposit the same byte sequence at two
|
|
579
|
+
// different positions in two different contexts, and verify that the flat
|
|
580
|
+
// branch identity is preserved.
|
|
581
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
582
|
+
|
|
583
|
+
test("fundamental-gap — same bytes at different positions produce the same flat branch id", async () => {
|
|
584
|
+
const m = mk();
|
|
585
|
+
|
|
586
|
+
// Deposit two contexts where "the " (4 bytes) appears at DIFFERENT byte
|
|
587
|
+
// offsets. Context 1: "the " right after SYS. Context 2: "the " deeper
|
|
588
|
+
// in the context body.
|
|
589
|
+
await m.ingest([[
|
|
590
|
+
SYS + "the first context is about space.",
|
|
591
|
+
"Space is vast.",
|
|
592
|
+
]]);
|
|
593
|
+
|
|
594
|
+
// After the first deposit, find the flat branch for a known 4-byte SYS chunk.
|
|
595
|
+
// SYS chunks are at fixed positions and guaranteed to form leaf-parent groups.
|
|
596
|
+
const chunks1 = leafParentChunks(
|
|
597
|
+
m,
|
|
598
|
+
SYS + "the first context is about space.",
|
|
599
|
+
);
|
|
600
|
+
const firstChunk = chunks1.find((c) => c.bytes.length === 4);
|
|
601
|
+
assert.ok(firstChunk, "must find at least one 4-byte leaf-parent chunk");
|
|
602
|
+
const fb1 = m.store.findBranch(firstChunk.leafIds);
|
|
603
|
+
assert.notEqual(fb1, null, "flat branch must exist for first chunk");
|
|
604
|
+
|
|
605
|
+
// Deposit a second context with the SAME SYS opening
|
|
606
|
+
await m.ingest([[
|
|
607
|
+
SYS + "the second context is about oceans.",
|
|
608
|
+
"Oceans are deep.",
|
|
609
|
+
]]);
|
|
610
|
+
|
|
611
|
+
// The SAME SYS chunks must return the SAME flat branch ids.
|
|
612
|
+
// This is the position-independence invariant: the SYS opening is at the
|
|
613
|
+
// same byte offset (0) in both contexts, so the structural nodes hash-cons.
|
|
614
|
+
// But more importantly, the flat branches (stored by flat-branch storage) are shared because
|
|
615
|
+
// the leaf ids are identical.
|
|
616
|
+
const fb2 = m.store.findBranch(firstChunk.leafIds);
|
|
617
|
+
assert.equal(
|
|
618
|
+
fb2,
|
|
619
|
+
fb1,
|
|
620
|
+
"same leaf ids → same flat branch id across deposits. This is the " +
|
|
621
|
+
"foundation of position-independent identity. Without flat-branch storage, the second " +
|
|
622
|
+
"deposit might not find the flat branch stored by the first.",
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
// The flat branch must have accumulated parents from BOTH deposits.
|
|
626
|
+
// Each deposit's structural tree contains the SYS opening chunks; each
|
|
627
|
+
// structural node hash-conses to the same flat branch via hash-consing.
|
|
628
|
+
const parents = m.store.parents(fb1);
|
|
629
|
+
assert.ok(
|
|
630
|
+
parents.length >= 1,
|
|
631
|
+
`flat branch must have ≥ 1 structural parent after two deposits, ` +
|
|
632
|
+
`got ${parents.length}. flatLeafIds hash-consing must link ` +
|
|
633
|
+
`structural nodes from both contexts to this flat branch.`,
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
await m.store.close();
|
|
637
|
+
});
|