@hviana/sema 0.9.0 → 0.9.2

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 (118) hide show
  1. package/AGENTS.md +7 -7
  2. package/dist/src/alu/src/index.d.ts +1 -1
  3. package/dist/src/alu/src/index.js +1 -1
  4. package/dist/src/alu/src/parser.js +2 -6
  5. package/dist/src/alu/src/resonance.d.ts +13 -0
  6. package/dist/src/alu/src/resonance.js +41 -0
  7. package/dist/src/alu/test/alu.test.js +39 -0
  8. package/dist/src/bytes.d.ts +6 -2
  9. package/dist/src/bytes.js +10 -4
  10. package/dist/src/canon.js +44 -0
  11. package/dist/src/geometry.d.ts +19 -1
  12. package/dist/src/geometry.js +125 -141
  13. package/dist/src/meter.d.ts +33 -0
  14. package/dist/src/meter.js +34 -1
  15. package/dist/src/mind/articulation.js +14 -1
  16. package/dist/src/mind/attention.d.ts +12 -0
  17. package/dist/src/mind/attention.js +44 -16
  18. package/dist/src/mind/bridge.js +3 -3
  19. package/dist/src/mind/derivation.d.ts +40 -0
  20. package/dist/src/mind/derivation.js +34 -0
  21. package/dist/src/mind/evidence.d.ts +24 -0
  22. package/dist/src/mind/evidence.js +90 -0
  23. package/dist/src/mind/graph-search.d.ts +89 -15
  24. package/dist/src/mind/graph-search.js +345 -174
  25. package/dist/src/mind/learning.js +1 -1
  26. package/dist/src/mind/mechanisms/cover.d.ts +19 -3
  27. package/dist/src/mind/mechanisms/cover.js +142 -61
  28. package/dist/src/mind/mechanisms/recall.js +10 -3
  29. package/dist/src/mind/mind.d.ts +6 -0
  30. package/dist/src/mind/mind.js +5 -2
  31. package/dist/src/mind/pipeline.d.ts +5 -1
  32. package/dist/src/mind/pipeline.js +220 -90
  33. package/dist/src/mind/primitives.d.ts +25 -5
  34. package/dist/src/mind/primitives.js +107 -44
  35. package/dist/src/mind/reasoning.d.ts +18 -4
  36. package/dist/src/mind/reasoning.js +487 -328
  37. package/dist/src/mind/recognition.js +29 -13
  38. package/dist/src/mind/resonance.js +1 -11
  39. package/dist/src/mind/traverse.d.ts +45 -5
  40. package/dist/src/mind/traverse.js +285 -8
  41. package/dist/src/mind/types.d.ts +16 -1
  42. package/dist/src/store-sqlite.d.ts +25 -0
  43. package/dist/src/store-sqlite.js +89 -1
  44. package/dist/src/store.d.ts +48 -4
  45. package/dist/src/store.js +86 -6
  46. package/docs/INDEX.md +20 -19
  47. package/docs/INVARIANTS.md +17 -16
  48. package/docs/architecture/bounded-reads.md +1 -1
  49. package/docs/architecture/caches.md +5 -4
  50. package/docs/architecture/closure.md +45 -5
  51. package/docs/architecture/cost-model.md +16 -0
  52. package/docs/architecture/evidence.md +113 -0
  53. package/docs/architecture/exact-vs-approximate.md +10 -9
  54. package/docs/architecture/factored-machinery.md +14 -13
  55. package/docs/architecture/fold-contract.md +51 -1
  56. package/docs/architecture/mechanism-market.md +21 -0
  57. package/docs/architecture/memoization.md +3 -3
  58. package/docs/architecture/meter.md +2 -1
  59. package/docs/architecture/saturation.md +12 -0
  60. package/docs/architecture/store.md +25 -2
  61. package/docs/failures/tempting-but-wrong.md +13 -2
  62. package/docs/harness/gates.md +12 -10
  63. package/docs/mechanisms/cover.md +23 -6
  64. package/jsr.json +1 -1
  65. package/package.json +1 -1
  66. package/src/alu/README.md +10 -2
  67. package/src/alu/src/index.ts +1 -0
  68. package/src/alu/src/parser.ts +6 -6
  69. package/src/alu/src/resonance.ts +42 -0
  70. package/src/alu/test/alu.test.ts +40 -0
  71. package/src/bytes.ts +13 -3
  72. package/src/canon.ts +40 -0
  73. package/src/geometry.ts +183 -154
  74. package/src/meter.ts +34 -1
  75. package/src/mind/articulation.ts +14 -2
  76. package/src/mind/attention.ts +47 -25
  77. package/src/mind/bridge.ts +3 -3
  78. package/src/mind/derivation.ts +77 -0
  79. package/src/mind/evidence.ts +107 -0
  80. package/src/mind/graph-search.ts +449 -221
  81. package/src/mind/learning.ts +1 -7
  82. package/src/mind/match.ts +1 -2
  83. package/src/mind/mechanisms/cast.ts +1 -2
  84. package/src/mind/mechanisms/cover.ts +207 -87
  85. package/src/mind/mechanisms/extraction.ts +1 -2
  86. package/src/mind/mechanisms/prefix-completion.ts +1 -1
  87. package/src/mind/mechanisms/recall.ts +17 -5
  88. package/src/mind/mechanisms/reference.ts +1 -1
  89. package/src/mind/mind.ts +9 -30
  90. package/src/mind/pipeline.ts +263 -104
  91. package/src/mind/primitives.ts +119 -43
  92. package/src/mind/reasoning.ts +611 -419
  93. package/src/mind/recognition.ts +24 -9
  94. package/src/mind/resonance.ts +2 -16
  95. package/src/mind/trace.ts +1 -1
  96. package/src/mind/traverse.ts +321 -8
  97. package/src/mind/types.ts +15 -11
  98. package/src/store-sqlite.ts +92 -1
  99. package/src/store.ts +113 -7
  100. package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
  101. package/test/106-the-join-fires.test.mjs +21 -0
  102. package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
  103. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
  104. package/test/136-the-two-named-limits.test.mjs +3 -2
  105. package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
  106. package/test/148-exact-shortcuts-agree.test.mjs +188 -0
  107. package/test/149-the-closure-engine.test.mjs +138 -0
  108. package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
  109. package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
  110. package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
  111. package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
  112. package/test/154-the-question-names-the-step.test.mjs +281 -0
  113. package/test/24-generalization.test.mjs +32 -0
  114. package/test/36-bloom.test.mjs +53 -0
  115. package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
  116. package/test/48-recognise-turn-connective.test.mjs +3 -2
  117. package/test/55-cost-meter.test.mjs +4 -4
  118. package/test/90-connector-read-cap.test.mjs +7 -7
@@ -11,12 +11,14 @@
11
11
  import { PASS, STEP } from "./graph-search.js";
12
12
  import { gistOf, read, resolve } from "./primitives.js";
13
13
  import { recognise } from "./recognition.js";
14
- import { fuseAttention, reason } from "./reasoning.js";
15
- import { closed, remainderOf, unaccountedBytes, unexplainedSpans, windowOf, } from "./derivation.js";
14
+ import { fusionLayer, walkLayer } from "./reasoning.js";
15
+ import { closed, closeOver, remainderOf, unaccountedBytes, unexplainedSpans, windowOf, } from "./derivation.js";
16
16
  import { rItem } from "./trace.js";
17
17
  import { unexplainedLabel } from "./rationale.js";
18
- import { hubBound } from "./traverse.js";
19
- import { Precomputed } from "./pipeline-mechanism.js";
18
+ import { hubBound, offsetCanon, scaffoldExtents } from "./traverse.js";
19
+ import { windowIndex, witness } from "./evidence.js";
20
+ import { indexOf } from "../bytes.js";
21
+ import { Precomputed, } from "./pipeline-mechanism.js";
20
22
  import { coverMechanism } from "./mechanisms/cover.js";
21
23
  import { castMechanism } from "./mechanisms/cast.js";
22
24
  import { confluenceMechanism } from "./mechanisms/confluence.js";
@@ -26,7 +28,7 @@ import { prefixMechanism } from "./mechanisms/prefix-completion.js";
26
28
  import { recallMechanism } from "./mechanisms/recall.js";
27
29
  // Re-exports: cover's pre-resolution helpers and the ALU adapter kept
28
30
  // importable from the pipeline module (their historical home).
29
- export { resolveConcepts, resolveConnectors } from "./mechanisms/cover.js";
31
+ export { offerConcepts, offerConnectors } from "./mechanisms/cover.js";
30
32
  export { aluToMechanism } from "./mechanisms/alu.js";
31
33
  // ── Extension dispatch (pre-loop parse) ─────────────────────────────────────
32
34
  async function collectComputed(ctx, mechanisms, query) {
@@ -83,6 +85,13 @@ export async function think(ctx, query, mechs) {
83
85
  if (query.length === 0)
84
86
  return null;
85
87
  ctx._edgeGuide = gistOf(ctx, query);
88
+ {
89
+ const asked = offsetCanon(ctx, query);
90
+ ctx._edgeAsked = {
91
+ bytes: asked,
92
+ index: windowIndex(asked, ctx.space.maxGroup),
93
+ };
94
+ }
86
95
  ctx._edgeChoice.clear();
87
96
  const t = ctx.trace?.enter("think", [rItem(query, "query")]);
88
97
  const done = (answer, note) => {
@@ -197,12 +206,14 @@ export async function think(ctx, query, mechs) {
197
206
  // below.
198
207
  const incumbent = best;
199
208
  const incumbentGrade = incumbent === null ? null : grade(incumbent.weight);
200
- const regime = worthRunning(2 * STEP)
209
+ const regime = worthDeclared(2 * STEP)
201
210
  ? "composition"
202
211
  : "retrieval";
203
212
  ctx.trace?.step("regimePrediction", [rItem(query, "query")], [], regime === "retrieval"
204
- ? `retrieval regime — incumbent grade ${incumbentGrade} ≤ climb floor ${climbFloorGrade}, ` +
205
- `so no mechanism floored above that grade runs; the consensus climb will not run`
213
+ ? (incumbentGrade !== null && incumbentGrade <= climbFloorGrade
214
+ ? `retrieval regime — incumbent grade ${incumbentGrade} ≤ climb floor ${climbFloorGrade}, `
215
+ : `retrieval regime — grade ${bound} already reached by a mechanism run ahead, below climb floor ${climbFloorGrade}, `) +
216
+ `so no mechanism floored above that grade runs; CAST will not climb`
206
217
  : `composition regime — ${incumbentGrade === null
207
218
  ? "no incumbent (nothing grounded)"
208
219
  : `incumbent grade ${incumbentGrade}`} above climb floor ${climbFloorGrade}, so the full market and climb run`, undefined, {
@@ -210,38 +221,147 @@ export async function think(ctx, query, mechs) {
210
221
  regime,
211
222
  incumbentGrade,
212
223
  climbFloorGrade,
224
+ ...(bound !== Infinity ? { boundGrade: bound } : {}),
213
225
  });
214
226
  };
215
227
  // Phase 3: grounding loop
216
228
  // Per-mechanism accounting (src/meter.ts). The market's whole premise is
217
229
  // that mechanisms compete on one cost scale — so the profiling read-out is
218
230
  // also per-mechanism, uniformly: the loop never asks which one it holds.
219
- for (let mi = 0; mi < mechanisms.length; mi++) {
220
- const mech = mechanisms[mi];
221
- if (mi > 0)
222
- reportRegime();
231
+ //
232
+ // A CHEAPER BOUND IS LOOKED AT BEFORE A DEARER ONE IS PAID FOR.
233
+ //
234
+ // The declared order is the tie-break priority, and the pruning above is
235
+ // only as strong as the incumbent it has: a mechanism floored LOWER than the
236
+ // one about to invest, but declared after it, could not prune it. Measured
237
+ // on the 31.7M-node store: a lowercased dialogue turn (#97 of the battery)
238
+ // was won by recall at grade 1 — after CAST (floor grade 2) had paid the
239
+ // consensus climb and the weave, confluence (3) a reach climb, and extraction
240
+ // and reference their reads: ~10 s of a 12.6 s response, for candidates that
241
+ // could not win.
242
+ //
243
+ // So before mechanism `m` first-touches anything, every LATER mechanism whose
244
+ // bound is strictly lower runs AHEAD of it, cheapest bound first, and the
245
+ // lowest grade any of them reaches becomes `bound`. A mechanism whose floor
246
+ // grade exceeds `bound` is then skipped. THE DECISION IS UNCHANGED — the
247
+ // same candidate wins as in the declared order:
248
+ // • a mechanism `p` run ahead with best grade g bounds the final grade by
249
+ // g: in the declared order p either runs (its candidate is weighed) or is
250
+ // pruned by an incumbent already at or below p's floor ≤ g;
251
+ // • so a mechanism floored above `bound` has only candidates the final
252
+ // winner strictly outgrades — and with every candidate above `bound`
253
+ // dropped, every mechanism floored at or below it meets the same
254
+ // run-or-prune decision (`f < incumbent` iff `f < min(incumbent,
255
+ // bound + 1)` for f ≤ bound) and yields the same candidates;
256
+ // • and the winner is chosen from the candidates at or below `bound`, in
257
+ // declared order — `consider` replays them where they are declared.
258
+ // Equal-grade floors are NOT skipped (≤, not <): a mechanism declared earlier
259
+ // keeps the tie it would have won. Running `p` ahead is never extra work:
260
+ // what prunes p in the declared order is a candidate at or below p's floor,
261
+ // which only a mechanism floored at or below it can produce — and every such
262
+ // mechanism is either already run or run ahead of p.
263
+ //
264
+ // The bound is learnt by asking `floor` with a `worthRunning` that refuses:
265
+ // under the investment discipline (pipeline-mechanism.ts) a floor that cannot
266
+ // pay returns its bound UNINVESTED, so the question costs no analysis.
267
+ const refuse = () => false;
268
+ const probed = new Array(mechanisms.length);
269
+ const probeGrade = async (i) => {
270
+ if (probed[i] === undefined) {
271
+ const f = await mechanisms[i].floor(ctx, query, pre, refuse);
272
+ probed[i] = f === null ? null : grade(f);
273
+ }
274
+ return probed[i];
275
+ };
276
+ /** Per mechanism: its floor once computed, and its results once run. */
277
+ const floors = new Map();
278
+ const runs = new Map();
279
+ let bound = Infinity;
280
+ const worthAhead = (floor) => grade(floor) <
281
+ Math.min(best === null ? Infinity : grade(best.weight), bound);
282
+ const worthDeclared = (floor) => worthRunning(floor) && grade(floor) <= bound;
283
+ const floorOf = async (i, worth) => {
284
+ if (floors.has(i))
285
+ return floors.get(i);
286
+ const mech = mechanisms[i];
223
287
  const floor = meter
224
- ? await meter.time(`${mech.name}.floor`, () => mech.floor(ctx, query, pre, worthRunning))
225
- : await mech.floor(ctx, query, pre, worthRunning);
288
+ ? await meter.time(`${mech.name}.floor`, () => mech.floor(ctx, query, pre, worth))
289
+ : await mech.floor(ctx, query, pre, worth);
226
290
  if (meter) {
227
291
  if (floor === null)
228
292
  meter.mechanismSkips++;
229
293
  else
230
294
  meter.mechanismFloors++;
231
295
  }
296
+ floors.set(i, floor);
297
+ return floor;
298
+ };
299
+ const runOf = async (i) => {
300
+ let results = runs.get(i);
301
+ if (results === undefined) {
302
+ const mech = mechanisms[i];
303
+ if (meter)
304
+ meter.mechanismRuns++;
305
+ results = meter
306
+ ? await meter.time(`${mech.name}.run`, () => mech.run(ctx, query, pre))
307
+ : await mech.run(ctx, query, pre);
308
+ runs.set(i, results);
309
+ }
310
+ return results;
311
+ };
312
+ const runAhead = async (mi) => {
313
+ const g = await probeGrade(mi);
314
+ if (g === null)
315
+ return;
316
+ const ahead = [];
317
+ for (let j = mi + 1; j < mechanisms.length; j++) {
318
+ if (floors.has(j))
319
+ continue;
320
+ const gj = await probeGrade(j);
321
+ if (gj !== null && gj < g)
322
+ ahead.push([gj, j]);
323
+ }
324
+ ahead.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
325
+ for (const [gj, j] of ahead) {
326
+ // Only what the declared order would also run: a bound that cannot beat
327
+ // what is already held is not even asked for its real floor.
328
+ if (!(gj < Math.min(best === null ? Infinity : grade(best.weight), bound))) {
329
+ continue;
330
+ }
331
+ const floor = await floorOf(j, worthAhead);
332
+ if (floor === null || !worthAhead(floor))
333
+ continue;
334
+ ctx.trace?.step("runAhead", [], [], `${mechanisms[j].name} runs ahead of ${mechanisms[mi].name} — its floor (grade ${grade(floor)}) is below ${mechanisms[mi].name}'s (grade ${g}), so its result bounds what ${mechanisms[mi].name} could win`);
335
+ for (const r of await runOf(j)) {
336
+ // `consider` drops an empty answer, so it bounds nothing.
337
+ if (r.bytes.length === 0)
338
+ continue;
339
+ bound = Math.min(bound, grade(weigh(r.accounted, r.moves)));
340
+ }
341
+ }
342
+ };
343
+ for (let mi = 0; mi < mechanisms.length; mi++) {
344
+ const mech = mechanisms[mi];
345
+ if (mi > 0) {
346
+ await runAhead(mi);
347
+ reportRegime();
348
+ }
349
+ const floor = await floorOf(mi, worthDeclared);
232
350
  if (floor === null) {
233
351
  ctx.trace?.step("skipMechanism", [], [], `${mech.name} skipped — structural precondition failed`);
234
352
  continue;
235
353
  }
354
+ if (grade(floor) > bound) {
355
+ if (meter)
356
+ meter.mechanismsBounded++;
357
+ ctx.trace?.step("skipMechanism", [], [], `${mech.name} skipped — floor ${floor} cannot beat grade ${bound}, already reached by a mechanism run ahead`);
358
+ continue;
359
+ }
236
360
  if (!worthRunning(floor)) {
237
361
  ctx.trace?.step("skipMechanism", [], [], `${mech.name} skipped — floor ${floor} cannot beat incumbent (grade ${grade(best.weight)})`);
238
362
  continue;
239
363
  }
240
- if (meter)
241
- meter.mechanismRuns++;
242
- const results = meter
243
- ? await meter.time(`${mech.name}.run`, () => mech.run(ctx, query, pre))
244
- : await mech.run(ctx, query, pre);
364
+ const results = await runOf(mi);
245
365
  for (const r of results) {
246
366
  // ONE FORMULA, EVERY CANDIDATE: the chart's derivation reports how many
247
367
  // discrete moves it made and which bytes it could not recognise; the
@@ -352,19 +472,28 @@ export async function think(ctx, query, mechs) {
352
472
  // without evidence stays owed, and a later transition pays it only by carrying
353
473
  // it (the law reads the window; see derivation.ts). Same reading, one
354
474
  // definition — not a second spelling of it here.
355
- const explained = [
475
+ const priced = [
356
476
  ...decided.accounted,
357
477
  ...pre.computed.map((u) => [u.i, u.j]),
358
- ].filter(([a, b]) => windowOf([a, b], answer, query, ctx.space.maxGroup) !== null);
478
+ ];
479
+ const explained = priced.filter(([a, b]) => windowOf([a, b], answer, query, ctx.space.maxGroup) !== null);
480
+ // THE DERIVATION IS BORN OWING ITS DISCRIMINATIVE MATERIAL. The bytes a
481
+ // corpus-global scaffolding window reaches (traverse.ts, `scaffoldExtents`)
482
+ // are nobody's debt — otherwise a later step could claim to pay them by
483
+ // restating ` is `, which every fact holds, and the law (which measures
484
+ // carrying by any window of what is owed) would admit it; the substitution
485
+ // bridge reads gaps the same way (`explainedSpan`). PRICING is untouched:
486
+ // the ladder still charges every unexplained byte, because for a question
487
+ // made of nothing but scaffolding (`How are you today?`) covering those bytes
488
+ // IS the evidence.
489
+ const scaffold = scaffoldExtents(ctx, query);
490
+ const paid = remainderOf(query.length, [...explained, ...scaffold], ctx.space.maxGroup);
359
491
  // WHAT THE CONSTRUCTION WITHHOLDS, at or above one quantum: the difference between
360
492
  // the remainder paid in full and the remainder paid by carrying. Both readings
361
- // are the law's, so the floor is applied once and in one place.
362
- const paidInFull = remainderOf(query.length, [
363
- ...decided.accounted,
364
- ...pre.computed.map((u) => [u.i, u.j]),
365
- ], ctx.space.maxGroup);
366
- const paid = remainderOf(query.length, explained, ctx.space.maxGroup);
493
+ // are the law's, so the floor is applied once and in one place — and the
494
+ // paid-in-full reading is computed only when a meter will read it.
367
495
  if (ctx.meter) {
496
+ const paidInFull = remainderOf(query.length, priced, ctx.space.maxGroup);
368
497
  ctx.meter.groundingWithheldBytes += unaccountedBytes(paid) -
369
498
  unaccountedBytes(paidInFull);
370
499
  }
@@ -378,8 +507,23 @@ export async function think(ctx, query, mechs) {
378
507
  };
379
508
  const uncovered = state.remainder;
380
509
  // ── Post-grounding, gated by the declaration and the remainder ────────
510
+ // WHAT THE GROUNDING SPOKE FOR, when it did not declare it: the forms inside
511
+ // the answer that the ASKER already holds. An answer is the asker's material
512
+ // plus what the corpus added — the fact's own value — and only the former was
513
+ // spoken for: re-entering it walks back to the question. The latter is what a
514
+ // chain continues THROUGH. Consuming every form recognised in the answer
515
+ // consumed that too, so a chain could start only where recognition happened
516
+ // to miss the next entity — measured on 133 held-out 2Wiki compositional
517
+ // questions, 3 pivot steps in all once interior recognition named the entity
518
+ // inside every fact. Witnessed exactly, as `chooseNext`'s exact tier reads the
519
+ // question (evidence.ts); a form below one window cannot be witnessed, and is
520
+ // consumed as before.
521
+ const asked = ctx._edgeAsked;
381
522
  const preConsumed = declaredUsed ??
382
- new Set(recognise(ctx, answer).sites.map((s) => s.payload));
523
+ new Set(recognise(ctx, answer).sites
524
+ .filter((s) => asked === null || s.end - s.start < ctx.space.maxGroup ||
525
+ witness(offsetCanon(ctx, answer.subarray(s.start, s.end)), [asked.index], ctx.space.maxGroup).complete)
526
+ .map((s) => s.payload));
383
527
  // A grounding that DECLARED itself complete is not extended: the answer is
384
528
  // already a trained form's own continuation, reached through an identity
385
529
  // claim about the query, so a multi-hop pivot could only chain past the
@@ -408,7 +552,15 @@ export async function think(ctx, query, mechs) {
408
552
  // `preConsumed` is derived by re-recognising the answer — "everything in
409
553
  // it", not "what it voiced" — and a containment rule over that would
410
554
  // suppress every pivot the answer legitimately contains.
411
- const voiced = declaredUsed === undefined ? [] : [...declaredUsed].flatMap((id) => ctx.store.nextFirst(id, hubBound(ctx)).map((n) => read(ctx, n)));
555
+ //
556
+ // …and a continuation the ANSWER ITSELF holds was voiced, not withheld. A
557
+ // substitution that answers with its anchor's own continuation (`The director
558
+ // of Beat Girl is Edmond T. Gréville.`) declared that anchor used, so reading
559
+ // its continuations as withheld refused every pivot inside the answer — the
560
+ // chain could never step past the entity the first hop introduced.
561
+ const voiced = declaredUsed === undefined ? [] : [...declaredUsed].flatMap((id) => ctx.store.nextFirst(id, hubBound(ctx))
562
+ .map((n) => read(ctx, n))
563
+ .filter((v) => indexOf(answer, v, 0) < 0));
412
564
  // WHAT THIS BRANCH READ, published where it was read. Post-grounding decides
413
565
  // by the DECLARATION (`decided.used`, which becomes `voiced`), by what the
414
566
  // recognition already consumed (`preConsumed`) and by the derivation's own
@@ -451,73 +603,51 @@ export async function think(ctx, query, mechs) {
451
603
  meter.postGroundingRemainderSpans += uncovered.length;
452
604
  meter.postGroundingRemainderBytes += unaccountedBytes(uncovered);
453
605
  }
454
- // THE WALK THAT DOES NOT RUN, NAMED — the audit's point 1 could not attribute
455
- // real questions that stopped with no note anywhere. They never reached the
456
- // offer: `decided.complete` says the grounding supplied a fixed point, so the
457
- // walk is skipped BY DESIGN and the state is the grounding's own. That state
458
- // carries `fixed: true` (the trace already reports it), and the law's first
459
- // clause refuses any continuation against it — so there is no `null` here to
460
- // read as exhaustion, and the `Offer` contract is not in play at all. The
461
- // silent stop is therefore a NAMED state, not a gap.
606
+ // ── THE CLOSURE ENGINE ───────────────────────────────────────────────
607
+ //
608
+ // The grounding's state is CLOSED under the law by two layers, in order: the
609
+ // multi-hop WALK (`walkLayer`: a forward absorb or a pivot, offered one at a
610
+ // time) and the multi-topic FUSION (`fusionLayer`: one composed transition).
611
+ // Every step either layer offers goes through the same `closure` and the same
612
+ // law; this function no longer sequences them or gates them by hand.
613
+ //
614
+ // WHAT USED TO BE TWO HAND-WRITTEN GATES IS NOW THE LAW'S, OR THE LAYER'S:
615
+ // • a declared-complete grounding (`fixed`) admits no transition — the
616
+ // engine enters no layer for it (the law's first clause), which is the
617
+ // walk-skip and the fusion-skip this branch used to spell separately;
618
+ // • a CLOSED derivation has nothing left for a second topic to account for,
619
+ // so the fusion layer does not ENGAGE — read off the state the walk
620
+ // reached, the one fusion is actually offered against.
621
+ //
622
+ // What the fusion needs from the grounding — whether its substance is purely
623
+ // computed (`unclimbed`) and where it stands in the query (`primarySpans`) —
624
+ // is the grounding's own evidence, resolved here where both readings are in
625
+ // hand.
462
626
  //
463
- // THE WALK CONSUMES AND RETURNS A STATE. It is handed the derivation's own —
464
- // the grounding's product, accounting, remainder and cost — and hands back the
465
- // state it advanced to, so what follows reads a state rather than bytes plus a
466
- // tuple rebuilt here. A supplied fixed point is the one case where the walk
467
- // does not run at all, and then the state is the grounding's own.
468
- const extension = decided.complete ? undefined : meter
469
- ? await meter.time("reason", () => reason(ctx, query, state, preConsumed, pre, voiced))
470
- : await reason(ctx, query, state, preConsumed, pre, voiced);
471
- const reasoned = extension ?? state;
472
- // Fuse only when the query has a genuine REMAINDER no mechanism's
473
- // structural evidence touched at all. `decided.accounted` alone
474
- // undercounts this: it is a COST-LADDER quantity (cover.ts prices its
475
- // masked/computed spans at near-zero and deliberately leaves them out of
476
- // `accounted` so PASS-bridged bytes are still charged), not a coverage
477
- // one — a query fully explained by one computed span plus bridged
478
- // connectors can report `accounted: []` while nothing is actually left
479
- // unexplained. The genuine remainder is what NEITHER the winning
480
- // candidate's accounted spans NOR any recognised extension's computed
481
- // span (`pre.computed` — every mechanism's parse() output, ALU included)
482
- // ever touched. A remainder under one river-fold quantum (W, the same
483
- // floor cover.ts's restatedSpan and the honesty-density bar above both
484
- // use) is bridging punctuation/whitespace, never a second topic —
485
- // observed: a single space between two fully-computed arithmetic spans
486
- // ("2+2 3+3") registered as "unaccounted" and pulled in an unrelated
487
- // corpus fact, corrupting "4 6" into "4 63".
488
- // THE GATE ASKS THE LAW, and that is an OPTIMISATION, not a tidy-up: the state
489
- // above ALREADY carries the remainder (`remainderOf`, per-span, with the W
490
- // floor applied), so asking it costs nothing, while the total this line used to
491
- // compute (`unaccounted(explained)`) was one more sum over the spans on every
492
- // response. The two readings are the same condition, not two: the ACCOUNTING
493
- // applies the same W floor the gate does, so a gap below one quantum never
494
- // survives into `explained` and the total cannot reach W without some single
495
- // gap reaching it. Measured over twelve constructions at W = 4 (test/136.3,
496
- // which pins the equivalence and both sides of it).
497
- // Whether the winning candidate's entire recognised substance is
498
- // COMPUTED — every accounted span exactly a pre.computed span, nothing
499
- // from a genuinely recognised/climbed site. fuseAttention's lone-root
500
- // shortcut assumes a single point of attention already IS primary's own
501
- // source; that assumption is exactly backwards for a pure computation
502
- // (an ALU result has no anchor of its own) — see fuseAttention's
503
- // `unclimbed` parameter, gated there by Attention.breadth so a
504
- // coincidental echo (which this flag alone cannot distinguish) is still
505
- // rejected.
627
+ // Whether the winning candidate's entire recognised substance is COMPUTED —
628
+ // every accounted span exactly a pre.computed span, nothing from a genuinely
629
+ // recognised/climbed site. fuseAttention's lone-root shortcut assumes a
630
+ // single point of attention already IS primary's own source; that assumption
631
+ // is exactly backwards for a pure computation (an ALU result has no anchor of
632
+ // its own) — gated there by Attention.breadth so a coincidental echo (which
633
+ // this flag alone cannot distinguish) is still rejected.
506
634
  const unclimbed = state.accounted.length > 0 &&
507
635
  state.accounted.every(([i, j]) => pre.computed.some((u) => u.i === i && u.j === j));
508
- // Where the winning grounding stands in the query — fusion places primary
509
- // by it (see fuseAttention's `primarySpans`). `accounted` is the
510
- // cost-ladder read and is authoritative when non-empty; when it is empty
511
- // the grounding is a pure COMPUTATION, whose evidence is its computed span.
512
- // Exactly the cost-ladder-vs-coverage distinction `explained` above draws,
513
- // read here for POSITION instead of for coverage — and resolved here, where
514
- // both readings are in hand, rather than inside fuseAttention.
636
+ // Where the winning grounding stands in the query — fusion places primary by
637
+ // it. `accounted` is the cost-ladder read and is authoritative when
638
+ // non-empty; when it is empty the grounding is a pure COMPUTATION, whose
639
+ // evidence is its computed span — the cost-ladder-vs-coverage distinction
640
+ // `explained` above draws, read here for POSITION instead of coverage.
515
641
  const primarySpans = state.accounted.length > 0
516
642
  ? state.accounted
517
643
  : pre.computed.map((u) => [u.i, u.j]);
518
- const fused = closed(state) ? reasoned : meter
519
- ? await meter.time("fuse", () => fuseAttention(ctx, query, reasoned, pre, unclimbed, primarySpans))
520
- : await fuseAttention(ctx, query, reasoned, pre, unclimbed, primarySpans);
644
+ const fused = await closeOver(state, query, ctx.space.maxGroup, [
645
+ walkLayer(ctx, query, preConsumed, pre, voiced),
646
+ {
647
+ ...fusionLayer(ctx, query, pre, unclimbed, primarySpans),
648
+ engages: (d) => !closed(d),
649
+ },
650
+ ], meter ? (name, walk) => meter.time(name, walk) : undefined);
521
651
  done(fused.product,
522
652
  // NO CLAIM ABOUT FUSION HERE. `fuseAttention` is entered whenever a
523
653
  // remainder ≥ W exists and returns early when there is nothing to bridge, so
@@ -1,11 +1,6 @@
1
1
  import { Vec } from "../vec.js";
2
2
  import { Sema } from "../sema.js";
3
3
  import type { Input, MindContext } from "./types.js";
4
- /** The content key of a byte span — one latin1 char per byte, an exact,
5
- * collision-free encoding. Spans on the perception path are query-scale
6
- * (windows, regions, candidate spans), so key construction is far cheaper
7
- * than the river fold it deduplicates. */
8
- export declare function latin1Key(bytes: Uint8Array): string;
9
4
  /** The {@link perceive} memo key: the span's content PLUS the boundary set it
10
5
  * was folded under. The tree is a function of BOTH — the same bytes fold
11
6
  * plainly with no boundaries and into a left-nested stable-prefix shape with
@@ -59,6 +54,31 @@ export declare function foldTree(ctx: MindContext, n: Sema, start: number, visit
59
54
  end: number;
60
55
  node: number | null;
61
56
  };
57
+ /** The EXACT content-addressed node of a byte stream — `foldTree(perceive)`,
58
+ * read for identity alone.
59
+ *
60
+ * A fold names a branch only when every child is named, so identity needs the
61
+ * fold's SHAPE and the store's answer per node, never its vectors:
62
+ * {@link contentIdentity} walks the same shape (geometry.ts — one grouping
63
+ * rule, two algebras) and asks the store bottom-up, building no D-dimensional
64
+ * gist and leaving nothing in the perception memo. Each node is named the
65
+ * way the store's write side names it ({@link branchNaming}). `test/148` pins
66
+ * the agreement with the full fold over random and corpus spans. */
67
+ export declare function exactNode(ctx: MindContext, bytes: Uint8Array): number | null;
68
+ /** {@link exactNode}, with whether the span's own name was found only through
69
+ * its BYTES — its children named no branch, and the flat node over the same
70
+ * bytes did ({@link branchNaming}). That is where the exact lookup used to
71
+ * MISS, so it is where {@link resolve} still asks the canonical class: the
72
+ * class may hold the learnt member that leads somewhere, which a flat index
73
+ * entry need not (measured: `tonight` named an edge-less window and
74
+ * pre-empted the case-folded `Tonight` whose edge a composition stood on).
75
+ * Recognition's probes reach it through `resolve`; asking it again for the
76
+ * perceived tree's own byte-named groups changed none of 116 real queries and
77
+ * no test, so it is not asked there. */
78
+ export declare function exactNaming(ctx: MindContext, bytes: Uint8Array): {
79
+ id: number | null;
80
+ byBytes: boolean;
81
+ };
62
82
  /** The canonical node id of a byte span: perceive it in isolation — the way
63
83
  * training did — and recover its root bottom-up. Returns null if any part is
64
84
  * unknown. */