@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,289 @@
|
|
|
1
|
+
// 135-one-law-any-producer.test.mjs — the law over REAL states, from different
|
|
2
|
+
// producers, with the same verdicts.
|
|
3
|
+
//
|
|
4
|
+
// THE CLAIM THE EXTRACTION HAS TO EARN, and the one test/133 and test/134 do not
|
|
5
|
+
// reach: composition means a product is consumable by ANY transition the law
|
|
6
|
+
// admits, and the law never asks who produced it. 133 shows that over synthetic
|
|
7
|
+
// states and one provenance label each; 134 shows it against the engine's own
|
|
8
|
+
// refusal on ONE fixture. This file takes the states the ENGINE actually
|
|
9
|
+
// produced, across five fixtures whose winners are DIFFERENT mechanisms, and asks
|
|
10
|
+
// the law about them:
|
|
11
|
+
//
|
|
12
|
+
// • the winners must really span several mechanisms — otherwise nothing here is
|
|
13
|
+
// exercised;
|
|
14
|
+
// • for every fixture, the state's own published fields (the remainder's spans
|
|
15
|
+
// and bytes, the supplied fixed point) decide every continuation, and the
|
|
16
|
+
// verdicts follow the law's clauses and nothing else;
|
|
17
|
+
// • and re-labelling a state with every other mechanism's provenance changes NO
|
|
18
|
+
// verdict — the invariant, over real states rather than synthetic ones.
|
|
19
|
+
//
|
|
20
|
+
// The observed behaviour is then read back through the same law: a fixture that
|
|
21
|
+
// was extended took a step the law admits, one that was stopped had a supplied
|
|
22
|
+
// fixed point (FIXED), and one that was never extended had no continuation
|
|
23
|
+
// offered.
|
|
24
|
+
|
|
25
|
+
import { test } from "node:test";
|
|
26
|
+
import assert from "node:assert/strict";
|
|
27
|
+
|
|
28
|
+
const law = await import("../dist/src/mind/derivation.js");
|
|
29
|
+
const { Mind, SQliteStore } = await import("../dist/src/index.js");
|
|
30
|
+
const enc = (s) => new TextEncoder().encode(s);
|
|
31
|
+
const dec = (b) => new TextDecoder().decode(b).replace(/\s+/g, " ").trim();
|
|
32
|
+
|
|
33
|
+
const FIVE = [
|
|
34
|
+
{
|
|
35
|
+
name: "join chain (test/120)",
|
|
36
|
+
pairs: (() => {
|
|
37
|
+
const F = [
|
|
38
|
+
"The director of Eva is Gustaf Molander.",
|
|
39
|
+
"The country of Gustaf Molander is Sweden.",
|
|
40
|
+
"The capital of Sweden is Stockholm.",
|
|
41
|
+
"The mayor of Stockholm is Karin Wanngard.",
|
|
42
|
+
"The party of Karin Wanngard is the Social Democrats.",
|
|
43
|
+
];
|
|
44
|
+
const K = [
|
|
45
|
+
["eva director", 0],
|
|
46
|
+
["gustaf molander country", 1],
|
|
47
|
+
["sweden capital", 2],
|
|
48
|
+
["stockholm mayor", 3],
|
|
49
|
+
["karin wanngard party", 4],
|
|
50
|
+
];
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const [key, i] of K) {
|
|
53
|
+
out.push([key.split(" ").slice(0, -1).join(" "), F[i]], [key, F[i]]);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
})(),
|
|
57
|
+
query: "eva director country capital mayor party",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "nested completion (test/98)",
|
|
61
|
+
pairs: [["a", "p q"], ["p", "r"], ["q", "s"], ["r s", "m n"]],
|
|
62
|
+
query: "a",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "the brake (test/110)",
|
|
66
|
+
pairs: [
|
|
67
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
68
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
69
|
+
["the Eiffel Tower", "the Eiffel Tower is in Paris"],
|
|
70
|
+
],
|
|
71
|
+
query: "What is the capital of France famous for",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "declared fixed point (test/100)",
|
|
75
|
+
pairs: [
|
|
76
|
+
["How do I compile hello.c?", "Run gcc hello.c"],
|
|
77
|
+
["How do I compile server.c?", "Run gcc server.c"],
|
|
78
|
+
["How do I compile parser.c?", "Run gcc parser.c"],
|
|
79
|
+
],
|
|
80
|
+
query: "How do I compile program.c?",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "two topics",
|
|
84
|
+
pairs: [
|
|
85
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
86
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
87
|
+
["2+2", "2+2 equals 4"],
|
|
88
|
+
["Gustaf Molander", "Gustaf Molander was a Swedish film director"],
|
|
89
|
+
],
|
|
90
|
+
query: "What is the capital of France? And what is 2 + 2?",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const WALK = new Set([
|
|
95
|
+
"pivotStep",
|
|
96
|
+
"absorbForward",
|
|
97
|
+
"pivotRefused",
|
|
98
|
+
"fuseAttention",
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
async function observe(f) {
|
|
102
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
103
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
104
|
+
await mind.ingest(f.pairs);
|
|
105
|
+
const steps = [];
|
|
106
|
+
const answer = String(await mind.respondText(f.query, (s) => steps.push(s)))
|
|
107
|
+
.trim();
|
|
108
|
+
// The winner's provenance is read from the POST-GROUNDING step, which every
|
|
109
|
+
// response emits. It is deliberately NOT read from the decision payload: that
|
|
110
|
+
// step is `narrowDecision` (which carries only a margin) when the market held
|
|
111
|
+
// one candidate, so reading it left the producer null on two of these five
|
|
112
|
+
// fixtures -- and a `Set` of producers then counted that null as a producer,
|
|
113
|
+
// which is how an earlier version of this file passed for the wrong reason.
|
|
114
|
+
const pg = steps.filter((s) => s.data && s.data.fixed !== undefined).pop()
|
|
115
|
+
?.data;
|
|
116
|
+
const counters = mind.lastCost?.counters ?? {};
|
|
117
|
+
const observed = {
|
|
118
|
+
name: f.name,
|
|
119
|
+
query: f.query,
|
|
120
|
+
answer,
|
|
121
|
+
provenance: pg?.provenance ?? null,
|
|
122
|
+
remainderSpans: pg?.remainderSpans ?? 0,
|
|
123
|
+
remainderBytes: pg?.remainderBytes ?? 0,
|
|
124
|
+
fixed: pg?.fixed === true,
|
|
125
|
+
walk: steps.map((s) => s.mechanism.at(-1)).filter((m) => WALK.has(m)),
|
|
126
|
+
pivotSteps: counters.pivotSteps ?? 0,
|
|
127
|
+
W: mind.space.maxGroup,
|
|
128
|
+
};
|
|
129
|
+
await store.close();
|
|
130
|
+
return observed;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
test("135. the law decides real states the same way, whoever produced them", async () => {
|
|
134
|
+
const seen = [];
|
|
135
|
+
for (const f of FIVE) seen.push(await observe(f));
|
|
136
|
+
|
|
137
|
+
// (1) THE FIXTURES MUST EXERCISE SEVERAL PRODUCERS, or nothing below is a test.
|
|
138
|
+
assert.ok(
|
|
139
|
+
seen.every((o) => o.provenance !== null),
|
|
140
|
+
`every fixture must report its winner: got ${
|
|
141
|
+
JSON.stringify(seen.map((o) => o.provenance))
|
|
142
|
+
}`,
|
|
143
|
+
);
|
|
144
|
+
const producers = new Set(seen.map((o) => o.provenance));
|
|
145
|
+
assert.ok(
|
|
146
|
+
producers.size >= 3,
|
|
147
|
+
`the fixtures' winners must span several mechanisms, got ${
|
|
148
|
+
JSON.stringify([...producers])
|
|
149
|
+
}`,
|
|
150
|
+
);
|
|
151
|
+
// (1b) AND THE BRANCHES BELOW MUST FIRE. A test whose every fixture fell
|
|
152
|
+
// through the same branch would pass while pinning one clause.
|
|
153
|
+
assert.ok(seen.some((o) => o.fixed), "one fixture must supply a fixed point");
|
|
154
|
+
assert.ok(
|
|
155
|
+
seen.some((o) => o.walk.includes("pivotRefused")),
|
|
156
|
+
"one fixture must show a refusal",
|
|
157
|
+
);
|
|
158
|
+
assert.ok(
|
|
159
|
+
seen.some((o) => o.remainderBytes > 0 && !o.fixed),
|
|
160
|
+
"one fixture must owe material and not be fixed",
|
|
161
|
+
);
|
|
162
|
+
assert.ok(
|
|
163
|
+
seen.some((o) => o.remainderBytes === 0 && !o.fixed),
|
|
164
|
+
"one fixture must be closed with no supplied fixed point",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// (2) THE STATE'S OWN FIELDS DECIDE, over the same battery for every fixture.
|
|
168
|
+
const battery = (o) => {
|
|
169
|
+
const query = enc(o.query);
|
|
170
|
+
const alien = enc("qqqqqqqqqqqqqqqq");
|
|
171
|
+
return {
|
|
172
|
+
carries: { product: query, contains: true, cost: 1 },
|
|
173
|
+
moves: { product: alien, contains: true, reaches: true, cost: 1 },
|
|
174
|
+
plain: { product: alien, contains: true, cost: 1 },
|
|
175
|
+
alien: { product: alien, contains: false, cost: 1 },
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const stateOf = (o) => {
|
|
179
|
+
// The remainder as the fixture's own published counts describe it: the
|
|
180
|
+
// grounding accounts for the query's head and owes its tail.
|
|
181
|
+
assert.ok(
|
|
182
|
+
o.remainderSpans <= 1,
|
|
183
|
+
`${o.name}: this construction assumes at most one owed span, got ${o.remainderSpans}`,
|
|
184
|
+
);
|
|
185
|
+
const remainder = o.remainderBytes === 0
|
|
186
|
+
? []
|
|
187
|
+
: [[o.query.length - o.remainderBytes, o.query.length]];
|
|
188
|
+
return {
|
|
189
|
+
product: enc(o.answer),
|
|
190
|
+
accounted: [],
|
|
191
|
+
remainder,
|
|
192
|
+
cost: 0,
|
|
193
|
+
fixed: o.fixed,
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
for (const o of seen) {
|
|
197
|
+
const query = enc(o.query);
|
|
198
|
+
const state = stateOf(o);
|
|
199
|
+
const conts = battery(o);
|
|
200
|
+
const closed = state.remainder.length === 0;
|
|
201
|
+
// the law's clauses, per fixture, on the fixture's own state
|
|
202
|
+
for (const [cn, t] of Object.entries(conts)) {
|
|
203
|
+
const verdict = law.admissible(state, t, query, o.W);
|
|
204
|
+
if (o.fixed) {
|
|
205
|
+
assert.equal(
|
|
206
|
+
verdict,
|
|
207
|
+
null,
|
|
208
|
+
`${o.name}: a supplied fixed point stops ${cn}`,
|
|
209
|
+
);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (cn === "alien") {
|
|
213
|
+
assert.equal(
|
|
214
|
+
verdict,
|
|
215
|
+
null,
|
|
216
|
+
`${o.name}: identity comes before progress`,
|
|
217
|
+
);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (closed) {
|
|
221
|
+
assert.notEqual(
|
|
222
|
+
verdict,
|
|
223
|
+
null,
|
|
224
|
+
`${o.name}: a closed state admits ${cn}`,
|
|
225
|
+
);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (cn === "carries" || cn === "moves") {
|
|
229
|
+
assert.notEqual(verdict, null, `${o.name}: ${cn} is progress`);
|
|
230
|
+
} else {
|
|
231
|
+
assert.equal(
|
|
232
|
+
verdict,
|
|
233
|
+
null,
|
|
234
|
+
`${o.name}: ${cn} is neither progress nor closed`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// (2b) COST IS NOT A TERM. The law says the ladder is the ORDER, not a
|
|
240
|
+
// clause; a state's verdict must not move when its cost does.
|
|
241
|
+
for (const c of [0, 1, 1e6]) {
|
|
242
|
+
for (const [cn, t] of Object.entries(conts)) {
|
|
243
|
+
assert.deepEqual(
|
|
244
|
+
law.admissible({ ...state, cost: c }, t, query, o.W),
|
|
245
|
+
law.admissible(state, t, query, o.W),
|
|
246
|
+
`${o.name} x ${cn}: the verdict moved with the cost (${c})`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// (3) RE-LABELLING CHANGES NOTHING, over real states this time.
|
|
252
|
+
for (const label of producers) {
|
|
253
|
+
const labelled = { ...state, provenance: label };
|
|
254
|
+
for (const [cn, t] of Object.entries(conts)) {
|
|
255
|
+
assert.deepEqual(
|
|
256
|
+
law.admissible(labelled, t, query, o.W),
|
|
257
|
+
law.admissible(state, t, query, o.W),
|
|
258
|
+
`${o.name} x ${cn}: the verdict changed under the label "${label}"`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// (4) AND THE ENGINE'S OBSERVED WALK IS THE LAW'S CONSEQUENCE.
|
|
265
|
+
for (const o of seen) {
|
|
266
|
+
if (o.fixed) {
|
|
267
|
+
assert.deepEqual(
|
|
268
|
+
o.walk,
|
|
269
|
+
[],
|
|
270
|
+
`${o.name}: a supplied fixed point must stop the walk (saw ${o.walk})`,
|
|
271
|
+
);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (o.walk.includes("pivotStep") || o.walk.includes("absorbForward")) {
|
|
275
|
+
assert.ok(
|
|
276
|
+
o.pivotSteps >= 1 || o.walk.includes("absorbForward"),
|
|
277
|
+
`${o.name}: a taken step must be one the meter counts`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
// a refusal, where it happened, is a step the law declined — and the fixture
|
|
281
|
+
// that has one is the one whose remainder is not empty
|
|
282
|
+
if (o.walk.includes("pivotRefused")) {
|
|
283
|
+
assert.ok(
|
|
284
|
+
o.remainderBytes > 0,
|
|
285
|
+
`${o.name}: a refusal implies there was material left to engage`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// 136-the-two-named-limits.test.mjs — the two sites the extraction LEAVES ALONE,
|
|
2
|
+
// pinned so that changing them requires the measurement the report says is
|
|
3
|
+
// missing.
|
|
4
|
+
//
|
|
5
|
+
// The investigation found two places where the architecture's own reading and
|
|
6
|
+
// the code's reading differ, and the plan's rule is: register, prove, decide
|
|
7
|
+
// whether it is a bug, decide whether it is a consequence, and ONLY THEN propose
|
|
8
|
+
// a change. Neither has been proven a bug, so neither was changed -- and these
|
|
9
|
+
// two assertions exist so that the next person cannot change them by accident,
|
|
10
|
+
// believing they are tidying up:
|
|
11
|
+
//
|
|
12
|
+
// 136.1 THE FUSE GATE KEEPS ITS OWN READING. It decides with the TOTAL
|
|
13
|
+
// unaccounted bytes against one quantum, while the state's `remainder` is
|
|
14
|
+
// per-span by construction. The two readings AGREE unless every gap is below
|
|
15
|
+
// one quantum and their sum is at or above it -- and that construction is what
|
|
16
|
+
// nobody has built, so switching the gate to the state's own `closed` would
|
|
17
|
+
// change an answer no one has measured. The assertion therefore pins both
|
|
18
|
+
// halves: that the two readings DO differ on that construction (so the reason
|
|
19
|
+
// lives in the test and not only in a comment), and that the gate still uses
|
|
20
|
+
// the total.
|
|
21
|
+
//
|
|
22
|
+
// 136.2 THE EXTENSION IS NOT PRICED. The reasoner reports what its steps cost
|
|
23
|
+
// and what they accounted for, and the caller never folds that into the
|
|
24
|
+
// decided candidate: the candidate's weight is the ladder formula alone. This
|
|
25
|
+
// is a DIVERGENCE between the documented intent and the implementation, on
|
|
26
|
+
// purpose un-fixed: pricing it would change which grounding wins, and that
|
|
27
|
+
// comparison has not been measured. The assertion pins the current arithmetic
|
|
28
|
+
// on a fixture where the walk really takes a step (guarded), so folding the
|
|
29
|
+
// cost in breaks it and forces the measurement.
|
|
30
|
+
|
|
31
|
+
import { test } from "node:test";
|
|
32
|
+
import assert from "node:assert/strict";
|
|
33
|
+
import { readFile } from "node:fs/promises";
|
|
34
|
+
import { dirname, join } from "node:path";
|
|
35
|
+
import { fileURLToPath } from "node:url";
|
|
36
|
+
|
|
37
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
const law = await import("../dist/src/mind/derivation.js");
|
|
39
|
+
const { STEP, PASS } = await import("../dist/src/mind/graph-search.js");
|
|
40
|
+
const { Mind, SQliteStore } = await import("../dist/src/index.js");
|
|
41
|
+
|
|
42
|
+
test("136.1 the fuse gate asks the LAW — one condition, one home, one less sum", async () => {
|
|
43
|
+
// THE DISCRIMINATING CONSTRUCTION, from the law's own functions: a query whose
|
|
44
|
+
// accounted spans leave only sub-quantum gaps, whose SUM is at or above one
|
|
45
|
+
// quantum. The two READINGS differ here — that is why the register kept them
|
|
46
|
+
// apart — and the question is whether the difference can reach a real response.
|
|
47
|
+
const W = 4;
|
|
48
|
+
const len = 12;
|
|
49
|
+
const explained = [[0, 3], [6, 9]];
|
|
50
|
+
const gaps = law.unexplainedSpans(len, explained);
|
|
51
|
+
assert.deepEqual(
|
|
52
|
+
gaps,
|
|
53
|
+
[[3, 6], [9, 12]],
|
|
54
|
+
"the construction must leave exactly the two sub-quantum gaps",
|
|
55
|
+
);
|
|
56
|
+
const total = law.unaccountedBytes(gaps);
|
|
57
|
+
const perSpan = law.remainderOf(len, explained, W);
|
|
58
|
+
assert.ok(
|
|
59
|
+
total >= W,
|
|
60
|
+
`on the raw spans the total reading opens: ${total} >= ${W}`,
|
|
61
|
+
);
|
|
62
|
+
assert.deepEqual(
|
|
63
|
+
perSpan,
|
|
64
|
+
[],
|
|
65
|
+
"and the per-span reading closes — the difference is real at this level",
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// AND IT CANNOT REACH A RESPONSE, which 136.3 measures: the ACCOUNTING applies
|
|
69
|
+
// the same W floor, so a sub-quantum gap never survives into `explained`. The
|
|
70
|
+
// gate therefore asks the law, and that is an OPTIMISATION and not a tidy-up —
|
|
71
|
+
// `remainderOf` is already computed for the state, while the total the gate used
|
|
72
|
+
// to sum (`unaccounted(explained)`) was one more pass over the spans per
|
|
73
|
+
// response, for the same answer.
|
|
74
|
+
const src = await readFile(
|
|
75
|
+
join(here, "..", "src", "mind", "pipeline.ts"),
|
|
76
|
+
"utf8",
|
|
77
|
+
);
|
|
78
|
+
assert.match(
|
|
79
|
+
src,
|
|
80
|
+
/const fused = closed\(state\)/,
|
|
81
|
+
"the fuse gate must ask the law's `closed(state)` — the condition the state " +
|
|
82
|
+
"already carries. If it went back to summing `unaccounted(explained)`, that " +
|
|
83
|
+
"is a second reading of one condition AND an extra sum per response: 136.3 " +
|
|
84
|
+
"measures the equivalence, so re-read it before restoring the total",
|
|
85
|
+
);
|
|
86
|
+
assert.ok(
|
|
87
|
+
!/const remainder = unaccounted\(explained\);/.test(src),
|
|
88
|
+
"the total-bytes sum is back: the gate has two readings of one condition again",
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("136.2 the extension is not priced — and it pays for itself", async () => {
|
|
93
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
94
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
95
|
+
await mind.ingest([
|
|
96
|
+
["What is the capital of France", "The capital of France is Paris"],
|
|
97
|
+
["Paris", "Paris is famous for the Eiffel Tower"],
|
|
98
|
+
["the Eiffel Tower", "the Eiffel Tower is in Paris"],
|
|
99
|
+
]);
|
|
100
|
+
const steps = [];
|
|
101
|
+
await mind.respondText(
|
|
102
|
+
"What is the capital of France famous for",
|
|
103
|
+
(s) => steps.push(s),
|
|
104
|
+
);
|
|
105
|
+
const counters = mind.lastCost?.counters ?? {};
|
|
106
|
+
const dg = steps.filter((s) => Array.isArray(s.data?.candidates)).pop()?.data;
|
|
107
|
+
const winner = dg?.candidates.find((c) => c.decided);
|
|
108
|
+
await store.close();
|
|
109
|
+
|
|
110
|
+
// THE VACUITY GUARD: the walk must really have taken a step, or the equality
|
|
111
|
+
// below would hold for a response that never extended anything.
|
|
112
|
+
assert.ok(
|
|
113
|
+
(counters.reasonSteps ?? 0) >= 1,
|
|
114
|
+
`the fixture must take at least one extension step, got ${counters.reasonSteps}`,
|
|
115
|
+
);
|
|
116
|
+
assert.ok(winner, "the response must publish its decided candidate");
|
|
117
|
+
// AND THE EXTENSION PAYS FOR ITSELF, measured: the walk's steps are answered by
|
|
118
|
+
// the material its witnesses carried, by orders of magnitude (this fixture takes
|
|
119
|
+
// ONE extension: 1 step against 11 bytes carried — 1 against 11000 in the one
|
|
120
|
+
// currency). That is why no price DECISION is added for the extension: its
|
|
121
|
+
// warrant is already the closure law's (a step must engage the remainder or
|
|
122
|
+
// move), its cost is counted and reported (`reasonSteps`), and a second,
|
|
123
|
+
// price-shaped admission would be a second home for the same decision — one that
|
|
124
|
+
// would refuse the IDENTITY steps the law admits, which is the documented
|
|
125
|
+
// failure of the (c) remedy (material lost: "hot" for "cold hot").
|
|
126
|
+
assert.ok(
|
|
127
|
+
(counters.reasonAccountedBytes ?? 0) * PASS >
|
|
128
|
+
(counters.reasonSteps ?? 0) * STEP,
|
|
129
|
+
"the extension is carrying less than its steps are worth — measured 11 bytes " +
|
|
130
|
+
"against 1 step here. If this ever fails, the absent price comparison HAS " +
|
|
131
|
+
"teeth and the register's GAP 2 is real: measure what pricing the extension " +
|
|
132
|
+
"does to the suite before touching this",
|
|
133
|
+
);
|
|
134
|
+
assert.equal(
|
|
135
|
+
winner.weight,
|
|
136
|
+
STEP + PASS * winner.unexplainedBytes,
|
|
137
|
+
"the winning candidate's weight is now something other than the ladder " +
|
|
138
|
+
"formula on its own accounting — most likely the extension's cost was " +
|
|
139
|
+
"folded in. That is a behaviour change: measure what pricing the " +
|
|
140
|
+
"extension does to the suite's answers and counters, then update this test",
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("136.3 the gate's total and the law's per-span reading agree — measured, not argued", async () => {
|
|
145
|
+
// GAP 1 of the §22 register. The two readings WOULD diverge if a gap below one
|
|
146
|
+
// quantum could survive into the accounting — a total >= W while every gap < W —
|
|
147
|
+
// and the register kept that as an unmeasured limit. It is measured here and it
|
|
148
|
+
// does not happen: on every construction below the response either reports NO
|
|
149
|
+
// remainder or one whose gaps are each >= W, because the ACCOUNTING applies the
|
|
150
|
+
// same W floor the gate does. Sub-quantum material between recognised spans sits
|
|
151
|
+
// INSIDE the composed spans, so it never becomes a gap; and material the cover
|
|
152
|
+
// cannot compose stays unrecognised in runs the recognition's own floor already
|
|
153
|
+
// treats as non-evidence. The gate's total reading is therefore not a second,
|
|
154
|
+
// laxer condition: it is the same condition, which is what makes 136.1's source
|
|
155
|
+
// pin safe rather than a hazard.
|
|
156
|
+
const { Mind, SQliteStore } = await import("../dist/src/index.js");
|
|
157
|
+
const corpus = [
|
|
158
|
+
["What is 2 + 2?", "What is 2 + 2? 4"],
|
|
159
|
+
["What is 3 + 3?", "What is 3 + 3? 6"],
|
|
160
|
+
["Paris", "Paris is the capital of France"],
|
|
161
|
+
["Hamlet", "Hamlet was written by Shakespeare"],
|
|
162
|
+
["Romeo", "Romeo loves Juliet"],
|
|
163
|
+
["Macbeth", "Macbeth is a Scottish play"],
|
|
164
|
+
];
|
|
165
|
+
const queries = [
|
|
166
|
+
"Paris,Hamlet,Romeo,Macbeth", // four 1-byte gaps: total 4 = W
|
|
167
|
+
"Paris; Hamlet; Romeo; Macbeth", // four 2-byte gaps: total 8
|
|
168
|
+
"Paris - Hamlet - Romeo - Macbeth", // four 3-byte gaps: total 12
|
|
169
|
+
"2+2, 3+3, 4+4", // sub-quantum separators inside a composition
|
|
170
|
+
"Paris and Hamlet and Romeo and Macbeth", // 5-byte gaps: a genuine remainder
|
|
171
|
+
];
|
|
172
|
+
const store = new SQliteStore({ path: ":memory:" });
|
|
173
|
+
const mind = new Mind({ seed: 7, store, profile: true });
|
|
174
|
+
await mind.ingest(corpus);
|
|
175
|
+
const W = mind.space.maxGroup;
|
|
176
|
+
let closed = 0;
|
|
177
|
+
let open = 0;
|
|
178
|
+
for (const q of queries) {
|
|
179
|
+
const steps = [];
|
|
180
|
+
await mind.respondText(q, (s) => steps.push(s));
|
|
181
|
+
const pg = steps.filter((s) => s.data && s.data.fixed !== undefined).pop()
|
|
182
|
+
?.data;
|
|
183
|
+
const spans = pg?.remainderSpans ?? 0;
|
|
184
|
+
const bytes = pg?.remainderBytes ?? 0;
|
|
185
|
+
assert.equal(
|
|
186
|
+
bytes >= W,
|
|
187
|
+
spans > 0,
|
|
188
|
+
`${JSON.stringify(q)}: the gate's total reading says ${
|
|
189
|
+
bytes >= W ? "open" : "closed"
|
|
190
|
+
} ` +
|
|
191
|
+
`(${bytes} bytes vs W=${W}) while the law's per-span reading says ` +
|
|
192
|
+
`${
|
|
193
|
+
spans > 0 ? "open" : "closed"
|
|
194
|
+
} (${spans} spans) — they diverged, so the ` +
|
|
195
|
+
`accounting no longer applies the W floor and the gate IS a laxer second reading`,
|
|
196
|
+
);
|
|
197
|
+
if (bytes > 0) open++;
|
|
198
|
+
else closed++;
|
|
199
|
+
}
|
|
200
|
+
assert.ok(
|
|
201
|
+
open > 0 && closed > 0,
|
|
202
|
+
`the set must exercise both sides, got open=${open} closed=${closed}`,
|
|
203
|
+
);
|
|
204
|
+
await store.close();
|
|
205
|
+
});
|