@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.
Files changed (110) hide show
  1. package/AGENTS.md +470 -0
  2. package/AUTHORS.md +12 -0
  3. package/COMMERCIAL-LICENSE.md +19 -0
  4. package/CONTRIBUTING.md +15 -0
  5. package/HOW_IT_WORKS.md +4210 -0
  6. package/LICENSE.md +117 -0
  7. package/README.md +290 -0
  8. package/TRADEMARKS.md +19 -0
  9. package/example/demo.ts +46 -0
  10. package/example/train_base.ts +2675 -0
  11. package/index.html +893 -0
  12. package/package.json +25 -0
  13. package/src/alphabet.ts +34 -0
  14. package/src/alu/README.md +332 -0
  15. package/src/alu/src/alu.ts +541 -0
  16. package/src/alu/src/expr.ts +339 -0
  17. package/src/alu/src/index.ts +115 -0
  18. package/src/alu/src/kernel-arith.ts +377 -0
  19. package/src/alu/src/kernel-bits.ts +199 -0
  20. package/src/alu/src/kernel-logic.ts +102 -0
  21. package/src/alu/src/kernel-nd.ts +235 -0
  22. package/src/alu/src/kernel-numeric.ts +466 -0
  23. package/src/alu/src/operation.ts +344 -0
  24. package/src/alu/src/parser.ts +574 -0
  25. package/src/alu/src/resonance.ts +161 -0
  26. package/src/alu/src/text.ts +83 -0
  27. package/src/alu/src/value.ts +327 -0
  28. package/src/alu/test/alu.test.ts +1004 -0
  29. package/src/bytes.ts +62 -0
  30. package/src/config.ts +227 -0
  31. package/src/derive/README.md +295 -0
  32. package/src/derive/src/deduction.ts +263 -0
  33. package/src/derive/src/index.ts +24 -0
  34. package/src/derive/src/priority-queue.ts +70 -0
  35. package/src/derive/src/rewrite.ts +127 -0
  36. package/src/derive/src/trie.ts +242 -0
  37. package/src/derive/test/derive.test.ts +137 -0
  38. package/src/extension.ts +42 -0
  39. package/src/geometry.ts +511 -0
  40. package/src/index.ts +38 -0
  41. package/src/ingest-cache.ts +224 -0
  42. package/src/mind/articulation.ts +137 -0
  43. package/src/mind/attention.ts +1061 -0
  44. package/src/mind/canonical.ts +107 -0
  45. package/src/mind/graph-search.ts +1100 -0
  46. package/src/mind/index.ts +18 -0
  47. package/src/mind/junction.ts +347 -0
  48. package/src/mind/learning.ts +246 -0
  49. package/src/mind/match.ts +507 -0
  50. package/src/mind/mechanisms/alu.ts +33 -0
  51. package/src/mind/mechanisms/cast.ts +568 -0
  52. package/src/mind/mechanisms/confluence.ts +268 -0
  53. package/src/mind/mechanisms/cover.ts +248 -0
  54. package/src/mind/mechanisms/extraction.ts +422 -0
  55. package/src/mind/mechanisms/recall.ts +241 -0
  56. package/src/mind/mind.ts +483 -0
  57. package/src/mind/pipeline-mechanism.ts +326 -0
  58. package/src/mind/pipeline.ts +300 -0
  59. package/src/mind/primitives.ts +201 -0
  60. package/src/mind/rationale.ts +275 -0
  61. package/src/mind/reasoning.ts +198 -0
  62. package/src/mind/recognition.ts +234 -0
  63. package/src/mind/resonance.ts +0 -0
  64. package/src/mind/trace.ts +108 -0
  65. package/src/mind/traverse.ts +556 -0
  66. package/src/mind/types.ts +281 -0
  67. package/src/rabitq-hnsw/README.md +303 -0
  68. package/src/rabitq-hnsw/src/database.ts +492 -0
  69. package/src/rabitq-hnsw/src/heap.ts +90 -0
  70. package/src/rabitq-hnsw/src/hnsw.ts +514 -0
  71. package/src/rabitq-hnsw/src/index.ts +15 -0
  72. package/src/rabitq-hnsw/src/prng.ts +39 -0
  73. package/src/rabitq-hnsw/src/rabitq.ts +308 -0
  74. package/src/rabitq-hnsw/src/store.ts +994 -0
  75. package/src/rabitq-hnsw/test/hnsw.test.ts +1213 -0
  76. package/src/store-sqlite.ts +928 -0
  77. package/src/store.ts +2148 -0
  78. package/src/vec.ts +124 -0
  79. package/test/00-extract.test.mjs +151 -0
  80. package/test/01-floor.test.mjs +20 -0
  81. package/test/02-roundtrip.test.mjs +83 -0
  82. package/test/03-recall.test.mjs +98 -0
  83. package/test/04-think.test.mjs +627 -0
  84. package/test/05-concepts.test.mjs +84 -0
  85. package/test/08-storage.test.mjs +948 -0
  86. package/test/09-edges.test.mjs +65 -0
  87. package/test/11-universality.test.mjs +180 -0
  88. package/test/13-conversation.test.mjs +228 -0
  89. package/test/14-scaling.test.mjs +503 -0
  90. package/test/15-decomposition-gap.test.mjs +0 -0
  91. package/test/16-bridge.test.mjs +250 -0
  92. package/test/17-intelligence.test.mjs +240 -0
  93. package/test/18-alu.test.mjs +209 -0
  94. package/test/19-nd.test.mjs +254 -0
  95. package/test/20-stability.test.mjs +489 -0
  96. package/test/21-partial.test.mjs +158 -0
  97. package/test/22-multihop.test.mjs +130 -0
  98. package/test/23-rationale.test.mjs +316 -0
  99. package/test/24-generalization.test.mjs +416 -0
  100. package/test/25-embedding.test.mjs +75 -0
  101. package/test/26-cooperative.test.mjs +89 -0
  102. package/test/27-saturation-drop.test.mjs +637 -0
  103. package/test/28-unknowable.test.mjs +88 -0
  104. package/test/29-counterfactual.test.mjs +476 -0
  105. package/test/30-conflict-resolution.test.mjs +166 -0
  106. package/test/31-audit.test.mjs +288 -0
  107. package/test/32-confluence.test.mjs +173 -0
  108. package/test/33-multi-candidate.test.mjs +222 -0
  109. package/test/34-cross-region.test.mjs +252 -0
  110. package/tsconfig.json +16 -0
@@ -0,0 +1,209 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { Mind } from "../dist/src/index.js";
4
+ import { SQliteStore } from "../dist/src/store-sqlite.js";
5
+
6
+ // =========================================================================
7
+ // THE ALU — arithmetic / logic / numerical computation as learned facts
8
+ // =========================================================================
9
+ // The ALU is not "hard-coded" logic masquerading as intelligence.
10
+ // It is a proof of concept: the symbolic component of Sema allows manual rules to be used alongside derived rules.
11
+ // The `alu` sub-lib gives the mind a minimal computational kernel — one logic
12
+ // gate (nand), the field-and-order primitives, and one limit operator
13
+ // (converge) — from which arithmetic, logic, and numerical analysis all derive.
14
+ // Its operations are MANUAL rules that compose in the SAME lightest-derivation
15
+ // search as learned facts (see src/mind/graph-search.ts / the ALU MindExtension): a query's
16
+ // operation is recognised (literally or, generically, by resonance), evaluated,
17
+ // and folded into the cover as a recognised completion.
18
+ //
19
+ // Two properties these tests pin down:
20
+ // • COMPUTATION where the corpus is silent — the mind answers "2+2" with "4"
21
+ // having learned no arithmetic, because the ALU rule grounds it.
22
+ // • THE ALU ALWAYS WINS — a computation the query invokes is authoritative for
23
+ // its span: it OVERRIDES any learned fact for the same bytes (even a
24
+ // deliberately-trained "2+2"→"5" yields "4"). This overrides only the
25
+ // colliding span, not the rest of thinking: a query with no operation
26
+ // behaves exactly as before, and a computation composes with an unrelated
27
+ // rewrite in one answer (the rest of the pre-ALU baseline is unchanged; see
28
+ // the other test files, all still green).
29
+
30
+ const mk = () => new Mind({ seed: 7 });
31
+
32
+ // ── 1. Arithmetic with NO training — pure computation ────────────────────
33
+
34
+ test("computes a sum with no arithmetic ever trained", async () => {
35
+ const m = mk();
36
+ // The store is empty of arithmetic facts; "4" comes from the ALU rule alone.
37
+ assert.equal(await m.respondText("2+2"), "4");
38
+ await m.store.close();
39
+ });
40
+
41
+ test("computes product, difference, quotient", async () => {
42
+ const m = mk();
43
+ assert.equal(await m.respondText("6*7"), "42");
44
+ assert.equal(await m.respondText("10-3"), "7");
45
+ assert.equal(await m.respondText("100/4"), "25");
46
+ await m.store.close();
47
+ });
48
+
49
+ test("multi-digit operands the chunker would split are read whole", async () => {
50
+ const m = mk();
51
+ assert.equal(await m.respondText("12+30"), "42");
52
+ assert.equal(await m.respondText("123+456"), "579");
53
+ await m.store.close();
54
+ });
55
+
56
+ // ── 2. The computation inside the asker's framing ────────────────────────
57
+
58
+ test("a computation fires inside a question, like a learned part would", async () => {
59
+ const m = mk();
60
+ assert.equal(await m.respondText("what is 2+2"), "4");
61
+ assert.equal(await m.respondText("compute 6*7 now"), "42");
62
+ await m.store.close();
63
+ });
64
+
65
+ test("punctuation around a computation is preserved", async () => {
66
+ const m = mk();
67
+ assert.equal(await m.respondText("2+2."), "4.");
68
+ assert.equal(await m.respondText("2+2?"), "4?");
69
+ await m.store.close();
70
+ });
71
+
72
+ // ── 3. Composition: chaining and precedence (the recursive ALU) ──────────
73
+
74
+ test("chained arithmetic reduces fully (composition for free)", async () => {
75
+ const m = mk();
76
+ assert.equal(await m.respondText("1+2+3"), "6");
77
+ assert.equal(await m.respondText("7-2-1"), "4");
78
+ await m.store.close();
79
+ });
80
+
81
+ test("precedence is honoured by the recursive expression evaluator", async () => {
82
+ const m = mk();
83
+ assert.equal(await m.respondText("2+3*4"), "14"); // not 20
84
+ await m.store.close();
85
+ });
86
+
87
+ test("spaced arithmetic is the same as packed — the separator is bridged, not required", async () => {
88
+ const m = mk();
89
+ // "3 + 3" and "3+3" are one expression: the run tolerates a bridged separator
90
+ // (whitespace), exactly as the cover bridges the space in "ice fire". No
91
+ // spelling is privileged — recognising 3+3 but not 3 + 3 was the absurd lexer.
92
+ assert.equal(await m.respondText("3 + 3"), "6");
93
+ assert.equal(await m.respondText("2 + 3 * 4"), "14");
94
+ assert.equal(await m.respondText("10 - 3"), "7");
95
+ await m.store.close();
96
+ });
97
+
98
+ // ── 4. Unary operations recognised by their NAME ─────────────────────────
99
+
100
+ test("a named unary op applies to its operand", async () => {
101
+ const m = mk();
102
+ assert.equal(await m.respondText("sqrt 144"), "12");
103
+ assert.equal(await m.respondText("negate 5"), "-5");
104
+ await m.store.close();
105
+ });
106
+
107
+ // ── 5. The numerical limit layer over an EXPRESSION (recursive calls) ────
108
+ // A derivative / integral acts on a FUNCTION, evaluated by a recursive
109
+ // application of the same ALU. The OPERATION is named in words the query
110
+ // carries; the expression and point/bounds are parsed from the rest.
111
+
112
+ test("derivative of an expression at a point", async () => {
113
+ const m = mk();
114
+ assert.equal(await m.respondText("derivative of x^2 at 3"), "6");
115
+ await m.store.close();
116
+ });
117
+
118
+ test("definite integral of an expression over bounds", async () => {
119
+ const m = mk();
120
+ assert.equal(await m.respondText("integral of x from 0 to 1"), "0.5");
121
+ await m.store.close();
122
+ });
123
+
124
+ // ── 6. The polymorphic inverse ───────────────────────────────────────────
125
+ // The inverse of a NUMBER is its negation; the inverse of a learnt FORM is its
126
+ // grounded opposite (the corpus's own opposition relation, recalled — never a
127
+ // fabricated antonym).
128
+
129
+ test("inverse of a number is its negation", async () => {
130
+ const m = mk();
131
+ assert.equal(await m.respondText("inverse 3"), "-3");
132
+ assert.equal(await m.respondText("opposite 7"), "-7");
133
+ await m.store.close();
134
+ });
135
+
136
+ test("inverse of a form is its grounded opposite", async () => {
137
+ const m = mk();
138
+ await m.ingest([["large", "small"]]); // the corpus grounds the opposition
139
+ assert.equal(await m.respondText("inverse large"), "small");
140
+ await m.store.close();
141
+ });
142
+
143
+ // ── 7. THE ALU ALWAYS WINS — computation overrides recall; rest unchanged ─
144
+
145
+ test("a computation overrides a learned fact (the ALU always wins)", async () => {
146
+ const m = mk();
147
+ await m.ingest([["2+2", "5"]]); // a deliberately wrong learnt fact
148
+ // The ALU must override the recall: a computation is authoritative for its
149
+ // span, so the computed answer wins over the corpus's stored one.
150
+ assert.equal(await m.respondText("2+2"), "4");
151
+ await m.store.close();
152
+ });
153
+
154
+ test("a computation and a learned rewrite compose in one answer", async () => {
155
+ const m = mk();
156
+ await m.ingest([["ice", "cold"]]);
157
+ const r = await m.respondText("ice 2+2");
158
+ assert.ok(r.includes("cold") && r.includes("4"), `got ${JSON.stringify(r)}`);
159
+ await m.store.close();
160
+ });
161
+
162
+ test("the ALU wins only on its span — a colliding fact is overridden, an unrelated rewrite still composes", async () => {
163
+ const m = mk();
164
+ // "2+2"→"5" collides with the computation; "ice"→"cold" does not.
165
+ await m.ingest([["2+2", "5"], ["ice", "cold"]]);
166
+ const r = await m.respondText("ice 2+2");
167
+ // The computation overrides "5" on its own span (→ "4"), yet the masking is
168
+ // surgical: the unrelated "ice"→"cold" rewrite is untouched, and "5" never
169
+ // leaks in.
170
+ assert.ok(
171
+ r.includes("cold") && r.includes("4") && !r.includes("5"),
172
+ `got ${JSON.stringify(r)}`,
173
+ );
174
+ await m.store.close();
175
+ });
176
+
177
+ test("a query with no operation is untouched by the ALU", async () => {
178
+ const m = mk();
179
+ await m.ingest([["ice", "cold"], ["fire", "hot"]]);
180
+ // Exactly the pre-ALU behaviour (see 04-think): multi-part rewrite, no ALU.
181
+ assert.equal(await m.respondText("ice fire"), "cold hot");
182
+ await m.store.close();
183
+ });
184
+
185
+ // ── 8. Disabling the ALU restores exact pre-ALU behaviour ────────────────
186
+
187
+ test("alu.enabled=false makes computation inert", async () => {
188
+ const m = new Mind({
189
+ seed: 7,
190
+ alu: { enabled: false },
191
+ });
192
+ // No arithmetic learnt and the ALU off → the query is not grounded as "4";
193
+ // it falls through to the resonant path (never the computed answer).
194
+ const r = await m.respondText("2+2");
195
+ assert.notEqual(r, "4");
196
+ await m.store.close();
197
+ });
198
+
199
+ // ── 9. Determinism (same seed/training → same answer) ────────────────────
200
+
201
+ test("computation is deterministic", async () => {
202
+ const run = async () => {
203
+ const m = mk();
204
+ const r = await m.respondText("12+30");
205
+ await m.store.close();
206
+ return r;
207
+ };
208
+ assert.equal(await run(), await run());
209
+ });
@@ -0,0 +1,254 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { Mind } from "../dist/src/index.js";
4
+ import { SQliteStore } from "../dist/src/store-sqlite.js";
5
+
6
+ // =========================================================================
7
+ // N-DIMENSIONAL VALUES through the MIND — the ALU's value generalised
8
+ // =========================================================================
9
+ //
10
+ // The ALU's `Value` gained one recursive case: an `nd` is an ordered list whose
11
+ // elements are themselves Values of ANY domain — a scalar (bit/int/real/symbol)
12
+ // or another `nd`. That single case is the whole generalisation: a matrix is an
13
+ // nd of nds, a ragged table is an nd of unequal-length nds, and a heterogeneous
14
+ // row mixes numbers and symbols. There is no separate vector/matrix type and no
15
+ // new primitive per rank.
16
+ //
17
+ // Two properties make "all operations support nd" hold, and BOTH route through
18
+ // the mind's resonance — the same meaning channel concept hops and the
19
+ // polymorphic inverse already use:
20
+ //
21
+ // • BROADCAST — every SCALAR op lifts over a list element-wise, recursing
22
+ // through nesting. The polymorphic inverse is the sharp case: over a
23
+ // heterogeneous list it negates the numbers and resolves each symbol's
24
+ // RESONANT OPPOSITE, grounded in the corpus (never fabricated).
25
+ //
26
+ // • HIGHER-ORDER ops (map / reduce / filter / find) take an OPERATION as their
27
+ // argument, resolved INTELLIGENTLY — a literal surface form ("+","max"), a
28
+ // synonym, or a meaning the bytes do not spell, via resonance. So the fold
29
+ // of a reduce, the transform of a map, the predicate of a filter is ANY
30
+ // operation the kernel already has: there is no bespoke callback table.
31
+ //
32
+ // `Alu.compute(op, operandBytes)` is the entry point: it pre-resolves the
33
+ // resonance every reachable symbol needs (recursing into nd) and runs the
34
+ // synchronous kernel against it. Operands are byte spans; a bracket literal
35
+ // "[1,2,3]" decodes to an nd and the result re-encodes canonically.
36
+ //
37
+ // The programmatic API belongs to the ALU's own instance, not the Mind: the
38
+ // Mind only knows the generic MindExtension contract (parse). A caller that
39
+ // wants direct computation registers the ALU itself through the extensions
40
+ // factory — which receives the mind's host port — and keeps the reference.
41
+
42
+ import { Alu } from "../dist/src/alu/src/index.js";
43
+ import { aluToMechanism } from "../dist/src/mind/pipeline.js";
44
+
45
+ /** A mind wired with an ALU extension whose instance the caller keeps: the
46
+ * mind stays generic; the computational API lives on the extension. */
47
+ const mk = () => {
48
+ let alu;
49
+ const m = new Mind({
50
+ seed: 7,
51
+ alu: { enabled: false }, // the built-in stays off; we hold our own
52
+ mechanismFactories: [(host) => {
53
+ alu = new Alu({}, host);
54
+ return aluToMechanism(alu);
55
+ }],
56
+ });
57
+ m.alu = alu; // test convenience: carry the handle alongside the mind
58
+ return m;
59
+ };
60
+
61
+ const enc = (s) => new TextEncoder().encode(s);
62
+ const dec = (b) => (b === null ? null : new TextDecoder().decode(b));
63
+ /** Compute and decode in one step. */
64
+ const compute = async (m, op, ...operands) =>
65
+ dec(await m.alu.compute(op, operands.map(enc)));
66
+
67
+ // ── 1. Broadcast: arithmetic lifts over lists with NO training ───────────
68
+
69
+ test("a scalar op broadcasts over a list, and over two lists element-wise", async () => {
70
+ const m = mk();
71
+ assert.equal(await compute(m, "add", "[1,2,3]", "[4,5,6]"), "[5,7,9]");
72
+ assert.equal(await compute(m, "add", "[1,2,3]", "10"), "[11,12,13]");
73
+ assert.equal(await compute(m, "multiply", "[1,2,3]", "[2,2,2]"), "[2,4,6]");
74
+ await m.store.close();
75
+ });
76
+
77
+ test("broadcast recurses through nesting — a matrix op is the same operation", async () => {
78
+ const m = mk();
79
+ // The element-wise lift re-enters apply per element, so an nd of nds lifts
80
+ // twice with no matrix-specific code.
81
+ assert.equal(
82
+ await compute(m, "add", "[[1,2],[3,4]]", "[[10,20],[30,40]]"),
83
+ "[[11,22],[33,44]]",
84
+ );
85
+ assert.equal(
86
+ await compute(m, "multiply", "[[1,2],[3,4]]", "3"),
87
+ "[[3,6],[9,12]]",
88
+ );
89
+ await m.store.close();
90
+ });
91
+
92
+ test("a list with no training computes where the corpus is silent (like 2+2)", async () => {
93
+ const m = mk();
94
+ // Exactly the ALU-grounds-computation property of 18-alu, now over a list.
95
+ assert.equal(await compute(m, "sqrt", "[1,4,9,16]"), "[1,2,3,4]");
96
+ await m.store.close();
97
+ });
98
+
99
+ test("a list is recognised by ANY consistent separator, no spelling privileged", async () => {
100
+ const m = mk();
101
+ // The Mind recognises a list as a run of element-values joined by a consistent
102
+ // separator — the same connective the cover bridges between recognised spans.
103
+ // Bracket+comma, bare spaces, commas, or the word "and" all name the SAME list;
104
+ // the bracket form is just the canonical OUTPUT spelling.
105
+ for (const xs of ["[1,2,3]", "1 2 3", "1, 2, 3", "1 and 2 and 3"]) {
106
+ assert.equal(await compute(m, "reverse", xs), "[3,2,1]", xs);
107
+ assert.equal(await compute(m, "reduce", xs, "+"), "6", xs);
108
+ }
109
+ // Two separator-free lists broadcast element-wise just the same.
110
+ assert.equal(await compute(m, "add", "1 2 3", "4 5 6"), "[5,7,9]");
111
+ await m.store.close();
112
+ });
113
+
114
+ test("an inconsistent separator is NOT one list (it stays an opaque scalar)", async () => {
115
+ const m = mk();
116
+ // "1 2,3" mixes a space and a comma between operands — not one consistent
117
+ // connective, so it is not recognised as a sequence; length over a non-list
118
+ // declines, exactly the "this rule does not fire" contract.
119
+ assert.equal(await compute(m, "length", "1 2,3"), null);
120
+ await m.store.close();
121
+ });
122
+
123
+ // ── 2. The polymorphic inverse broadcasts, resolving each symbol's opposite ─
124
+
125
+ test("inverse over a heterogeneous list: numbers negate, symbols resonate", async () => {
126
+ const m = mk();
127
+ // The corpus grounds the oppositions; the inverse recalls them, never invents.
128
+ await m.ingest([["large", "small"], ["tall", "short"]]);
129
+ // One op over [symbol, number, symbol] — each element dispatched on its domain.
130
+ assert.equal(
131
+ await compute(m, "inverse", "[large,3,tall]"),
132
+ "[small,-3,short]",
133
+ );
134
+ await m.store.close();
135
+ });
136
+
137
+ test("a symbol with no grounded opposite is left unchanged inside the list", async () => {
138
+ const m = mk();
139
+ await m.ingest([["hot", "cold"]]);
140
+ // "hot" resolves; the ungrounded "zorp" is faithfully left as-is (not faked).
141
+ assert.equal(await compute(m, "inverse", "[hot,zorp]"), "[cold,zorp]");
142
+ await m.store.close();
143
+ });
144
+
145
+ // ── 3. reduce: the fold is ANY existing binary operation ─────────────────
146
+
147
+ test("reduce folds by the operation named — +, *, max, min are sum/product/extremes", async () => {
148
+ const m = mk();
149
+ assert.equal(await compute(m, "reduce", "[1,2,3,4]", "+"), "10");
150
+ assert.equal(await compute(m, "reduce", "[1,2,3,4]", "*"), "24");
151
+ assert.equal(await compute(m, "reduce", "[3,1,4,1,5,9]", "max"), "9");
152
+ assert.equal(await compute(m, "reduce", "[3,1,4,1,5,9]", "min"), "1");
153
+ await m.store.close();
154
+ });
155
+
156
+ test("reduce by a binary op broadcasts — reduce(rows, +) is the column sum", async () => {
157
+ const m = mk();
158
+ // The fold's "+" itself broadcasts over the row-lists, so summing the rows of a
159
+ // matrix yields the per-column totals — composition, not a special routine.
160
+ assert.equal(
161
+ await compute(m, "reduce", "[[1,2,3],[4,5,6],[7,8,9]]", "+"),
162
+ "[12,15,18]",
163
+ );
164
+ await m.store.close();
165
+ });
166
+
167
+ // ── 4. map / filter / find with operation arguments ──────────────────────
168
+
169
+ test("map applies the named unary op to each element", async () => {
170
+ const m = mk();
171
+ assert.equal(await compute(m, "map", "[1,2,3]", "negate"), "[-1,-2,-3]");
172
+ assert.equal(await compute(m, "map", "[1,4,9]", "sqrt"), "[1,2,3]");
173
+ await m.store.close();
174
+ });
175
+
176
+ test("filter keeps elements the predicate accepts; find returns the first (else empty)", async () => {
177
+ const m = mk();
178
+ // "sign" is 0 for zero, 1 for positive — a truthiness predicate over the list.
179
+ assert.equal(await compute(m, "filter", "[0,5,0,3,0,8]", "sign"), "[5,3,8]");
180
+ assert.equal(await compute(m, "find", "[0,0,7,0]", "sign"), "7");
181
+ // nothing matches → the empty list (a grounded "nothing", not a fabricated hit)
182
+ assert.equal(await compute(m, "find", "[0,0,0]", "sign"), "[]");
183
+ await m.store.close();
184
+ });
185
+
186
+ // ── 5. The operation argument is resolved through the mind's recognition ──
187
+ // machinery — ANY operation named however the kernel knows it (a synonym is
188
+ // a surface form; an unspelled MEANING resonates by gist — that gist path is
189
+ // exercised in the ALU unit suite with a stub, since the live gist index has
190
+ // a high acceptance threshold).
191
+
192
+ test("the same fold is named by ANY of an operation's synonyms, not a fixed token", async () => {
193
+ const m = mk();
194
+ // add answers to "+", "plus", "sum", "total"; multiply to "*", "times",
195
+ // "product". reduce reuses whichever the asker names — there is no bespoke
196
+ // reduce-operator table; it is the kernel's own operation vocabulary.
197
+ for (const word of ["+", "plus", "sum", "total"]) {
198
+ assert.equal(await compute(m, "reduce", "[10,20,30]", word), "60", word);
199
+ }
200
+ for (const word of ["*", "times", "product"]) {
201
+ assert.equal(await compute(m, "reduce", "[2,3,4]", word), "24", word);
202
+ }
203
+ await m.store.close();
204
+ });
205
+
206
+ // ── 6. Structural plumbing end-to-end ────────────────────────────────────
207
+
208
+ test("length / at / reverse / concat / zip / range / rank / shape over the mind", async () => {
209
+ const m = mk();
210
+ assert.equal(await compute(m, "length", "[5,6,7,8]"), "4");
211
+ assert.equal(await compute(m, "at", "[5,6,7,8]", "-1"), "8"); // negative = from end
212
+ assert.equal(await compute(m, "reverse", "[1,2,3]"), "[3,2,1]");
213
+ assert.equal(await compute(m, "concat", "[1,2]", "[3,4]"), "[1,2,3,4]");
214
+ assert.equal(
215
+ await compute(m, "zip", "[1,2,3]", "[4,5,6]"),
216
+ "[[1,4],[2,5],[3,6]]",
217
+ );
218
+ assert.equal(await compute(m, "range", "5"), "[0,1,2,3,4]");
219
+ assert.equal(await compute(m, "rank", "[[1,2],[3,4]]"), "2");
220
+ assert.equal(await compute(m, "shape", "[[1,2,3],[4,5,6]]"), "[2,3]");
221
+ await m.store.close();
222
+ });
223
+
224
+ // ── 7. Composition and determinism ───────────────────────────────────────
225
+
226
+ test("a computed nd is itself an operand (composition for free)", async () => {
227
+ const m = mk();
228
+ // map to a new list, then reduce it — the result of one op feeds the next.
229
+ const mapped = await compute(m, "map", "[1,2,3,4]", "negate"); // "[-1,-2,-3,-4]"
230
+ assert.equal(mapped, "[-1,-2,-3,-4]");
231
+ const summed = await compute(m, "reduce", mapped, "+"); // "-10"
232
+ assert.equal(summed, "-10");
233
+ await m.store.close();
234
+ });
235
+
236
+ test("nd computation is deterministic and disabling the ALU makes it inert", async () => {
237
+ const run = async () => {
238
+ const m = mk();
239
+ const r = await compute(m, "add", "[1,2,3]", "[4,5,6]");
240
+ await m.store.close();
241
+ return r;
242
+ };
243
+ assert.equal(await run(), await run());
244
+
245
+ // A mind with no ALU extension at all: the operation vocabulary simply does
246
+ // not exist anywhere on the mind — computation is inert by absence, not by a
247
+ // flag check in the machinery.
248
+ const off = new Mind({
249
+ seed: 7,
250
+ alu: { enabled: false },
251
+ });
252
+ assert.equal(typeof off.compute, "undefined");
253
+ await off.store.close();
254
+ });