@hviana/sema 0.7.3 → 0.7.6
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 +95 -843
- package/README.md +11 -11
- package/dist/src/mind/graph-search.d.ts +32 -22
- package/dist/src/mind/graph-search.js +97 -54
- package/dist/src/mind/mind.js +14 -0
- package/dist/src/store-sqlite.js +17 -0
- package/dist/src/store.d.ts +18 -0
- package/dist/src/store.js +10 -0
- package/docs/INDEX.md +71 -0
- package/docs/INVARIANTS.md +19 -0
- package/docs/architecture/bounded-reads.md +85 -0
- package/docs/architecture/caches.md +89 -0
- package/docs/architecture/commonality.md +45 -0
- package/docs/architecture/cost-model.md +71 -0
- package/docs/architecture/determinism.md +73 -0
- package/docs/architecture/exact-vs-approximate.md +47 -0
- package/docs/architecture/factored-machinery.md +28 -0
- package/docs/architecture/fold-contract.md +87 -0
- package/docs/architecture/halo-sketch.md +99 -0
- package/docs/architecture/match-project.md +62 -0
- package/docs/architecture/mechanism-market.md +95 -0
- package/docs/architecture/memoization.md +96 -0
- package/docs/architecture/meter.md +55 -0
- package/docs/architecture/saturation.md +92 -0
- package/docs/architecture/store.md +79 -0
- package/docs/architecture/thresholds.md +79 -0
- package/docs/failures/tempting-but-wrong.md +144 -0
- package/docs/harness/gates.md +56 -0
- package/docs/mechanisms/alu.md +75 -0
- package/docs/mechanisms/cast.md +75 -0
- package/docs/mechanisms/confluence.md +36 -0
- package/docs/mechanisms/cover.md +54 -0
- package/docs/mechanisms/extraction.md +53 -0
- package/docs/mechanisms/prefix-completion.md +54 -0
- package/docs/mechanisms/recall.md +69 -0
- package/docs/mechanisms/reference.md +58 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/mind/graph-search.ts +101 -55
- package/src/mind/mind.ts +14 -0
- package/src/store-sqlite.ts +19 -0
- package/src/store.ts +22 -0
- package/test/89-completion-recursion.test.mjs +30 -10
- package/test/97-store-seed.test.mjs +105 -0
- package/test/98-completion-chaining.test.mjs +140 -0
- package/HOW_IT_WORKS.md +0 -5836
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// 98-completion-chaining.test.mjs — the completion recursion NESTS, and the
|
|
2
|
+
// two properties that make that reachable are pinned here.
|
|
3
|
+
//
|
|
4
|
+
// `graph-search.ts`'s `recompleteNode` used to refuse any re-cover inside a
|
|
5
|
+
// re-cover (`recompleteOpen.size > 0`), so a chain through a PRODUCED composite
|
|
6
|
+
// stopped after a single recomposition: the depth the A*LD substrate licenses
|
|
7
|
+
// was unreachable. The recursion now nests, but only through ACCEPTED
|
|
8
|
+
// completions and only along the produced form's own parts — and with per-query
|
|
9
|
+
// work that stays the answer's (test/89 pins the cost).
|
|
10
|
+
//
|
|
11
|
+
// WHY THESE AND NOT test/15's §9–§12: those exercise chains whose parts all live
|
|
12
|
+
// INSIDE the query's own span ("a e", "x y", "p q r"); they pass even with
|
|
13
|
+
// `recompleteNode` disabled outright. §6 reaches a produced composite, but only
|
|
14
|
+
// one recomposition deep. The cases here fail on the pre-change tree:
|
|
15
|
+
// 1. the chain stops at the intermediate composite ("m n", not "z"), and
|
|
16
|
+
// 2. the nested derivation never reaches the rationale.
|
|
17
|
+
//
|
|
18
|
+
// MEASURED on the pre-change tree (this file's fixtures):
|
|
19
|
+
// respondText("seed") === "m n" (one recomposition)
|
|
20
|
+
// rationale moves = no `fuse`, no `recompose`
|
|
21
|
+
// After the change: "z", and both moves present.
|
|
22
|
+
|
|
23
|
+
import { test } from "node:test";
|
|
24
|
+
import assert from "node:assert/strict";
|
|
25
|
+
import { Mind } from "../dist/src/index.js";
|
|
26
|
+
|
|
27
|
+
/** seed → "p q" → (p→r, q→s) → "r s" → "m n" → (m→x, n→y) → "x y" → z
|
|
28
|
+
*
|
|
29
|
+
* The query names only `seed`. Every form after the first hop is PRODUCED by
|
|
30
|
+
* an edge, so reaching `z` requires TWO nested completions: "p q" recomposes to
|
|
31
|
+
* "m n", and "m n" recomposes again to "x y" → z. A single-level completion
|
|
32
|
+
* stops one composite short, at "m n". */
|
|
33
|
+
async function deepChain() {
|
|
34
|
+
const m = new Mind({ seed: 7 });
|
|
35
|
+
await m.ingest([
|
|
36
|
+
["seed", "p q"],
|
|
37
|
+
["p", "r"],
|
|
38
|
+
["q", "s"],
|
|
39
|
+
["r s", "m n"],
|
|
40
|
+
["m", "x"],
|
|
41
|
+
["n", "y"],
|
|
42
|
+
["x y", "z"],
|
|
43
|
+
]);
|
|
44
|
+
return m;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
test("a produced composite completes through two nested recompositions", async () => {
|
|
48
|
+
const m = await deepChain();
|
|
49
|
+
assert.equal(
|
|
50
|
+
(await m.respondText("seed")).replace(/\0+/g, ""),
|
|
51
|
+
"z",
|
|
52
|
+
);
|
|
53
|
+
await m.store.close();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("nested completions reach the rationale", async () => {
|
|
57
|
+
const m = await deepChain();
|
|
58
|
+
const steps = [];
|
|
59
|
+
await m.respondText("seed", (s) => steps.push(s));
|
|
60
|
+
const moves = new Set(steps.map((s) => s.mechanism.at(-1)));
|
|
61
|
+
// The top cover cannot fabricate these on its own: recomposition happens
|
|
62
|
+
// inside the nested solves, and they were invisible before the sink was
|
|
63
|
+
// threaded through.
|
|
64
|
+
assert.ok(
|
|
65
|
+
moves.has("recompose"),
|
|
66
|
+
`expected a recompose move in the rationale, got: ${[...moves].join(", ")}`,
|
|
67
|
+
);
|
|
68
|
+
assert.ok(
|
|
69
|
+
moves.has("fuse"),
|
|
70
|
+
`expected a fuse move in the rationale, got: ${[...moves].join(", ")}`,
|
|
71
|
+
);
|
|
72
|
+
await m.store.close();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("a produced composite is completed by its own parts, not by a learned form inside it", async () => {
|
|
76
|
+
// The produced bytes are "hi there"; `hi` is a learned context that is NOT one
|
|
77
|
+
// of their parts. Re-recognising arbitrary forms inside the bytes is what
|
|
78
|
+
// let a hub-heavy utterance explode on the trained store (`respond("hi.")`:
|
|
79
|
+
// 37 bytes, seven hub openers, 2.5 GB, OOM) — the produced form is decomposed
|
|
80
|
+
// instead by the machinery that owns its shape (leaves/splits), with the
|
|
81
|
+
// recognised SITES filtered to the node's own kids.
|
|
82
|
+
//
|
|
83
|
+
// MEASURED on the pre-change tree: no `recompose` move here at all — the
|
|
84
|
+
// unrelated `hi` site was taken instead of decomposing the form.
|
|
85
|
+
const m = new Mind({ seed: 7 });
|
|
86
|
+
await m.ingest([
|
|
87
|
+
["seed", "hi there"],
|
|
88
|
+
["hi", "KLX"],
|
|
89
|
+
]);
|
|
90
|
+
const steps = [];
|
|
91
|
+
const answer = (await m.respondText("seed", (s) => steps.push(s)))
|
|
92
|
+
.replace(/\0+/g, "")
|
|
93
|
+
.trim();
|
|
94
|
+
assert.equal(answer, "hi there");
|
|
95
|
+
const moves = new Set(steps.map((s) => s.mechanism.at(-1)));
|
|
96
|
+
assert.ok(
|
|
97
|
+
moves.has("recompose"),
|
|
98
|
+
`expected the completion to decompose the form by its parts, got: ${
|
|
99
|
+
[...moves].join(", ")
|
|
100
|
+
}`,
|
|
101
|
+
);
|
|
102
|
+
await m.store.close();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("the chain runs as deep as the graph licenses (four nested completions)", async () => {
|
|
106
|
+
const m = new Mind({ seed: 7 });
|
|
107
|
+
await m.ingest([
|
|
108
|
+
["seed", "p1 q1"],
|
|
109
|
+
["p1", "a1"],
|
|
110
|
+
["q1", "b1"],
|
|
111
|
+
["a1 b1", "p2 q2"],
|
|
112
|
+
["p2", "a2"],
|
|
113
|
+
["q2", "b2"],
|
|
114
|
+
["a2 b2", "p3 q3"],
|
|
115
|
+
["p3", "a3"],
|
|
116
|
+
["q3", "b3"],
|
|
117
|
+
["a3 b3", "FIM"],
|
|
118
|
+
]);
|
|
119
|
+
assert.equal((await m.respondText("seed")).replace(/\0+/g, ""), "FIM");
|
|
120
|
+
await m.store.close();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("a completion cycle terminates deterministically (the stack is the guard)", async () => {
|
|
124
|
+
// The recomposition of "x y" leads back to "x y" itself. Membership in
|
|
125
|
+
// `recompleteOpen` refuses the re-entry, so the recursion terminates on the
|
|
126
|
+
// form it already holds instead of re-covering it forever — and it answers
|
|
127
|
+
// the same bytes twice.
|
|
128
|
+
const m = new Mind({ seed: 7 });
|
|
129
|
+
await m.ingest([
|
|
130
|
+
["seed", "x y"],
|
|
131
|
+
["x", "A"],
|
|
132
|
+
["y", "B"],
|
|
133
|
+
["A B", "x y"],
|
|
134
|
+
]);
|
|
135
|
+
const first = (await m.respondText("seed")).replace(/\0+/g, "").trim();
|
|
136
|
+
const second = (await m.respondText("seed")).replace(/\0+/g, "").trim();
|
|
137
|
+
assert.equal(first, second);
|
|
138
|
+
assert.equal(first, "x y");
|
|
139
|
+
await m.store.close();
|
|
140
|
+
});
|