@hviana/sema 0.8.3 → 0.8.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 +11 -10
- package/README.md +17 -38
- package/dist/example/demo.js +85 -34
- package/dist/src/geometry.d.ts +0 -10
- package/dist/src/geometry.js +0 -12
- package/dist/src/meter.d.ts +31 -5
- package/dist/src/meter.js +31 -5
- package/dist/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.js +2 -1
- package/dist/src/mind/derivation.d.ts +201 -0
- package/dist/src/mind/derivation.js +327 -0
- package/dist/src/mind/graph-search.d.ts +2 -1
- package/dist/src/mind/graph-search.js +37 -15
- package/dist/src/mind/match.d.ts +2 -0
- package/dist/src/mind/match.js +2 -0
- package/dist/src/mind/mechanisms/alu.js +0 -2
- package/dist/src/mind/mechanisms/cast.d.ts +1 -5
- package/dist/src/mind/mechanisms/cast.js +15 -18
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
- package/dist/src/mind/mechanisms/confluence.js +3 -9
- package/dist/src/mind/mechanisms/cover.js +17 -20
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
- package/dist/src/mind/mechanisms/extraction.js +13 -8
- package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
- package/dist/src/mind/mechanisms/recall.d.ts +0 -1
- package/dist/src/mind/mechanisms/recall.js +8 -9
- package/dist/src/mind/mechanisms/reference.js +3 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +1 -4
- package/dist/src/mind/pipeline.js +106 -41
- package/dist/src/mind/rationale.d.ts +0 -11
- package/dist/src/mind/rationale.js +6 -32
- package/dist/src/mind/reasoning.d.ts +4 -30
- package/dist/src/mind/reasoning.js +191 -151
- package/dist/src/mind/types.js +6 -3
- package/docs/INDEX.md +23 -24
- package/docs/INVARIANTS.md +16 -17
- package/docs/architecture/bounded-reads.md +4 -4
- package/docs/architecture/closure.md +78 -0
- package/docs/architecture/commonality.md +27 -18
- package/docs/architecture/cost-model.md +5 -5
- package/docs/architecture/exact-vs-approximate.md +4 -4
- package/docs/architecture/factored-machinery.md +14 -14
- package/docs/architecture/mechanism-market.md +9 -9
- package/docs/architecture/meter.md +4 -5
- package/docs/architecture/store.md +2 -2
- package/docs/architecture/thresholds.md +1 -1
- package/docs/failures/tempting-but-wrong.md +11 -1
- package/docs/harness/gates.md +6 -6
- package/docs/mechanisms/cover.md +2 -2
- package/example/demo.ts +90 -37
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/geometry.ts +0 -13
- package/src/meter.ts +31 -5
- package/src/mind/articulation.ts +0 -1
- package/src/mind/attention.ts +2 -1
- package/src/mind/derivation.ts +477 -0
- package/src/mind/graph-search.ts +37 -20
- package/src/mind/match.ts +2 -0
- package/src/mind/mechanisms/alu.ts +0 -2
- package/src/mind/mechanisms/cast.ts +17 -21
- package/src/mind/mechanisms/confluence.ts +3 -13
- package/src/mind/mechanisms/cover.ts +17 -20
- package/src/mind/mechanisms/extraction.ts +13 -9
- package/src/mind/mechanisms/prefix-completion.ts +0 -1
- package/src/mind/mechanisms/recall.ts +7 -9
- package/src/mind/mechanisms/reference.ts +2 -3
- package/src/mind/pipeline-mechanism.ts +1 -4
- package/src/mind/pipeline.ts +121 -46
- package/src/mind/rationale.ts +6 -36
- package/src/mind/reasoning.ts +220 -178
- package/src/mind/types.ts +5 -2
- package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +3 -3
- package/test/133-the-decision-point-renders-the-state.test.mjs +204 -0
- package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
- package/test/135-one-law-any-producer.test.mjs +289 -0
- package/test/136-the-two-named-limits.test.mjs +205 -0
- package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
- package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
- package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
- package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
- package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
- package/test/142-the-layer-offers-only-what-the-law-admits.test.mjs +93 -0
- package/test/143-cycles-terminate-and-are-not-closure.test.mjs +62 -0
- package/test/36-already-answered-fusion.test.mjs +20 -2
- package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
- package/test/38-reason-restate-guard.test.mjs +22 -2
- package/test/55-cost-meter.test.mjs +6 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// 142 — the layer offers only what the law admits, which is what makes a refusal final.
|
|
2
|
+
//
|
|
3
|
+
// THE CONTRACT THIS PINS. `closure` stops when the law refuses the continuation the layer
|
|
4
|
+
// offered, and reads that refusal as "no continuation exists". That is valid only under
|
|
5
|
+
// ONE of two architectures: the layer searches for an admissible continuation and offers
|
|
6
|
+
// only a valid one (X), rather than offering candidates for the law to filter (Y). Sema is
|
|
7
|
+
// X — the producer tries the forward absorb first and reaches for the pivot only when the
|
|
8
|
+
// absorb's guards fail, returning null when neither exists — so the one offer the law can
|
|
9
|
+
// still refuse is the pivot without ownership, and it is the layer's last.
|
|
10
|
+
//
|
|
11
|
+
// 142.1 pins the premise: the law CAN refuse (a continuation that neither carries question
|
|
12
|
+
// material nor declares a move), and admits the two species. Without a refusal the contract
|
|
13
|
+
// would be vacuous. 142.2 pins the consequence in the engine: the walk ends at that refusal
|
|
14
|
+
// and the satisfying answer is untouched.
|
|
15
|
+
|
|
16
|
+
import { test } from "node:test";
|
|
17
|
+
import assert from "node:assert/strict";
|
|
18
|
+
import { Mind } from "../dist/src/index.js";
|
|
19
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
20
|
+
import { admissible } from "../dist/src/mind/derivation.js";
|
|
21
|
+
|
|
22
|
+
const QUERY = new TextEncoder().encode("ABCDEFGHIJ");
|
|
23
|
+
const W = 4;
|
|
24
|
+
const state = () => ({
|
|
25
|
+
product: new Uint8Array(0),
|
|
26
|
+
accounted: [],
|
|
27
|
+
remainder: [[0, 10]],
|
|
28
|
+
cost: 0,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("142.1 the law refuses what neither carries nor reaches, and admits both species", () => {
|
|
32
|
+
// neither: no window held, no move declared
|
|
33
|
+
assert.equal(
|
|
34
|
+
admissible(
|
|
35
|
+
state(),
|
|
36
|
+
{ product: new TextEncoder().encode("Paris"), contains: true, cost: 1 },
|
|
37
|
+
QUERY,
|
|
38
|
+
W,
|
|
39
|
+
),
|
|
40
|
+
null,
|
|
41
|
+
"an offer that neither carries nor reaches is refused — so offering one is not free",
|
|
42
|
+
);
|
|
43
|
+
// carries: the product holds a window of the remainder
|
|
44
|
+
assert.notEqual(
|
|
45
|
+
admissible(
|
|
46
|
+
state(),
|
|
47
|
+
{
|
|
48
|
+
product: new TextEncoder().encode("ZZZZABCDZZ"),
|
|
49
|
+
contains: true,
|
|
50
|
+
cost: 1,
|
|
51
|
+
},
|
|
52
|
+
QUERY,
|
|
53
|
+
W,
|
|
54
|
+
),
|
|
55
|
+
null,
|
|
56
|
+
"carrying admits",
|
|
57
|
+
);
|
|
58
|
+
// reaches: a declared move, which needs no question material
|
|
59
|
+
assert.notEqual(
|
|
60
|
+
admissible(
|
|
61
|
+
state(),
|
|
62
|
+
{
|
|
63
|
+
product: new TextEncoder().encode("Paris"),
|
|
64
|
+
contains: true,
|
|
65
|
+
reaches: true,
|
|
66
|
+
cost: 1,
|
|
67
|
+
},
|
|
68
|
+
QUERY,
|
|
69
|
+
W,
|
|
70
|
+
),
|
|
71
|
+
null,
|
|
72
|
+
"a declared move admits on its own ground",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("142.2 the walk ends at the refusal, with the satisfying answer untouched", async () => {
|
|
77
|
+
const LINKS = [
|
|
78
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
79
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
80
|
+
["the Eiffel Tower", "the Eiffel Tower is in Paris"],
|
|
81
|
+
];
|
|
82
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
83
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
84
|
+
await mind.ingest(LINKS);
|
|
85
|
+
const out = await mind.respond("What is the capital of France famous for");
|
|
86
|
+
const text = new TextDecoder().decode(out.bytes).replace(/\0+/g, "").trim();
|
|
87
|
+
await store.close();
|
|
88
|
+
assert.equal(
|
|
89
|
+
text,
|
|
90
|
+
LINKS[1][1],
|
|
91
|
+
"the layer's last offer was refused, and the answer stands",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// 143 — cycles, in the engine: three topologies, and what must hold for each.
|
|
2
|
+
//
|
|
3
|
+
// The law holds no `visited`, no `depth`, no `hop`, no `once` and no `cap` — cycle
|
|
4
|
+
// prevention belongs to the layer that controls states and consumption, and a cycle is not
|
|
5
|
+
// closure. So the engine must terminate on each topology anyway, and the answer must be a
|
|
6
|
+
// fact of the corpus rather than a composition typed around the loop.
|
|
7
|
+
//
|
|
8
|
+
// A→B→A · A→B→C→A · A→B, B→C, C→B (a loop with a tail, where the refusal is not trivial).
|
|
9
|
+
// Each fact is filed under both of its ends, the way the store files one, so the walk has
|
|
10
|
+
// every edge the loop needs.
|
|
11
|
+
|
|
12
|
+
import { test } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { Mind } from "../dist/src/index.js";
|
|
15
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
16
|
+
|
|
17
|
+
const TOPOLOGIES = {
|
|
18
|
+
"A→B→A": [
|
|
19
|
+
["Alice knows Bob", "Alice knows Bob"],
|
|
20
|
+
["Bob knows Alice", "Bob knows Alice"],
|
|
21
|
+
],
|
|
22
|
+
"A→B→C→A": [
|
|
23
|
+
["Alice knows Bob", "Alice knows Bob"],
|
|
24
|
+
["Bob knows Carol", "Bob knows Carol"],
|
|
25
|
+
["Carol knows Alice", "Carol knows Alice"],
|
|
26
|
+
],
|
|
27
|
+
"A→B, B→C, C→B": [
|
|
28
|
+
["Alice knows Bob", "Alice knows Bob"],
|
|
29
|
+
["Bob knows Carol", "Bob knows Carol"],
|
|
30
|
+
["Carol knows Bob", "Carol knows Bob"],
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async function ask(corpus, query) {
|
|
35
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
36
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
37
|
+
await mind.ingest(corpus);
|
|
38
|
+
const out = await mind.respond(query);
|
|
39
|
+
const text = new TextDecoder().decode(out.bytes).replace(/\0+/g, "").trim();
|
|
40
|
+
await store.close();
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const [name, corpus] of Object.entries(TOPOLOGIES)) {
|
|
45
|
+
test(`143 ${name} terminates and answers from the corpus`, async () => {
|
|
46
|
+
const first = await ask(corpus, "Alice knows");
|
|
47
|
+
const second = await ask(corpus, "Alice knows");
|
|
48
|
+
assert.equal(
|
|
49
|
+
second,
|
|
50
|
+
first,
|
|
51
|
+
"two runs agree: no order-dependent wander around the loop",
|
|
52
|
+
);
|
|
53
|
+
if (first.length > 0) {
|
|
54
|
+
assert.ok(
|
|
55
|
+
corpus.some(([, fact]) => first.includes(fact) || fact.includes(first)),
|
|
56
|
+
`the answer is one of the corpus's own facts, not a composition typed around the loop: ${
|
|
57
|
+
JSON.stringify(first)
|
|
58
|
+
}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -94,7 +94,16 @@ test("fuseAttention: a root whose continuation is already answered in the query
|
|
|
94
94
|
}),
|
|
95
95
|
guide,
|
|
96
96
|
};
|
|
97
|
-
|
|
97
|
+
// fusion consumes and returns the derivation STATE; these cases exercise the
|
|
98
|
+
// mechanism's own gates, so the state is the minimum one for them.
|
|
99
|
+
const out = dec(
|
|
100
|
+
(await fuseAttention(m, q, {
|
|
101
|
+
product: primary,
|
|
102
|
+
accounted: [],
|
|
103
|
+
remainder: [],
|
|
104
|
+
cost: 0,
|
|
105
|
+
}, pre)).product,
|
|
106
|
+
);
|
|
98
107
|
assert.ok(
|
|
99
108
|
!out.includes("reply-greet"),
|
|
100
109
|
`a root whose continuation is already answered in the query must not fuse in, got "${out}"`,
|
|
@@ -126,7 +135,16 @@ test("fuseAttention: ordinary multi-topic fusion (no embedded answers) is comple
|
|
|
126
135
|
}),
|
|
127
136
|
guide,
|
|
128
137
|
};
|
|
129
|
-
|
|
138
|
+
// fusion consumes and returns the derivation STATE; these cases exercise the
|
|
139
|
+
// mechanism's own gates, so the state is the minimum one for them.
|
|
140
|
+
const out = dec(
|
|
141
|
+
(await fuseAttention(m, q, {
|
|
142
|
+
product: primary,
|
|
143
|
+
accounted: [],
|
|
144
|
+
remainder: [],
|
|
145
|
+
cost: 0,
|
|
146
|
+
}, pre)).product,
|
|
147
|
+
);
|
|
130
148
|
assert.ok(
|
|
131
149
|
out.includes("answer alpha"),
|
|
132
150
|
`an ordinary further topic must still fuse in, got "${out}"`,
|
|
@@ -127,7 +127,16 @@ test("fuseAttention: 2 clusters, low breadth — dispersion alone is enough (gap
|
|
|
127
127
|
}),
|
|
128
128
|
guide,
|
|
129
129
|
};
|
|
130
|
-
|
|
130
|
+
// fusion consumes and returns the derivation STATE; these cases exercise the
|
|
131
|
+
// mechanism's own gates, so the state is the minimum one for them.
|
|
132
|
+
const out = dec(
|
|
133
|
+
(await fuseAttention(m, q, {
|
|
134
|
+
product: primary,
|
|
135
|
+
accounted: [],
|
|
136
|
+
remainder: [],
|
|
137
|
+
cost: 0,
|
|
138
|
+
}, pre)).product,
|
|
139
|
+
);
|
|
131
140
|
assert.equal(
|
|
132
141
|
out,
|
|
133
142
|
"answer alpha" + dec(primary),
|
|
@@ -154,7 +163,16 @@ test("fuseAttention: 1 cluster, dominant breadth — dominance alone is enough (
|
|
|
154
163
|
}),
|
|
155
164
|
guide,
|
|
156
165
|
};
|
|
157
|
-
|
|
166
|
+
// fusion consumes and returns the derivation STATE; these cases exercise the
|
|
167
|
+
// mechanism's own gates, so the state is the minimum one for them.
|
|
168
|
+
const out = dec(
|
|
169
|
+
(await fuseAttention(m, q, {
|
|
170
|
+
product: primary,
|
|
171
|
+
accounted: [],
|
|
172
|
+
remainder: [],
|
|
173
|
+
cost: 0,
|
|
174
|
+
}, pre)).product,
|
|
175
|
+
);
|
|
158
176
|
assert.equal(
|
|
159
177
|
out,
|
|
160
178
|
"answer alpha" + dec(primary),
|
|
@@ -180,7 +198,16 @@ test("fuseAttention: 1 cluster, low breadth — neither measure saves it (the li
|
|
|
180
198
|
}),
|
|
181
199
|
guide,
|
|
182
200
|
};
|
|
183
|
-
|
|
201
|
+
// fusion consumes and returns the derivation STATE; these cases exercise the
|
|
202
|
+
// mechanism's own gates, so the state is the minimum one for them.
|
|
203
|
+
const out = dec(
|
|
204
|
+
(await fuseAttention(m, q, {
|
|
205
|
+
product: primary,
|
|
206
|
+
accounted: [],
|
|
207
|
+
remainder: [],
|
|
208
|
+
cost: 0,
|
|
209
|
+
}, pre)).product,
|
|
210
|
+
);
|
|
184
211
|
assert.equal(
|
|
185
212
|
out,
|
|
186
213
|
dec(primary),
|
|
@@ -60,7 +60,17 @@ test("reason(): a forward hop landing on bytes already present in the query is r
|
|
|
60
60
|
// `reason()` reports the WHOLE extension (bytes + what it carried + how many
|
|
61
61
|
// steps) so a caller can price it; these assertions measure the bytes, and
|
|
62
62
|
// that is all this change touches — the law they pin is untouched.
|
|
63
|
-
|
|
63
|
+
// `reason()` now consumes and returns the derivation STATE; with no remainder
|
|
64
|
+
// the derivation is CLOSED, so the law admits any contained continuation —
|
|
65
|
+
// which is exactly what these two cases exercise. The assertions below are
|
|
66
|
+
// untouched.
|
|
67
|
+
const state = {
|
|
68
|
+
product: answer,
|
|
69
|
+
accounted: [],
|
|
70
|
+
remainder: [],
|
|
71
|
+
cost: 0,
|
|
72
|
+
};
|
|
73
|
+
const out = (await reason(m, query, state, new Set(), pre)).product;
|
|
64
74
|
assert.equal(
|
|
65
75
|
dec(out),
|
|
66
76
|
"bridge context",
|
|
@@ -88,7 +98,17 @@ test("reason(): an ordinary forward hop onto genuinely new content is unaffected
|
|
|
88
98
|
// `reason()` reports the WHOLE extension (bytes + what it carried + how many
|
|
89
99
|
// steps) so a caller can price it; these assertions measure the bytes, and
|
|
90
100
|
// that is all this change touches — the law they pin is untouched.
|
|
91
|
-
|
|
101
|
+
// `reason()` now consumes and returns the derivation STATE; with no remainder
|
|
102
|
+
// the derivation is CLOSED, so the law admits any contained continuation —
|
|
103
|
+
// which is exactly what these two cases exercise. The assertions below are
|
|
104
|
+
// untouched.
|
|
105
|
+
const state = {
|
|
106
|
+
product: answer,
|
|
107
|
+
accounted: [],
|
|
108
|
+
remainder: [],
|
|
109
|
+
cost: 0,
|
|
110
|
+
};
|
|
111
|
+
const out = (await reason(m, query, state, new Set(), pre)).product;
|
|
92
112
|
assert.equal(
|
|
93
113
|
dec(out),
|
|
94
114
|
"a wholly new fact never mentioned in any query",
|
|
@@ -419,7 +419,7 @@ test("12. the extension's cost obeys the ladder's own inequality", async () => {
|
|
|
419
419
|
await store.close();
|
|
420
420
|
|
|
421
421
|
const steps = c.reasonSteps ?? 0;
|
|
422
|
-
const carried = c.
|
|
422
|
+
const carried = c.reasonAccountedBytes ?? 0;
|
|
423
423
|
assert.ok(
|
|
424
424
|
steps >= 1,
|
|
425
425
|
`the extension must run for this to mean anything (got ${steps})`,
|
|
@@ -765,7 +765,7 @@ test("18. the remainder the pipeline decides on is visible", async () => {
|
|
|
765
765
|
};
|
|
766
766
|
|
|
767
767
|
// The pivoting query: the reasoner runs, so the remainder is non-empty
|
|
768
|
-
// (measured: reasonSteps 1,
|
|
768
|
+
// (measured: reasonSteps 1, reasonAccountedBytes 11).
|
|
769
769
|
const pivot = await run("What is the capital of France famous for");
|
|
770
770
|
assert.ok(
|
|
771
771
|
(pivot.steps ?? 0) >= 1,
|
|
@@ -949,8 +949,11 @@ test("21. the price's second term has one definition, and it is the complement",
|
|
|
949
949
|
// Since the value did NOT change, the lot is only pinned if something would
|
|
950
950
|
// fail when the helper sums the wrong thing — so this test uses an input where
|
|
951
951
|
// the two candidate readings DIFFER.
|
|
952
|
+
// PATH ONLY: the span algebra moved to the closure law's home
|
|
953
|
+
// (src/mind/derivation.ts) when the law was extracted; the assertions below
|
|
954
|
+
// are untouched and still pin the same contract.
|
|
952
955
|
const { unexplainedSpans, unaccountedBytes } = await import(
|
|
953
|
-
"../dist/src/mind/
|
|
956
|
+
"../dist/src/mind/derivation.js"
|
|
954
957
|
);
|
|
955
958
|
// Empty accounted ⇒ the whole query is unaccounted.
|
|
956
959
|
assert.equal(unaccountedBytes(unexplainedSpans(10, [])), 10);
|