@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.
Files changed (46) hide show
  1. package/AGENTS.md +95 -843
  2. package/README.md +11 -11
  3. package/dist/src/mind/graph-search.d.ts +32 -22
  4. package/dist/src/mind/graph-search.js +97 -54
  5. package/dist/src/mind/mind.js +14 -0
  6. package/dist/src/store-sqlite.js +17 -0
  7. package/dist/src/store.d.ts +18 -0
  8. package/dist/src/store.js +10 -0
  9. package/docs/INDEX.md +71 -0
  10. package/docs/INVARIANTS.md +19 -0
  11. package/docs/architecture/bounded-reads.md +85 -0
  12. package/docs/architecture/caches.md +89 -0
  13. package/docs/architecture/commonality.md +45 -0
  14. package/docs/architecture/cost-model.md +71 -0
  15. package/docs/architecture/determinism.md +73 -0
  16. package/docs/architecture/exact-vs-approximate.md +47 -0
  17. package/docs/architecture/factored-machinery.md +28 -0
  18. package/docs/architecture/fold-contract.md +87 -0
  19. package/docs/architecture/halo-sketch.md +99 -0
  20. package/docs/architecture/match-project.md +62 -0
  21. package/docs/architecture/mechanism-market.md +95 -0
  22. package/docs/architecture/memoization.md +96 -0
  23. package/docs/architecture/meter.md +55 -0
  24. package/docs/architecture/saturation.md +92 -0
  25. package/docs/architecture/store.md +79 -0
  26. package/docs/architecture/thresholds.md +79 -0
  27. package/docs/failures/tempting-but-wrong.md +144 -0
  28. package/docs/harness/gates.md +56 -0
  29. package/docs/mechanisms/alu.md +75 -0
  30. package/docs/mechanisms/cast.md +75 -0
  31. package/docs/mechanisms/confluence.md +36 -0
  32. package/docs/mechanisms/cover.md +54 -0
  33. package/docs/mechanisms/extraction.md +53 -0
  34. package/docs/mechanisms/prefix-completion.md +54 -0
  35. package/docs/mechanisms/recall.md +69 -0
  36. package/docs/mechanisms/reference.md +58 -0
  37. package/jsr.json +1 -1
  38. package/package.json +1 -1
  39. package/src/mind/graph-search.ts +101 -55
  40. package/src/mind/mind.ts +14 -0
  41. package/src/store-sqlite.ts +19 -0
  42. package/src/store.ts +22 -0
  43. package/test/89-completion-recursion.test.mjs +30 -10
  44. package/test/97-store-seed.test.mjs +105 -0
  45. package/test/98-completion-chaining.test.mjs +140 -0
  46. 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
+ });