@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
@@ -14,11 +14,10 @@ import { read } from "../primitives.js";
14
14
  import { argmaxBy, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "../traverse.js";
15
15
  import { analogyStrength, follow, project, reverseContext, sharedFrameStrengthOf, } from "../match.js";
16
16
  import { joinWithBridge } from "../resonance.js";
17
- import { restatesQuery } from "../reasoning.js";
18
17
  import { CONCEPT, STEP } from "../graph-search.js";
19
18
  import { indexOf } from "../../bytes.js";
20
19
  import { consensusFloor, dominates } from "../../geometry.js";
21
- import { unexplainedLabel, unexplainedSpans, } from "../rationale.js";
20
+ import { restates, unexplainedSpans } from "../derivation.js";
22
21
  import { rItem, rNode } from "../trace.js";
23
22
  import { dismissedKnownContent } from "../bridge.js";
24
23
  import { leafIdRun } from "../canonical.js";
@@ -89,7 +88,7 @@ const MIN_WEAVE = 2;
89
88
  * describes id ("...painted by Leonardo da Vinci." contains "Leonardo da
90
89
  * Vinci"). An incidental adjacency predecessor never does — it merely
91
90
  * preceded id in some unrelated document without ever mentioning it. No
92
- * new tuned constant: containment is the same primitive `restatesQuery`
91
+ * new tuned constant: containment is the same primitive `restates`
93
92
  * and `dominates`-style checks already use throughout this codebase.
94
93
  *
95
94
  * `allowForward` (default true) gates the FORWARD branch specifically —
@@ -421,7 +420,6 @@ export async function counterfactualTransfer(ctx, query, pre) {
421
420
  used: used ?? new Set(),
422
421
  accounted,
423
422
  moves,
424
- unexplained: unexplainedLabel(query, accounted),
425
423
  });
426
424
  };
427
425
  ctx.trace?.step("alignStructures", [rItem(query, "query")], points.map((p) => rNode(ctx, p.anchor, "structure", p.vote)), "the independent learnt structures the query weaves, by graded alignment");
@@ -522,7 +520,7 @@ export async function counterfactualTransfer(ctx, query, pre) {
522
520
  let answer = await joinWithBridge(ctx, filler, tail);
523
521
  const fwd = await follow(ctx, proj.anchor, qv);
524
522
  if (fwd !== null && indexOf(answer, fwd, 0) < 0 &&
525
- !restatesQuery(query, fwd)) {
523
+ !restates(query, fwd, 0, { proper: true })) {
526
524
  // THROUGH THE SHARED JOINER, not a bare concatenation.
527
525
  //
528
526
  // `joinWithBridge` is the composition step every out-of-search assembly
@@ -588,7 +586,7 @@ export async function counterfactualTransfer(ctx, query, pre) {
588
586
  // cap the test reads "none of the established continuations appears".
589
587
  const domNext = ctx.store.nextFirst(dominant.anchor, hubBound(ctx));
590
588
  const displaced = domNext
591
- .every((n) => indexOf(query, read(ctx, n), 0) < 0);
589
+ .every((n) => !restates(query, read(ctx, n), 0));
592
590
  // The SUBSTITUTE is what redirection speaks — the answer IS `project(last)`,
593
591
  // its own fact — so the same bar applies. The displaced structure is only
594
592
  // recognised as the slot being overridden and is never voiced, so it is
@@ -658,7 +656,7 @@ export async function counterfactualTransfer(ctx, query, pre) {
658
656
  if (queryScale(p.ctx.length) &&
659
657
  indexOf(dominant.ctx, p.ctx, 0) < 0 &&
660
658
  indexOf(p.ctx, dominant.ctx, 0) < 0 &&
661
- indexOf(query, p.ctx, 0) < 0) {
659
+ !restates(query, p.ctx, 0)) {
662
660
  analogs.push({ anchor: p.anchor, point: p, src: p });
663
661
  }
664
662
  // Reach through to the point's continuation targets regardless
@@ -675,7 +673,7 @@ export async function counterfactualTransfer(ctx, query, pre) {
675
673
  if (!queryScale(nctx.length) ||
676
674
  indexOf(dominant.ctx, nctx, 0) >= 0 ||
677
675
  indexOf(nctx, dominant.ctx, 0) >= 0 ||
678
- indexOf(query, nctx, 0) >= 0)
676
+ restates(query, nctx, 0))
679
677
  continue;
680
678
  analogs.push({ anchor: nid, point: null, src: p });
681
679
  }
@@ -731,7 +729,7 @@ export async function counterfactualTransfer(ctx, query, pre) {
731
729
  // grounds") — fine for ORIENTING mechanisms, not for voicing learnt
732
730
  // content the query never asked about. Computed once here; both the
733
731
  // hub fallback below and the comparison gate consume it.
734
- const rootTrusted = roots.some((r) => r.vote >= consensusFloor(corpusN(ctx)));
732
+ const rootTrusted = roots.some((r) => r.idfVote >= consensusFloor(corpusN(ctx))); // the IDF sum: the bar's own quantity
735
733
  // The context that ESTABLISHES a filler — the same reverse context, under
736
734
  // the same naming test, `seatOfNode` uses to VOICE an analog (a predecessor
737
735
  // whose bytes CONTAIN the node's: it names or describes it, rather than
@@ -1132,15 +1130,15 @@ export async function counterfactualTransfer(ctx, query, pre) {
1132
1130
  // consulting it here is the same fallback `resolve` already makes when an
1133
1131
  // exact content lookup misses, and it keeps this mechanism from carrying
1134
1132
  // any idea of its own about what a character is.
1135
- const echoesQuery = (x) => {
1136
- if (restatesQuery(query, x))
1137
- return true;
1138
- const canon = ctx.canon;
1139
- if (canon === null)
1140
- return false;
1141
- const cq = canon(query), cx = canon(x);
1142
- return cx.length < cq.length && indexOf(cq, cx, 0) >= 0;
1143
- };
1133
+ // TWO WITNESSES, ONE LAW: the byte reading, and — when the response carries
1134
+ // one — the same reading under the response's own equivalence. Asked twice
1135
+ // rather than branched on, because the equivalence is a property of the
1136
+ // injected canonicalizer (a substring-monotone function is the usual case,
1137
+ // not a guarantee), and the two readings are OR-ed here exactly as they were
1138
+ // before this became one definition.
1139
+ const echoesQuery = (x) => restates(query, x, 0, { proper: true }) ||
1140
+ (ctx.canon !== null &&
1141
+ restates(query, x, 0, { equate: ctx.canon, proper: true }));
1144
1142
  if (echoesQuery(b)) {
1145
1143
  const fwd = await follow(ctx, bestAnalog.anchor, qv);
1146
1144
  if (fwd !== null && fwd.length > 0 && !echoesQuery(fwd))
@@ -1240,7 +1238,6 @@ export const castMechanism = {
1240
1238
  accounted: c.accounted,
1241
1239
  moves: c.moves,
1242
1240
  used: c.used,
1243
- unexplained: c.unexplained,
1244
1241
  }));
1245
1242
  },
1246
1243
  };
@@ -10,9 +10,6 @@ export interface JoinResult {
10
10
  used: ReadonlySet<number>;
11
11
  accounted: Array<[number, number]>;
12
12
  moves: number;
13
- /** A human-readable label for the query bytes the meet left unexplained —
14
- * purely diagnostic, never priced. */
15
- unexplained: string;
16
13
  }
17
14
  /** The main confluence entry point. Given a query, detect whether it weaves
18
15
  * two or more INDEPENDENT constraints (ranked anchors supported by disjoint
@@ -42,7 +42,7 @@ import { read } from "../primitives.js";
42
42
  import { corpusN, reachOf } from "../traverse.js";
43
43
  import { dominates } from "../../geometry.js";
44
44
  import { STEP } from "../graph-search.js";
45
- import { unexplainedLabel } from "../rationale.js";
45
+ import { insideAnsweredTurn } from "../derivation.js";
46
46
  import { rItem, rNode } from "../trace.js";
47
47
  /** The main confluence entry point. Given a query, detect whether it weaves
48
48
  * two or more INDEPENDENT constraints (ranked anchors supported by disjoint
@@ -75,13 +75,9 @@ export async function confluenceJoin(ctx, query, pre) {
75
75
  // Recognition and attention still see the full transcript; only this
76
76
  // mechanism's constraint population excludes answered spans.
77
77
  const queryWin = new Map();
78
- let answered = 0;
78
+ const answered = { at: 0 };
79
79
  for (const [off, id] of pre.queryWindows) {
80
- while (answered < ctx.answeredSpans.length &&
81
- ctx.answeredSpans[answered][1] <= off)
82
- answered++;
83
- const span = ctx.answeredSpans[answered];
84
- if (span && span[0] <= off && off + W <= span[1])
80
+ if (insideAnsweredTurn(ctx.answeredSpans, answered, off, off + W))
85
81
  continue;
86
82
  queryWin.set(off, id);
87
83
  }
@@ -98,6 +94,30 @@ export async function confluenceJoin(ctx, query, pre) {
98
94
  // constraints). Shard-bound streams are no constraints, and their meets
99
95
  // are connective debris (". Sure,", "ngul" — observed).
100
96
  const bindsAConstituent = (cover) => cover.some(([cs, ce]) => ce - cs >= 2 * W);
97
+ // THE VOTE ENTERS AS ORDER, NEVER AS A BAR. This is the only one of the
98
+ // climb's four consumers (recall, fuseAttention, cast, here) that uses the
99
+ // evidence's MAGNITUDE without a floor, and it is legitimate by construction:
100
+ // `ranked` answers "which anchor is stronger" — a question about votes, so the
101
+ // comparison stays within one dimension — and the vote is otherwise only
102
+ // REPORTED (Stream.vote travels to the rationale's constraint nodes). What
103
+ // actually SELECTS a constraint is byte-structural and never the magnitude: a
104
+ // run of at least 2W (`bindsAConstituent`, with its accidental-sharing
105
+ // counter-examples above), disjoint covers (`disjoint`), and scaffolding never
106
+ // binds at all (`dominates(reachOf(…), N)`). The MEET such a stream may
107
+ // produce is selected the same way: a span shorter than 2W is rejected, and
108
+ // the winner is the one with the smallest `reach` (ties broken by the longer
109
+ // span) — a corpus quantity and bytes, never the vote, which appears only in
110
+ // the trace item.
111
+ // binds at all (`dominates(reachOf(…), N)`). The only cut in this loop is a
112
+ // BUDGET, and it is measured: stopping the scan at 2W anchors saves 50-70% of
113
+ // confluence's cost on non-conjunctive queries while preserving every genuinely
114
+ // conjunctive case, whose top anchors ARE its constraints.
115
+ // MEASURED (this goal, on THIS file's own conjunctive fixture): the two
116
+ // streams appear at ranks 1 and 4 against a budget of 2W = 8, on a query whose
117
+ // `ranked` is 9 — so the cut IS live (it would have returned null at the 8th
118
+ // anchor) and it does NOT prune the case it exists to protect. The other
119
+ // conjunctive fixture (the Leonardo one) finds them at ranks 0 and 1. Scope:
120
+ // these are the repo's conjunctive fixtures, and no more.
101
121
  const streams = [];
102
122
  const rankedCapped = ranked.length > pre.k ? ranked.slice(0, pre.k) : ranked;
103
123
  // CONJUNCTIVITY EARLY-EXIT: a conjunctive query's top-ranked anchors
@@ -234,7 +254,6 @@ export async function confluenceJoin(ctx, query, pre) {
234
254
  used: new Set([met.a.anchor, met.b.anchor]),
235
255
  accounted,
236
256
  moves: 3 * STEP,
237
- unexplained: unexplainedLabel(query, accounted),
238
257
  };
239
258
  }
240
259
  // ── Pipeline mechanism ──────────────────────────────────────────────────────
@@ -265,7 +284,6 @@ export const confluenceMechanism = {
265
284
  accounted: met.accounted,
266
285
  moves: met.moves,
267
286
  used: met.used,
268
- unexplained: met.unexplained,
269
287
  }];
270
288
  },
271
289
  };
@@ -13,8 +13,8 @@ import { guidedFirst, hubBound } from "../traverse.js";
13
13
  import { conceptHop } from "../match.js";
14
14
  import { bridge } from "../resonance.js";
15
15
  import { liftAnswer, liftedScaffolding, segRestatesQuery } from "../types.js";
16
- import { decodeText, unexplainedLabel } from "../rationale.js";
17
- import { indexOf } from "../../bytes.js";
16
+ import { decodeText } from "../rationale.js";
17
+ import { insideAnsweredTurn, restates } from "../derivation.js";
18
18
  import { rItem, rNode, traceDerivation } from "../trace.js";
19
19
  // ── Concept / connector pre-resolution ──────────────────────────────────────
20
20
  export async function resolveConcepts(ctx, sites) {
@@ -45,16 +45,13 @@ export async function resolveConnectors(ctx, sites, query) {
45
45
  // discarded — a semantically neutral gate (it removes work whose product
46
46
  // liftAnswer throws away), and a cumulative (multi-turn) query is exactly
47
47
  // where such already-answered continuations recur.
48
- let answered = 0;
48
+ const answered = { at: 0 };
49
49
  const ordered = [...sites]
50
50
  .sort((a, b) => a.start - b.start)
51
51
  .filter((s) => {
52
- while (answered < ctx.answeredSpans.length &&
53
- ctx.answeredSpans[answered][1] <= s.start)
54
- answered++;
55
- const span = ctx.answeredSpans[answered];
56
- if (span && span[0] <= s.start && s.end <= span[1])
52
+ if (insideAnsweredTurn(ctx.answeredSpans, answered, s.start, s.end)) {
57
53
  return false;
54
+ }
58
55
  if (query === undefined || ctx.answeredSpans.length === 0)
59
56
  return true;
60
57
  const continuations = ctx.store.nextFirst(s.payload, hubBound(ctx));
@@ -77,7 +74,11 @@ export async function resolveConnectors(ctx, sites, query) {
77
74
  // 3 bytes this reads 4 bytes per candidate instead of the ~231 it
78
75
  // averaged before.
79
76
  const bytes = read(ctx, answer, query.length + 1);
80
- return bytes.length <= query.length && indexOf(query, bytes, 0) >= 0;
77
+ // THE CONTENT READING of the same exclusion: the site's own
78
+ // continuation already occurs in the query, so voicing it back adds
79
+ // nothing. It is the restatement law with no `proper` flag — the
80
+ // containment reading that also admits the whole query.
81
+ return restates(query, bytes, 0);
81
82
  });
82
83
  });
83
84
  const bridgePair = async (l, r) => {
@@ -171,18 +172,11 @@ export const coverMechanism = {
171
172
  ? await ctx.meter.time("cover.resolveConnectors", () => resolveConnectors(ctx, sites, query))
172
173
  : await resolveConnectors(ctx, sites, query);
173
174
  let splits = rec.splits;
174
- let starts = rec.starts;
175
175
  if (computed.length > 0) {
176
176
  splits = new Set(rec.splits);
177
- starts = new Set(rec.starts);
178
177
  for (const u of computed) {
179
178
  splits.add(u.i);
180
179
  splits.add(u.j);
181
- // A computation's own boundaries carry the same fold-level evidence
182
- // a chunk boundary does — "computation always wins" (see the header
183
- // comment) extends to being trusted ground for cross-leaf recovery.
184
- starts.add(u.i);
185
- starts.add(u.j);
186
180
  }
187
181
  }
188
182
  const concepts = ctx.meter
@@ -208,7 +202,7 @@ export const coverMechanism = {
208
202
  ])),
209
203
  ...computedResults.map((u) => rItem(u.bytes, "computed")),
210
204
  ], coverDeps.length ? coverDeps : undefined);
211
- const solved = ctx.search.cover(query.length, sites, concepts, rec.leaves, splits, starts, undefined, connectors, computedResults, ctx.trace ? (steps) => traceDerivation(ctx, steps) : undefined);
205
+ const solved = ctx.search.cover(query.length, sites, concepts, rec.leaves, splits, undefined, connectors, computedResults, ctx.trace ? (steps) => traceDerivation(ctx, steps) : undefined);
212
206
  const segs = solved && solved.segs;
213
207
  tCover?.done(segs === null
214
208
  ? []
@@ -247,9 +241,12 @@ export const coverMechanism = {
247
241
  return [{
248
242
  bytes: composed,
249
243
  accounted,
250
- moves: 0,
251
- weight: solved.cost, // A*LD derivation's g-value IS the weight
252
- unexplained: unexplainedLabel(query, accounted),
244
+ // The derivation's DISCRETE work. The bytes the chart could not
245
+ // recognise are NOT priced here: they are exactly the spans `accounted`
246
+ // leaves uncovered, and the pipeline's one formula charges them at PASS —
247
+ // the same formula that prices every other mechanism's candidate. No
248
+ // mechanism spells a cost of its own.
249
+ moves: solved.moves,
253
250
  // How much of the composed answer is the asker's own unexplained words
254
251
  // (the spans the liftAnswer trace above labels "scaffolding"). Cover is
255
252
  // the mechanism that can carry them, because a PASS span still lands in
@@ -19,7 +19,6 @@ import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
19
19
  export declare function extractBySkill(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<{
20
20
  bytes: Uint8Array;
21
21
  accounted: Array<[number, number]>;
22
- unexplained: string;
23
22
  } | null>;
24
23
  /** Decompose an answer into substrings of its surrounding context, in order —
25
24
  * the STRONG span-shape reading (see the section note above). Returns null
@@ -8,7 +8,7 @@
8
8
  // that contains it.
9
9
  import { locate } from "../match.js";
10
10
  import { concatBytes, indexOf } from "../../bytes.js";
11
- import { decodeText, unexplainedLabel } from "../rationale.js";
11
+ import { decodeText } from "../rationale.js";
12
12
  import { CONCEPT, STEP } from "../graph-search.js";
13
13
  import { rItem, rNode, traceFail } from "../trace.js";
14
14
  // ── Extraction ────────────────────────────────────────────────────────────
@@ -81,6 +81,12 @@ export async function extractBySkill(ctx, query, pre) {
81
81
  const searched = ranked.slice(0, pre.k);
82
82
  let shapeMisses = 0;
83
83
  let subQuantum = 0;
84
+ // A SECOND refusal counter, because the note below used to call an UNANCHORED
85
+ // read "sub-quantum" — which is false, and it is the kind of instrumentation
86
+ // defect AGENTS §6 says to close where it lives: a reader could not tell from
87
+ // the trace which of the two gates refused. Neither counter reaches a
88
+ // decision (meter.ts contract 1); they exist so the refusal is legible.
89
+ let unanchored = 0;
84
90
  for (const cand of searched) {
85
91
  const exemplar = await pre.spanShapedOf(cand.anchor);
86
92
  if (!exemplar) {
@@ -116,15 +122,16 @@ export async function extractBySkill(ctx, query, pre) {
116
122
  // Here the field is this mechanism's own output and carries its documented
117
123
  // meaning, so the test is sound exactly where the convention does not reach.
118
124
  if (built.accounted.length === 0) {
119
- subQuantum++;
125
+ unanchored++;
120
126
  continue;
121
127
  }
122
- if (shapeMisses > 0 || subQuantum > 0) {
128
+ if (shapeMisses > 0 || subQuantum > 0 || unanchored > 0) {
123
129
  ctx.trace?.step("trySkillAnchors", [
124
- rItem(query.subarray(0, 0), `skipped ${shapeMisses + subQuantum}`),
130
+ rItem(query.subarray(0, 0), `skipped ${shapeMisses + subQuantum + unanchored}`),
125
131
  rNode(ctx, cand.anchor, "chosen"),
126
- ], [], `skipped ${shapeMisses} non-exemplar and ${subQuantum} sub-quantum ` +
127
- `anchor(s) before one yielded a usable extraction`);
132
+ ], [], `skipped ${shapeMisses} non-exemplar, ${subQuantum} sub-quantum and ` +
133
+ `${unanchored} unanchored anchor(s) before one yielded a usable ` +
134
+ `extraction`);
128
135
  }
129
136
  t?.done([rItem(built.bytes, "extracted")], built.pieces === 1
130
137
  ? `apply a learnt extraction skill — read the analogous span of the query` +
@@ -134,7 +141,6 @@ export async function extractBySkill(ctx, query, pre) {
134
141
  return {
135
142
  bytes: built.bytes,
136
143
  accounted: built.accounted,
137
- unexplained: unexplainedLabel(query, built.accounted),
138
144
  };
139
145
  }
140
146
  if (shapeMisses === searched.length) {
@@ -321,7 +327,6 @@ export const extractionMechanism = {
321
327
  bytes: ex.bytes,
322
328
  accounted: ex.accounted,
323
329
  moves: CONCEPT + STEP * ex.accounted.length,
324
- unexplained: ex.unexplained,
325
330
  }];
326
331
  },
327
332
  };
@@ -255,7 +255,6 @@ export const prefixMechanism = {
255
255
  // IDENTITY bridge takes.
256
256
  accounted: [[0, query.length]],
257
257
  moves: STEP,
258
- unexplained: "",
259
258
  // NOT complete: the query is a proper PREFIX, so the form may carry more
260
259
  // past the remainder this voiced.
261
260
  }];
@@ -6,7 +6,6 @@ export interface RecallResult {
6
6
  echoed: boolean;
7
7
  accounted: Array<[number, number]>;
8
8
  moves: number;
9
- unexplained: string;
10
9
  /** See {@link import("../pipeline-mechanism.js").MechanismResult.complete}
11
10
  * — set by the IDENTITY-bridge tier alone. */
12
11
  complete?: boolean;
@@ -6,11 +6,11 @@
6
6
  import { cosine } from "../../vec.js";
7
7
  import { consensusFloor, dominates, identityBar, reachThreshold, significanceBar, } from "../../geometry.js";
8
8
  import { gistOf, read, resolve } from "../primitives.js";
9
- import { bytesEqual, indexOf } from "../../bytes.js";
9
+ import { indexOf } from "../../bytes.js";
10
10
  import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
11
11
  import { follow, project, reverseContext, voicesDisplacedFiller, } from "../match.js";
12
12
  import { CONCEPT, STEP } from "../graph-search.js";
13
- import { unexplainedLabel } from "../rationale.js";
13
+ import { restates as lawRestates } from "../derivation.js";
14
14
  import { rItem, rNode } from "../trace.js";
15
15
  import { substitutionBridge } from "../bridge.js";
16
16
  /** Recall the answer by resonating the whole query against the content index. */
@@ -29,7 +29,6 @@ export async function recallByResonance(ctx, query, pre) {
29
29
  echoed,
30
30
  accounted,
31
31
  moves,
32
- unexplained: unexplainedLabel(query, accounted),
33
32
  ...(complete ? { complete } : {}),
34
33
  };
35
34
  };
@@ -103,7 +102,7 @@ export async function recallByResonance(ctx, query, pre) {
103
102
  // guard the argument's OWN later restatement in the same
104
103
  // conversation reads as if it were the next thing to say.
105
104
  if (g !== null && g.length > 0 &&
106
- !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
105
+ !lawRestates(query, g, 0, { proper: true })) {
107
106
  return ground(g, "argument binding — the query's sole edge-source constituent, continuation followed", [[arg.start, arg.end]], STEP);
108
107
  }
109
108
  }
@@ -130,9 +129,10 @@ export async function recallByResonance(ctx, query, pre) {
130
129
  // same principle that keeps cast from voicing stored questions), and
131
130
  // projecting them forward is reverse recall's containment failure in the
132
131
  // other direction — "whatever followed these bytes in some document".
133
- const qKey = ctx.canon ? ctx.canon(query) : query;
134
- const restates = (b) => bytesEqual(b, query) ||
135
- (ctx.canon !== null && bytesEqual(ctx.canon(b), qKey));
132
+ // THE EQUALITY READING of the restatement law: this tier rejects an answer
133
+ // that IS the question (an echo), and a proper fragment is handled by the
134
+ // tier's own subspan tests further down — so the law is asked with `whole`.
135
+ const restates = (b) => lawRestates(query, b, 0, { equate: ctx.canon, whole: true });
136
136
  const idBar = identityBar(ctx.store.D, ctx.space.maxGroup, query.length);
137
137
  if (top.score >= idBar) {
138
138
  for (const h of whole) {
@@ -194,9 +194,36 @@ export async function recallByResonance(ctx, query, pre) {
194
194
  // consensus", while breadth is the SCALE-INVARIANT reading — "a point whose
195
195
  // breadth clears `dominates` (> half the query's regions corroborate it) is
196
196
  // real consensus; one that does not is a coincidental single-region echo".
197
- // Attention.peak's contract makes the same point from the other side:
198
- // comparing a POOLED SUM against a floor that prices ONE region's evidence
199
- // is a dimensional error.
197
+ // THIS USED TO CLAIM A DIMENSIONAL ERROR, AND THAT CLAIM WAS FALSE.
198
+ // It read: "comparing a POOLED SUM against a floor that prices ONE region's
199
+ // evidence is a dimensional error." `consensusFloor` is not priced for one
200
+ // region: thresholds.md §2 derives it as the POOLED-vote significance floor —
201
+ // "each region contributes at most ln(N/c) <= ln(N); ln(N)+1/2 demands ..." —
202
+ // and attention.ts says the same where it builds the vote ("the scale
203
+ // consensusFloor is derived for"). The comparison is in ONE dimension, and
204
+ // it is so because the climb WEIGHTS BY IDF: `wf` in voteRegions is
205
+ // `direct ? df : combined ? idf + df : idf`, and the engine only ever runs the
206
+ // last one (DFMode's default "inverse", the mode every non-test caller uses —
207
+ // `direct` and `combined` are exercised by test/24 and test/27 only, and
208
+ // test/24 pins that their votes DO differ). In those two the sum would leave
209
+ // the floor's dimension and the floor would need re-deriving.
210
+ //
211
+ // What the OR below is really for is SCALE, not dimension (the paragraph
212
+ // above says it): a vote that clears ln(N)+1/2 means "strong" on a small store
213
+ // and "weak" on a large one for the same genuine consensus, so the
214
+ // scale-invariant breadth reading is added beside it.
215
+ //
216
+ // AND THE PREMISE IS IDF. The deviation in the other two weighting modes is
217
+ // TWO-SIDED and DERIVED: `direct` DEFLATES a region (ln(1+c) < ln(N/c) for
218
+ // small c) and `combined` INFLATES it (ln N + ln(1+1/c)), both by at most
219
+ // `ln 2` — see `geometry.ts`'s `consensusFloor`, where the bound lives.
220
+ // MEASURED on 8 anchors across 5 queries, running the same climb in all
221
+ // three modes: ZERO gate inversions — every anchor's `vote >= floor` verdict
222
+ // is the same in `inverse`, `direct` and `combined`, even where the readings
223
+ // straddle the floor on opposite sides (#148: inverse 3.39, combined 4.71
224
+ // above it, direct 1.31 below). Pinned by test/55's test 20. The bar is not
225
+ // re-derived for those modes because nothing reachable needs it; the premise
226
+ // is IDF, and that is now written where the gate reads it.
200
227
  //
201
228
  // Measured on the 15.7M-node store (N=325,615, so the old floor was 13.19).
202
229
  // The absolute vote cannot separate right from wrong at this scale, and the
@@ -265,7 +292,7 @@ export async function recallByResonance(ctx, query, pre) {
265
292
  const minVote = consensusFloor(corpusN(ctx));
266
293
  if (forest.length > 0 &&
267
294
  !allWindowsAreScaffolding(ctx, query) &&
268
- (forest[0].vote >= minVote ||
295
+ (forest[0].idfVote >= minVote || // the IDF sum: the bar's own quantity
269
296
  (dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))) {
270
297
  const g = await project(ctx, forest[0].anchor, queryGist);
271
298
  // THE ANCHOR'S OCCUPANT IS NOT THE ASKER'S. This tier grounds an anchor
@@ -288,7 +315,7 @@ export async function recallByResonance(ctx, query, pre) {
288
315
  // — never an answer (the same principle as `restates` above, extended
289
316
  // to fragments). Genuine anchor groundings — longer than the query,
290
317
  // or disjoint from it — pass untouched.
291
- else if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
318
+ else if (g && !lawRestates(query, g, 0, { proper: true })) {
292
319
  return ground(g, "scaffolding-dominated query — ground the consensus-climb anchor", [[forest[0].start, forest[0].end]], CONCEPT);
293
320
  }
294
321
  }
@@ -466,8 +493,8 @@ export const recallMechanism = {
466
493
  bytes: r.bytes,
467
494
  accounted: r.accounted,
468
495
  moves: r.moves,
469
- unexplained: r.unexplained,
470
496
  provenance: r.echoed ? "recall-echo" : "recall",
497
+ used: new Set(),
471
498
  ...(r.complete ? { complete: true } : {}),
472
499
  }];
473
500
  },
@@ -35,8 +35,8 @@
35
35
  // made AVAILABLE, never imposed.
36
36
  import { carriesFillers, distinct, follow, substituteAll } from "../match.js";
37
37
  import { dominates } from "../../geometry.js";
38
- import { bytesEqual, indexOf } from "../../bytes.js";
39
- import { unexplainedLabel } from "../rationale.js";
38
+ import { bytesEqual } from "../../bytes.js";
39
+ import { restates } from "../derivation.js";
40
40
  import { STEP } from "../graph-search.js";
41
41
  import { rItem, rNode, traceFail } from "../trace.js";
42
42
  /** The minimum number of instances that can establish a frame. One instance
@@ -196,7 +196,7 @@ export async function bindReference(ctx, query, pre) {
196
196
  return fail("the binding produced nothing");
197
197
  // Answering with the question is not answering — the same restated-fragment
198
198
  // guard every recall tier applies.
199
- if (bytes.length < query.length && indexOf(query, bytes, 0) >= 0) {
199
+ if (restates(query, bytes, 0, { proper: true })) {
200
200
  return fail("the binding restates part of the question");
201
201
  }
202
202
  const carried = !bytesEqual(bytes, first);
@@ -230,7 +230,6 @@ export async function bindReference(ctx, query, pre) {
230
230
  // binding claims strictly more than a one-slot binding, so where both are
231
231
  // licensed the smaller claim wins.
232
232
  moves: STEP * slots.length + STEP,
233
- unexplained: unexplainedLabel(query, accounted),
234
233
  // NOT scaffolding. That field counts answer bytes carried through BECAUSE
235
234
  // NOTHING EXPLAINED THEM; a referent is carried because the frame's slot
236
235
  // explains it, and it is accounted above. Reporting it would make every
@@ -75,6 +75,8 @@ export interface MindOptions {
75
75
  seed?: number;
76
76
  recallQueryK?: number;
77
77
  haloQueryK?: number;
78
+ /** Branch nodes the pivot sweep may probe — see {@link MindConfig}. */
79
+ pivotProbeK?: number;
78
80
  /** Items one rationale step may itemise — see {@link MindConfig}. */
79
81
  rationaleSampleK?: number;
80
82
  /** Corpus-reading capacities and budgets — see {@link MindConfig}. */
@@ -202,8 +204,8 @@ export declare class Mind implements MindContext {
202
204
  * with the most distributional evidence (highest `prevOf` count — the
203
205
  * structural manifestation of its halo). When evidence is equal the
204
206
  * first-inserted edge wins. */
205
- /** See {@link GraphSearchHost.contentCuts}. */
206
- contentCuts(bytes: Uint8Array): readonly number[];
207
+ /** See {@link GraphSearchHost.contentKeyEnds}. */
208
+ contentKeyEnds(prefix: Uint8Array, tail: Uint8Array): readonly number[];
207
209
  chooseNext(node: number): number | undefined;
208
210
  constructor(opts?: MindOptions);
209
211
  constructor(cfg: MindConfig, store: Store, _fromStore: true);
@@ -11,7 +11,8 @@
11
11
  import { makeKeyring, rng, setVecConfig } from "../vec.js";
12
12
  import { sampleCorpus, searchCorpus } from "./corpus.js";
13
13
  import { Alphabet } from "../alphabet.js";
14
- import { contentBoundaries, contentFoldIncremental, reachThreshold, } from "../geometry.js";
14
+ import { contentFoldIncremental, reachThreshold, } from "../geometry.js";
15
+ import { keyEnds } from "./canonical.js";
15
16
  import { BoundedMap } from "../store.js";
16
17
  import { SQliteStore } from "../store-sqlite.js";
17
18
  import { resolveConfig } from "../config.js";
@@ -160,9 +161,9 @@ export class Mind {
160
161
  * with the most distributional evidence (highest `prevOf` count — the
161
162
  * structural manifestation of its halo). When evidence is equal the
162
163
  * first-inserted edge wins. */
163
- /** See {@link GraphSearchHost.contentCuts}. */
164
- contentCuts(bytes) {
165
- return contentBoundaries(this.space, bytes);
164
+ /** See {@link GraphSearchHost.contentKeyEnds}. */
165
+ contentKeyEnds(prefix, tail) {
166
+ return keyEnds(this, prefix, tail);
166
167
  }
167
168
  chooseNext(node) {
168
169
  return chooseNext(this, node, this._edgeGuide);
@@ -151,10 +151,14 @@ export interface MechanismResult {
151
151
  bytes: Uint8Array;
152
152
  accounted: Array<[number, number]>;
153
153
  moves: number;
154
+ /** WHAT THIS ANSWER SPEAKS FOR — the anchors it voices, and therefore the
155
+ * content the reasoner must not pivot back through. Declared by the
156
+ * mechanism about its OWN result, exactly like `accounted`/`used`/
157
+ * `complete`: post-grounding honours the property and NEVER ASKS WHICH
158
+ * MECHANISM SET IT, so the market stays uniform. An EMPTY set is a real
159
+ * declaration — "this answer voices nothing" (recall) — and withholds
160
+ * nothing; omit the field and the pipeline re-recognises the answer. */
154
161
  used?: ReadonlySet<number>;
155
- unexplained: string;
156
- /** Explicit weight override. When absent, weight = moves + PASS·unaccounted. */
157
- weight?: number;
158
162
  /** Bytes of `bytes` that came from spans nothing recognised — the asker's
159
163
  * own words carried through verbatim rather than derived (see
160
164
  * {@link liftedScaffolding}). Reported, not priced: the ladder prices what