@hviana/sema 0.8.2 → 0.8.5

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 (126) hide show
  1. package/AGENTS.md +38 -37
  2. package/README.md +17 -38
  3. package/TRADEMARKS.md +0 -1
  4. package/dist/example/demo.js +85 -34
  5. package/dist/src/config.d.ts +11 -0
  6. package/dist/src/config.js +2 -0
  7. package/dist/src/geometry.d.ts +21 -10
  8. package/dist/src/geometry.js +21 -12
  9. package/dist/src/meter.d.ts +62 -0
  10. package/dist/src/meter.js +62 -0
  11. package/dist/src/mind/articulation.js +1 -1
  12. package/dist/src/mind/attention.d.ts +4 -0
  13. package/dist/src/mind/attention.js +167 -17
  14. package/dist/src/mind/canonical.d.ts +16 -0
  15. package/dist/src/mind/canonical.js +41 -0
  16. package/dist/src/mind/derivation.d.ts +201 -0
  17. package/dist/src/mind/derivation.js +327 -0
  18. package/dist/src/mind/graph-search.d.ts +2 -1
  19. package/dist/src/mind/graph-search.js +70 -29
  20. package/dist/src/mind/match.d.ts +3 -1
  21. package/dist/src/mind/match.js +7 -3
  22. package/dist/src/mind/mechanisms/alu.js +0 -2
  23. package/dist/src/mind/mechanisms/cast.d.ts +1 -5
  24. package/dist/src/mind/mechanisms/cast.js +16 -19
  25. package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
  26. package/dist/src/mind/mechanisms/confluence.js +27 -9
  27. package/dist/src/mind/mechanisms/cover.js +17 -20
  28. package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
  29. package/dist/src/mind/mechanisms/extraction.js +13 -8
  30. package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
  31. package/dist/src/mind/mechanisms/recall.d.ts +0 -1
  32. package/dist/src/mind/mechanisms/recall.js +40 -13
  33. package/dist/src/mind/mechanisms/reference.js +3 -4
  34. package/dist/src/mind/mind.d.ts +4 -2
  35. package/dist/src/mind/mind.js +5 -4
  36. package/dist/src/mind/pipeline-mechanism.d.ts +7 -3
  37. package/dist/src/mind/pipeline.js +136 -44
  38. package/dist/src/mind/primitives.js +9 -1
  39. package/dist/src/mind/rationale.d.ts +21 -5
  40. package/dist/src/mind/rationale.js +16 -21
  41. package/dist/src/mind/reasoning.d.ts +12 -20
  42. package/dist/src/mind/reasoning.js +190 -106
  43. package/dist/src/mind/recognition.js +4 -8
  44. package/dist/src/mind/resonance.js +20 -1
  45. package/dist/src/mind/trace.js +1 -0
  46. package/dist/src/mind/traverse.js +6 -2
  47. package/dist/src/mind/types.d.ts +36 -13
  48. package/dist/src/mind/types.js +6 -3
  49. package/docs/INDEX.md +23 -24
  50. package/docs/INVARIANTS.md +16 -17
  51. package/docs/architecture/bounded-reads.md +5 -5
  52. package/docs/architecture/closure.md +65 -0
  53. package/docs/architecture/commonality.md +29 -20
  54. package/docs/architecture/cost-model.md +7 -7
  55. package/docs/architecture/determinism.md +7 -7
  56. package/docs/architecture/exact-vs-approximate.md +4 -4
  57. package/docs/architecture/factored-machinery.md +14 -14
  58. package/docs/architecture/match-project.md +2 -3
  59. package/docs/architecture/mechanism-market.md +16 -16
  60. package/docs/architecture/meter.md +10 -11
  61. package/docs/architecture/store.md +4 -4
  62. package/docs/architecture/thresholds.md +1 -1
  63. package/docs/failures/tempting-but-wrong.md +14 -5
  64. package/docs/harness/gates.md +7 -7
  65. package/docs/mechanisms/cast.md +2 -2
  66. package/docs/mechanisms/cover.md +4 -5
  67. package/docs/mechanisms/extraction.md +7 -7
  68. package/docs/mechanisms/recall.md +8 -9
  69. package/example/demo.ts +90 -37
  70. package/jsr.json +1 -1
  71. package/package.json +1 -1
  72. package/src/alu/README.md +11 -12
  73. package/src/config.ts +13 -0
  74. package/src/geometry.ts +21 -13
  75. package/src/meter.ts +62 -0
  76. package/src/mind/articulation.ts +0 -1
  77. package/src/mind/attention.ts +169 -17
  78. package/src/mind/canonical.ts +43 -0
  79. package/src/mind/derivation.ts +473 -0
  80. package/src/mind/graph-search.ts +76 -34
  81. package/src/mind/match.ts +7 -3
  82. package/src/mind/mechanisms/alu.ts +0 -2
  83. package/src/mind/mechanisms/cast.ts +20 -22
  84. package/src/mind/mechanisms/confluence.ts +27 -13
  85. package/src/mind/mechanisms/cover.ts +17 -20
  86. package/src/mind/mechanisms/extraction.ts +13 -9
  87. package/src/mind/mechanisms/prefix-completion.ts +0 -1
  88. package/src/mind/mechanisms/recall.ts +39 -13
  89. package/src/mind/mechanisms/reference.ts +2 -3
  90. package/src/mind/mind.ts +6 -4
  91. package/src/mind/pipeline-mechanism.ts +7 -3
  92. package/src/mind/pipeline.ts +160 -52
  93. package/src/mind/primitives.ts +9 -1
  94. package/src/mind/rationale.ts +27 -23
  95. package/src/mind/reasoning.ts +227 -120
  96. package/src/mind/recognition.ts +4 -8
  97. package/src/mind/resonance.ts +19 -1
  98. package/src/mind/trace.ts +1 -0
  99. package/src/mind/traverse.ts +7 -5
  100. package/src/mind/types.ts +41 -15
  101. package/test/105-derive-through-reports-its-refusal.test.mjs +24 -0
  102. package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
  103. package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
  104. package/test/120-composition-is-consequence.test.mjs +132 -0
  105. package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
  106. package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
  107. package/test/123-the-paired-formulas-agree.test.mjs +90 -0
  108. package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
  109. package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
  110. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
  111. package/test/129-the-trace-payload-shape.test.mjs +164 -0
  112. package/test/133-the-decision-point-renders-the-state.test.mjs +204 -0
  113. package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
  114. package/test/135-one-law-any-producer.test.mjs +289 -0
  115. package/test/136-the-two-named-limits.test.mjs +205 -0
  116. package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
  117. package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
  118. package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
  119. package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
  120. package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
  121. package/test/32-confluence.test.mjs +68 -0
  122. package/test/36-already-answered-fusion.test.mjs +20 -2
  123. package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
  124. package/test/38-reason-restate-guard.test.mjs +28 -2
  125. package/test/43-cast-analog-seat.test.mjs +10 -0
  126. package/test/55-cost-meter.test.mjs +862 -0
@@ -171,3 +171,71 @@ test("E1 — the intersection is seed-independent (it is exact, not resonant)",
171
171
  await m.store.close();
172
172
  }
173
173
  });
174
+
175
+ // ═══════════════════════════════════════════════════════════════════════════
176
+ // Section F — the early-exit budget: live, but never pruning
177
+ // ═══════════════════════════════════════════════════════════════════════════
178
+
179
+ test("F1 — the two streams are found well inside the early-exit's budget", async () => {
180
+ // The mechanism scans the climb's ranked anchors with two cuts: `ranked.slice(0,
181
+ // pre.k)` and an early exit after `2W` anchors that returns null when fewer than
182
+ // two constraint streams exist. Both are spec-legal (a capacity, and a formula
183
+ // over W — config.ts's default `maxGroup` is 4) but neither was DERIVED; what
184
+ // makes them legitimate is that they do not prune the case they exist to protect.
185
+ //
186
+ // MEASURED on this corpus: the two streams appear at ranks 1 and 4 against the
187
+ // budget of 8, while `ranked` is 9 — the cut IS live (it would have returned
188
+ // null at the 8th anchor) and it does NOT prune. The last assertion is the
189
+ // anti-vacuity guard: if `ranked` ever fell to 8 or below, this test would be
190
+ // pinning nothing at all.
191
+ const W = 4; // the engine's default quantum: config.ts, geometry.maxGroup
192
+ const m = new Mind({
193
+ seed: 7,
194
+ store: new SQliteStore({ path: ":memory:" }),
195
+ profile: true,
196
+ });
197
+ await m.ingest(materials);
198
+ const steps = [];
199
+ await m.respondText(
200
+ "Which material is translucent and featherlight?",
201
+ (s) => steps.push(s),
202
+ );
203
+ await m.store.close();
204
+
205
+ const findAnchors = (o, depth = 0) => {
206
+ if (o === null || typeof o !== "object" || depth > 5) return null;
207
+ if (Array.isArray(o.anchors) && o.anchors.length) return o;
208
+ for (const v of Object.values(o)) {
209
+ const r = findAnchors(v, depth + 1);
210
+ if (r) return r;
211
+ }
212
+ return null;
213
+ };
214
+ const step = steps.find((s) => findAnchors(s.data ?? s) !== null);
215
+ const td = step ? findAnchors(step.data ?? step) : null;
216
+ assert.ok(td, "the climb must report its anchors");
217
+
218
+ const meet = steps.find((s) =>
219
+ (s.mechanism ?? []).join("/").includes("intersectEvidence")
220
+ );
221
+ assert.ok(meet, "confluence must ENTER on a conjunctive query");
222
+
223
+ const ranks = (meet.inputs ?? [])
224
+ .filter((i) => typeof i.node === "number")
225
+ .map((i) => (td.anchors.find((a) => a.anchor === i.node) ?? {}).rank);
226
+ assert.ok(
227
+ ranks.length >= 2,
228
+ `the meet needs two streams (got ${ranks.length})`,
229
+ );
230
+ assert.ok(
231
+ Math.max(...ranks) < 2 * W,
232
+ `the streams must appear inside the budget (ranks ${
233
+ ranks.join(",")
234
+ } vs 2W=${2 * W})`,
235
+ );
236
+ assert.ok(
237
+ td.anchors.length > 2 * W,
238
+ `and the cut must be live on this fixture, else this test pins nothing ` +
239
+ `(ranked=${td.anchors.length}, 2W=${2 * W})`,
240
+ );
241
+ });
@@ -94,7 +94,16 @@ test("fuseAttention: a root whose continuation is already answered in the query
94
94
  }),
95
95
  guide,
96
96
  };
97
- const out = dec(await fuseAttention(m, q, primary, pre));
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
- const out = dec(await fuseAttention(m, q, primary, pre));
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
- const out = dec(await fuseAttention(m, q, primary, pre));
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
- const out = dec(await fuseAttention(m, q, primary, pre));
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
- const out = dec(await fuseAttention(m, q, primary, pre));
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),
@@ -57,7 +57,20 @@ test("reason(): a forward hop landing on bytes already present in the query is r
57
57
  queryResolved: resolve(m, query),
58
58
  };
59
59
 
60
- const out = await reason(m, query, answer, new Set(), pre);
60
+ // `reason()` reports the WHOLE extension (bytes + what it carried + how many
61
+ // steps) so a caller can price it; these assertions measure the bytes, and
62
+ // that is all this change touches — the law they pin is untouched.
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;
61
74
  assert.equal(
62
75
  dec(out),
63
76
  "bridge context",
@@ -82,7 +95,20 @@ test("reason(): an ordinary forward hop onto genuinely new content is unaffected
82
95
  queryResolved: resolve(m, query),
83
96
  };
84
97
 
85
- const out = await reason(m, query, answer, new Set(), pre);
98
+ // `reason()` reports the WHOLE extension (bytes + what it carried + how many
99
+ // steps) so a caller can price it; these assertions measure the bytes, and
100
+ // that is all this change touches — the law they pin is untouched.
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;
86
112
  assert.equal(
87
113
  dec(out),
88
114
  "a wholly new fact never mentioned in any query",
@@ -65,6 +65,7 @@ test("CAST comparison: a nextOf-descendant analog is seated by its own bytes, no
65
65
  const dominant = {
66
66
  anchor: franceId,
67
67
  vote: 100,
68
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
68
69
  ctx: enc("What is the capital of France?"),
69
70
  runs: [{ qs: 0, qe: 30, cs: 0, weight: 1 }],
70
71
  };
@@ -75,6 +76,7 @@ test("CAST comparison: a nextOf-descendant analog is seated by its own bytes, no
75
76
  const spainPoint = {
76
77
  anchor: spainAnalogSrcId,
77
78
  vote: 50,
79
+ idfVote: 50, // the fixture builds an Attention: the field has to exist
78
80
  ctx: enc("some other prompt"),
79
81
  runs: [{ qs: 32, qe: 62, cs: 0, weight: 1 }],
80
82
  };
@@ -85,6 +87,7 @@ test("CAST comparison: a nextOf-descendant analog is seated by its own bytes, no
85
87
  {
86
88
  anchor: franceId,
87
89
  vote: 100,
90
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
88
91
  start: 0,
89
92
  end: 30,
90
93
  breadth: 1,
@@ -95,6 +98,7 @@ test("CAST comparison: a nextOf-descendant analog is seated by its own bytes, no
95
98
  {
96
99
  anchor: franceId,
97
100
  vote: 100,
101
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
98
102
  start: 0,
99
103
  end: 30,
100
104
  breadth: 1,
@@ -103,6 +107,7 @@ test("CAST comparison: a nextOf-descendant analog is seated by its own bytes, no
103
107
  {
104
108
  anchor: spainAnalogSrcId,
105
109
  vote: 50,
110
+ idfVote: 50, // the fixture builds an Attention: the field has to exist
106
111
  start: 32,
107
112
  end: 62,
108
113
  breadth: 0.5,
@@ -174,6 +179,7 @@ test("CAST comparison: a DIRECTLY aligned analog is never re-projected past its
174
179
  const dominant = {
175
180
  anchor: franceId,
176
181
  vote: 100,
182
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
177
183
  ctx: enc("What is the capital of France?"),
178
184
  runs: [{ qs: 0, qe: 30, cs: 0, weight: 1 }],
179
185
  };
@@ -182,6 +188,7 @@ test("CAST comparison: a DIRECTLY aligned analog is never re-projected past its
182
188
  const japanPoint = {
183
189
  anchor: japanId,
184
190
  vote: 50,
191
+ idfVote: 50, // the fixture builds an Attention: the field has to exist
185
192
  ctx: enc("What is the capital of Japan? Tokyo is the capital of Japan."),
186
193
  runs: [{ qs: 32, qe: 62, cs: 0, weight: 1 }],
187
194
  };
@@ -192,6 +199,7 @@ test("CAST comparison: a DIRECTLY aligned analog is never re-projected past its
192
199
  {
193
200
  anchor: franceId,
194
201
  vote: 100,
202
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
195
203
  start: 0,
196
204
  end: 30,
197
205
  breadth: 1,
@@ -202,6 +210,7 @@ test("CAST comparison: a DIRECTLY aligned analog is never re-projected past its
202
210
  {
203
211
  anchor: franceId,
204
212
  vote: 100,
213
+ idfVote: 100, // the fixture builds an Attention: the field has to exist
205
214
  start: 0,
206
215
  end: 30,
207
216
  breadth: 1,
@@ -210,6 +219,7 @@ test("CAST comparison: a DIRECTLY aligned analog is never re-projected past its
210
219
  {
211
220
  anchor: japanId,
212
221
  vote: 50,
222
+ idfVote: 50, // the fixture builds an Attention: the field has to exist
213
223
  start: 32,
214
224
  end: 62,
215
225
  breadth: 0.5,