@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
@@ -75,6 +75,10 @@ export interface ConsensusAnchorTrace {
75
75
  rank: number;
76
76
  pooledVote: number;
77
77
  idfVote: number;
78
+ /** The LARGEST single-region contribution behind this anchor — the bar
79
+ * recall's own gate reads (mechanisms/recall.ts). Published so the one
80
+ * decision-making quantity the climb computes is not invisible. */
81
+ peak: number;
78
82
  candidateBreadth: number;
79
83
  contributingVotes: number;
80
84
  contributingEvidence: number;
@@ -15,6 +15,7 @@ import { leafIdRun } from "./canonical.js";
15
15
  import { atomIsHub, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "./traverse.js";
16
16
  import { cachedRead, junctionContainersFrom, junctionSeeds, junctionSynonyms, loadJunctionSynonymSides, walkCache, } from "./junction.js";
17
17
  import { indexOf } from "../bytes.js";
18
+ import { restates } from "./derivation.js";
18
19
  import { rItem, rNode, traceDerivation } from "./trace.js";
19
20
  function newTraceDraft(perceivedCount) {
20
21
  return {
@@ -775,15 +776,18 @@ export async function voteRegions(ctx, query, regions, k, mode, N, reachMemo, td
775
776
  }
776
777
  contrastiveMargin = margin;
777
778
  // Scaled by what this region does NOT address — see `cov` above.
778
- const noiseFloor = estimatorNoise(ctx.store.D) * (1 - cov);
779
- if (margin <= noiseFloor) {
779
+ // The bar THIS gate applies: the estimator's noise scaled by what the
780
+ // region does NOT address (`cov`). ONE definition, used by the rejection
781
+ // path below and by the voted payload — the trace reports the applied bar.
782
+ const appliedFloor = estimatorNoise(ctx.store.D) * (1 - cov);
783
+ if (margin <= appliedFloor) {
780
784
  recordRegion("contrastive-margin-rejection", {
781
785
  selected,
782
786
  reachNode: voterId,
783
787
  idf,
784
788
  dfWeight: wf,
785
789
  contrastiveMargin: margin,
786
- contrastiveNoiseFloor: noiseFloor,
790
+ contrastiveNoiseFloor: appliedFloor,
787
791
  ...(contrastiveRival ? { contrastiveRival } : {}),
788
792
  });
789
793
  continue;
@@ -834,7 +838,12 @@ export async function voteRegions(ctx, query, regions, k, mode, N, reachMemo, td
834
838
  ...(contrastiveMargin !== undefined
835
839
  ? {
836
840
  contrastiveMargin,
837
- contrastiveNoiseFloor: estimatorNoise(ctx.store.D),
841
+ // THE BAR THE GATE ACTUALLY APPLIED — the same expression
842
+ // the rejection path's `appliedFloor` defines, inline here because
843
+ // this payload is built in a scope that does not carry that local.
844
+ // Publishing the raw estimatorNoise(D) instead made a region that
845
+ // PASSED look closer to its limit than it was.
846
+ contrastiveNoiseFloor: estimatorNoise(ctx.store.D) * (1 - cov),
838
847
  ...(contrastiveRival ? { contrastiveRival } : {}),
839
848
  }
840
849
  : {}),
@@ -939,7 +948,17 @@ export function poolVotes(ctx, regionVotes, sat, N, td) {
939
948
  },
940
949
  pool,
941
950
  };
942
- lightestDerivation(system);
951
+ // THE SEARCH WAS THE ONE LAYER WITH NO TIME. The climb's phases are timed
952
+ // (voteRegions, structuralResonance, crossRegion) but the pooled derivation
953
+ // was not, so any cost or gain inside it stayed invisible.
954
+ // `timeSync`, not `time`: the search is SYNCHRONOUS, and wrapping it in a
955
+ // promise only to time it would make the profiled path wait where the
956
+ // unprofiled one does not (meter.ts's own contract).
957
+ if (ctx.meter) {
958
+ ctx.meter.timeSync("climb.derivation", () => lightestDerivation(system));
959
+ }
960
+ else
961
+ lightestDerivation(system);
943
962
  const votes = new Map();
944
963
  const votesIdf = new Map();
945
964
  const support = new Map();
@@ -973,12 +992,24 @@ export function poolVotes(ctx, regionVotes, sat, N, td) {
973
992
  // The LARGEST single region's contribution to this anchor's pooled vote.
974
993
  // The pool is a SUM (deliberately — see the pooling note above), so it says
975
994
  // how much evidence there is in total, never whether any ONE place in the
976
- // query carries evidence on its own. Consumers that hold an anchor to
977
- // consensusFloor(N) = ln(N) + 1/2 need the latter: that bar prices ONE
978
- // region's maximally-discriminative evidence (ln N is the IDF of content
979
- // reaching a single context), so comparing a six-region sum against it is a
980
- // dimensional error. Recorded here, beside the count, because this is the
981
- // only place the per-region contributions are still separable.
995
+ // query carries evidence on its own. Recorded here, beside the count,
996
+ // because this is the only place the per-region contributions are still
997
+ // separable.
998
+ //
999
+ // THE BAR IS THE POOLED FLOOR, AND IT WAS ONCE CLAIMED OTHERWISE HERE.
1000
+ // This comment used to say that holding an anchor to consensusFloor(N)
1001
+ // "prices ONE region's evidence", so comparing a six-region sum against it
1002
+ // was "a dimensional error". THAT WAS FALSE. `thresholds.md` §2 derives
1003
+ // `consensusFloor` as the POOLED-vote significance floor ("each region
1004
+ // contributes at most ln(N/c) <= ln(N); ln(N) + 1/2 demands ..."), and the
1005
+ // climb weights by IDF, so the sum and the floor are in ONE dimension —
1006
+ // which is exactly why `recall.ts` gates `forest[0].idfVote` against it and
1007
+ // why `commitVotes` does too. The other two weighting modes DO leave that
1008
+ // dimension (by at most ln 2, two-sided: `direct` deflates a region and
1009
+ // `combined` inflates it), and the gates therefore read the IDF sum, which
1010
+ // is mode-independent; `test/55` tests 19 and 20 pin both halves — the sum
1011
+ // as the reading the bar is derived for, and the absence of any gate
1012
+ // inversion across the three modes.
982
1013
  const regionPeak = new Map();
983
1014
  const steps = [];
984
1015
  let order = 0;
@@ -1136,12 +1167,23 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
1136
1167
  anchor,
1137
1168
  vote,
1138
1169
  peak: regionPeak.get(anchor) ?? 0,
1170
+ idfVote: votesIdf.get(anchor) ?? 0,
1139
1171
  start: s.start,
1140
1172
  end: s.end,
1141
1173
  breadth: (regionSupport.get(anchor) ?? 0) / totalRegions,
1142
1174
  clusters: countClusters(regionSpans.get(anchor) ?? [], ctx.space.maxGroup),
1143
1175
  };
1144
1176
  })
1177
+ // THE ORDER IS NOT A PREFERENCE: with equal evidence it decides ADMISSION,
1178
+ // through the stable sort and the first-come overlap absorption below.
1179
+ // Measured on test/34's corpus, query "red": the two candidates (`red
1180
+ // circle` and `red square`) carry IDENTICAL `vote` and IDENTICAL `idfVote`
1181
+ // (1.3863 each, three seeds), so this comparator leaves them tied and the
1182
+ // stable sort keeps the ENUMERATION order — which is corpus-determined and
1183
+ // admits `red square`, 60/60 seeds. Adding an id tie-break (`|| a.anchor -
1184
+ // b.anchor`) picks `red circle` instead and makes a single region reach the
1185
+ // JOINT context, which is the premise `test/34` exists to protect. The
1186
+ // gates read IDF; this line only decides who gets looked at first.
1145
1187
  .sort((a, b) => b.vote - a.vote);
1146
1188
  const overlaps = (a, b) => a.start < b.end && b.start < a.end;
1147
1189
  // Read the root cut from the anchors the QUERY pointed at. A vote standing
@@ -1177,6 +1219,13 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
1177
1219
  rank,
1178
1220
  pooledVote: point.vote,
1179
1221
  idfVote: votesIdf.get(point.anchor) ?? 0,
1222
+ // The LARGEST single-region contribution behind this anchor — the bar
1223
+ // recall's own gate reads (mechanisms/recall.ts: forest[0].peak > LN2),
1224
+ // and until now the only decision-making quantity the climb computed and
1225
+ // did not publish. `regionPeak` reached `ranked` (see its build below)
1226
+ // and stopped there. Published, not recomputed: the value is the one the
1227
+ // climb already carries.
1228
+ peak: point.peak,
1180
1229
  candidateBreadth: regions.length,
1181
1230
  contributingVotes: regionAxioms.get(point.anchor) ?? 0,
1182
1231
  contributingEvidence: regionSupport.get(point.anchor) ?? 0,
@@ -1206,6 +1255,18 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
1206
1255
  let passesConsensusFloor;
1207
1256
  let pastLeadingSaturation;
1208
1257
  let tiedWithDominant;
1258
+ // ── ONE OF THREE ADMISSIONS, AND THEY ARE NOT THE SAME READING ────────
1259
+ // This block admits by VOTES: per-region evidence pooled, gated on the
1260
+ // natural break and on consensusFloor, with the dominant allowed to bypass
1261
+ // both. `structuralResonance` admits by a MARGIN over the estimator's own
1262
+ // noise, and `crossRegionVotes` admits by STRUCTURE (which regions may pair
1263
+ // at all, with at least one side individually discriminative). Read
1264
+ // together they look like one policy written three times; they are three
1265
+ // different measurements of the same question ("is this evidence?"), and
1266
+ // unifying them would average three readings into one — the mistake
1267
+ // `extraction.ts` records as "do not unify the two into one machine".
1268
+ // What they DO share, and must keep sharing, is the discipline of deriving
1269
+ // every bar from D/W/N rather than choosing it (thresholds.md).
1209
1270
  const rejectionReasons = [];
1210
1271
  if (absorbed) {
1211
1272
  status = "overlap";
@@ -1216,9 +1277,75 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
1216
1277
  pastLeadingSaturation = pastLeading;
1217
1278
  const vote = votesIdf.get(point.anchor) ?? 0;
1218
1279
  if (roots.length === 0) {
1219
- // The first non-overlapping root is DOMINANT and bypasses the two
1220
- // vote thresholds (it always grounds) — only the leading-saturation
1221
- // gate still applies to it.
1280
+ // THE DOMINANCE PRIVILEGE, AND THE TENSION IT CARRIES (measured).
1281
+ //
1282
+ // The first non-overlapping candidate is DOMINANT: it bypasses both
1283
+ // vote gates below and grounds on its own; only the leading-saturation
1284
+ // gate still applies to it. The privilege is load-bearing — analogies,
1285
+ // substitutions and composed contexts are precisely candidates the
1286
+ // query does NOT contain, and the engine loses them without it.
1287
+ //
1288
+ // WHICH candidate receives it, though, is decided by this loop's ORDER.
1289
+ // That order comes from `ranked`, and when two candidates carry equal
1290
+ // evidence the stable sort preserves the ENUMERATION order, so the
1291
+ // privilege is allocated by an ordering rather than by a rule.
1292
+ //
1293
+ // Measured on test/34's corpus, query "red":
1294
+ //
1295
+ // 0:#77 vote=1.3863 idf=1.3863 [0,3) | 1:#49 vote=1.3863 idf=1.3863 [0,3)
1296
+ //
1297
+ // Both candidates (`red square` #77, `red circle` #49) have IDENTICAL
1298
+ // `vote` AND IDENTICAL `idfVote` over the SAME support span, so the
1299
+ // comparator leaves them tied, the second is absorbed as "overlap", and
1300
+ // the first grounds. With this build's enumeration order that first is
1301
+ // `red square`, 60/60 seeds, and `red circle` — the JOINT context — is
1302
+ // never reached by "red" alone. That is the premise test/34 exists to
1303
+ // protect: no single region reaches the joint context, which is what
1304
+ // makes the binding query unreachable without direct region
1305
+ // interaction.
1306
+ //
1307
+ // THE TENSION: the premise therefore holds BY ENUMERATION ORDER, not by
1308
+ // a rule, so any change to this ordering can move the privilege onto the
1309
+ // joint context and let one region reach it. Measured: adding
1310
+ // `|| a.anchor - b.anchor` — the lowest-id tie-break that AGENTS.md §2
1311
+ // sanctions as an equivalent corpus-determined tie-break — does exactly
1312
+ // that: "red" then attends to `red circle`, test/34 fails 6/1, and the
1313
+ // canonical suite reports 1 failure.
1314
+ //
1315
+ // TWO ATTEMPTS TO MAKE IT A RULE, BOTH REFUTED BY MEASUREMENT:
1316
+ //
1317
+ // 1. EVIDENCE SEPARATION. Grant the privilege only when the first
1318
+ // candidate's evidence is separated from the next distinct
1319
+ // candidate's by more than the co-dominant band (sqrt(k) *
1320
+ // estimatorNoise(D)). Refuted: that band exists to ADMIT the
1321
+ // anchors the estimator cannot separate from the dominant — its own
1322
+ // documented purpose — so withholding the privilege on ties removes
1323
+ // the very case it was written for. Suite: 4 failures (the two
1324
+ // co-dominant band laws, breadth/scale invariance, test/29 D2).
1325
+ //
1326
+ // 2. QUERY-OWNED CONTENT. Grant the privilege only to a candidate
1327
+ // that IS a recognised region's identity (regions.some(r => r.id ===
1328
+ // point.anchor)). Measured: for "circle" that identity IS the
1329
+ // ranked candidate, so the privilege stays and `circle` grounds; for
1330
+ // "red" the identity is the `red` node itself while the candidates
1331
+ // are the conjunctions, so neither is privileged; for "red then
1332
+ // circle" the composed context carries idf 3.958 and clears both
1333
+ // gates on its own evidence. All four control queries came out
1334
+ // right — and the suite: 10 failures, six of them in the
1335
+ // analogy/counterfactual/CAST suites ("an analogy still transfers
1336
+ // from a structure the query never names"; "a substitute the query
1337
+ // NAMES may still be voiced"). Refuted: the privilege exists to
1338
+ // admit what the query does NOT contain, so identity is the wrong
1339
+ // axis.
1340
+ //
1341
+ // WHAT A FUTURE ATTEMPT MUST RESPECT: whatever allocates this privilege
1342
+ // has to (a) keep it available to candidates the query does not contain
1343
+ // — analogies, substitutions, compositions — and (b) not depend on the
1344
+ // estimator's ordering among anchors of equal evidence, because that
1345
+ // ordering is not a fact about the corpus. No lever satisfying both has
1346
+ // been found. Until one is, this premise rests on the enumeration order
1347
+ // recorded above, and test/34 is the only test that notices if it
1348
+ // moves.
1222
1349
  dominant = true;
1223
1350
  if (pastLeading) {
1224
1351
  status = "root";
@@ -1229,8 +1356,15 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
1229
1356
  }
1230
1357
  }
1231
1358
  else {
1232
- passesNaturalBreak = vote >= rootCut;
1233
- passesConsensusFloor = vote >= floor;
1359
+ // THE FLOOR AND THE BREAK READ THE IDF WEIGHTING. `floor` is derived
1360
+ // for pooled IDF-weighted votes, and `rootCut` comes from the IDF
1361
+ // distribution (`idfDesc`), so gating the mode-dependent `vote` against
1362
+ // either let a weighting mode change an admission (measured: anchor 87,
1363
+ // inverse 2.682 admitted vs direct 1.468 refused). Reading the IDF sum
1364
+ // makes the verdict mode-independent, and changes nothing in the
1365
+ // engine's own mode, where the two readings coincide.
1366
+ passesNaturalBreak = point.idfVote >= rootCut;
1367
+ passesConsensusFloor = point.idfVote >= floor;
1234
1368
  // CO-DOMINANT — an anchor the estimator cannot separate from the
1235
1369
  // dominant inherits the dominant's exemption, because that exemption's
1236
1370
  // only warrant is being TOP, and "top" is not a fact about the corpus
@@ -1685,6 +1819,13 @@ ownRootsA, ownRootsB, trace) {
1685
1819
  outcome,
1686
1820
  });
1687
1821
  };
1822
+ // ── ADMISSION BY MARGIN, not by votes (see voteRegions' note) ─────────
1823
+ // What this site measures: how far the best ANN proposal's effective score
1824
+ // (score × semanticConfidence) stands above the runner-up's, against
1825
+ // `estimatorNoise(D)`. What it does NOT measure: how many regions voted,
1826
+ // or whether the query's regions agree — that is voteRegions' question, and
1827
+ // here a synthetic gist has already replaced them. The two bars are both
1828
+ // derived (thresholds.md), and neither is a tuning of the other.
1688
1829
  let selected = null;
1689
1830
  let selectedReach = null;
1690
1831
  let selectedIdf = 0;
@@ -1790,6 +1931,15 @@ async function crossRegionVotes(ctx, query, regions, rvs, k, N, reachMemo, td) {
1790
1931
  // successfully reconstructed while probing one pair must not be read and
1791
1932
  // perceived again while probing another pair in the same climb.
1792
1933
  const siblingGistMemo = new Map();
1934
+ // ── ADMISSION BY STRUCTURE, not by a bar (see voteRegions' note) ──────
1935
+ // What this site decides: WHICH regions may pair at all — a region that
1936
+ // already voted (individually discriminative), or a KNOWN non-voting one as
1937
+ // the weak side of a pair whose other side voted; never two non-voting
1938
+ // regions, and never a span contained in a maximal one whose reading is
1939
+ // exact. The bar (the container's idf) comes later, on the candidate. So
1940
+ // its "rejection reasons" name structural disqualifications — a different
1941
+ // vocabulary because it answers a different question, and the three
1942
+ // taxonomies stay separate for the same reason the readings do.
1793
1943
  const votedSpans = new Set();
1794
1944
  for (const rv of rvs.votes)
1795
1945
  votedSpans.add(`${rv.start},${rv.end}`);
@@ -2130,7 +2280,7 @@ async function crossRegionVotes(ctx, query, regions, rvs, k, N, reachMemo, td) {
2130
2280
  const ri = indexOf(bytes, right, 0);
2131
2281
  if (li >= 0 && ri >= 0) {
2132
2282
  const joined = bytes.subarray(Math.min(li, ri), Math.max(li + left.length, ri + right.length));
2133
- if (indexOf(query, joined, 0) >= 0) {
2283
+ if (restates(query, joined, 0)) {
2134
2284
  if (structuralTrace)
2135
2285
  structuralTrace.selfEvidenceRejected++;
2136
2286
  continue; // query says it itself
@@ -23,6 +23,22 @@ export declare function leafIdRun(ctx: MindContext, bytes: Uint8Array, from: num
23
23
  * what a partial prefix means (deposit only interns the whole-stream flat
24
24
  * branch when the prefix covers everything). */
25
25
  export declare function leafIdPrefix(ctx: MindContext, bytes: Uint8Array): number[];
26
+ /** Which prefixes of `prefix ‖ tail` are STORED NODES — as lengths in the
27
+ * tail's own coordinates, ascending, excluding the empty one. This is the
28
+ * candidate set a rule needs to join an already-stored prefix to a suffix it
29
+ * has not stored: a key names a relation exactly when `prefix ‖ tail[0..p]` IS
30
+ * a node, and a key can end strictly inside the tail without sitting on any
31
+ * fold boundary (a stored member's end is the end of ITS OWN stream, and the
32
+ * fold never emits a cut at a stream's end). Measured: "stockholm mayor"
33
+ * exists, leads on, and its boundary 6 is in neither the tail's cuts nor the
34
+ * concatenation's.
35
+ *
36
+ * ONE cheap content-addressed probe per offset — `leafIdPrefix` walks the bytes
37
+ * once (a point probe each), `findBranch` hashes the growing kid run — and NO
38
+ * `resolve`, which is what keeps this off the O(suffix) vector folds the
39
+ * recognition path pays. It stops at the first byte that was never interned,
40
+ * which costs nothing real: a stored key's bytes are interned by construction. */
41
+ export declare function keyEnds(ctx: MindContext, prefix: Uint8Array, tail: Uint8Array): number[];
26
42
  /** The canonical W-window node ids of a byte stream, offset → id — the
27
43
  * CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
28
44
  * two deposits share IS the same node (hash-consing paid the comparison at
@@ -70,6 +70,47 @@ export function leafIdPrefix(ctx, bytes) {
70
70
  }
71
71
  return ids;
72
72
  }
73
+ /** Which prefixes of `prefix ‖ tail` are STORED NODES — as lengths in the
74
+ * tail's own coordinates, ascending, excluding the empty one. This is the
75
+ * candidate set a rule needs to join an already-stored prefix to a suffix it
76
+ * has not stored: a key names a relation exactly when `prefix ‖ tail[0..p]` IS
77
+ * a node, and a key can end strictly inside the tail without sitting on any
78
+ * fold boundary (a stored member's end is the end of ITS OWN stream, and the
79
+ * fold never emits a cut at a stream's end). Measured: "stockholm mayor"
80
+ * exists, leads on, and its boundary 6 is in neither the tail's cuts nor the
81
+ * concatenation's.
82
+ *
83
+ * ONE cheap content-addressed probe per offset — `leafIdPrefix` walks the bytes
84
+ * once (a point probe each), `findBranch` hashes the growing kid run — and NO
85
+ * `resolve`, which is what keeps this off the O(suffix) vector folds the
86
+ * recognition path pays. It stops at the first byte that was never interned,
87
+ * which costs nothing real: a stored key's bytes are interned by construction. */
88
+ export function keyEnds(ctx, prefix, tail) {
89
+ if (prefix.length === 0 || tail.length === 0)
90
+ return [];
91
+ const joined = new Uint8Array(prefix.length + tail.length);
92
+ joined.set(prefix, 0);
93
+ joined.set(tail, prefix.length);
94
+ const ids = leafIdPrefix(ctx, joined);
95
+ if (ids.length < prefix.length)
96
+ return [];
97
+ const ends = [];
98
+ // The kid run GROWS by one id per offset; `findBranch` wants an array, so the
99
+ // run is built once and pushed into, never re-sliced. Re-slicing
100
+ // `ids.slice(0, prefix.length + p)` per offset made this O(|tail| ·
101
+ // (|prefix| + |tail|)) — quadratic in the tail, where the learning path this
102
+ // follows slices a run that SHRINKS. Same ends, linear copying.
103
+ const run = ids.slice(0, prefix.length);
104
+ // The loop ENDS at the first byte that was never interned (`ids.length`):
105
+ // every later prefix contains it, so none of them can be a node either — this
106
+ // is where the scan stops, not a silent truncation of the answer.
107
+ for (let p = 1; prefix.length + p <= ids.length; p++) {
108
+ run.push(ids[prefix.length + p - 1]);
109
+ if (ctx.store.findBranch(run) !== null)
110
+ ends.push(p);
111
+ }
112
+ return ends;
113
+ }
73
114
  /** The canonical W-window node ids of a byte stream, offset → id — the
74
115
  * CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
75
116
  * two deposits share IS the same node (hash-consing paid the comparison at
@@ -0,0 +1,201 @@
1
+ /** A half-open `[start, end)` span of the asker's own bytes. */
2
+ export type Span = readonly [number, number];
3
+ /** The BYTE COUNT of a span list — what the currency calls `unaccounted` in
4
+ * `weight = moves + PASS·unaccounted`. ONE definition: this was four copies of
5
+ * the same `reduce` before the architecture audit collapsed them. */
6
+ export declare function unaccountedBytes(spans: ReadonlyArray<Span>): number;
7
+ /** The `[start, end)` gaps of `[0, queryLen)` NOT covered by `accounted` — the
8
+ * union-of-spans reading the ladder prices at PASS per byte, and the raw
9
+ * material every closure decision measures. Clipped to the question, sorted
10
+ * and merged, so two overlapping spans account for their union once. */
11
+ export declare function unexplainedSpans(queryLen: number, accounted: ReadonlyArray<Span>): Array<[number, number]>;
12
+ /** THE REMAINDER: what no step has accounted for, with every span below one
13
+ * river-fold quantum dropped. `W` is the mind's own line between bridging
14
+ * punctuation and a substantive phrase — the same floor `liftedScaffolding`
15
+ * and the honesty-density bar use — so a remainder under it licenses nothing
16
+ * and blocks nothing. This is the law's measure. */
17
+ export declare function remainderOf(queryLen: number, explained: ReadonlyArray<Span>, W: number): Array<[number, number]>;
18
+ /** A WITNESS — what a transition carries, in one reading: the SPAN it accounts
19
+ * for, and the WINDOW of the question the product itself holds. The window is
20
+ * present only when the product holds question material, and it is the ONLY
21
+ * thing the question's remainder may be consumed by. */
22
+ export interface Witness {
23
+ readonly span: Span;
24
+ readonly window?: Span;
25
+ }
26
+ /** The window of `span` that `product` holds, or null: the ONE reading of
27
+ * coverage — used by {@link carries}, by the move branch, and by the GROUNDING
28
+ * when it decides what its answer has actually paid for. */
29
+ export declare function windowOf(span: Span, product: Uint8Array, query: Uint8Array, W: number): Span | null;
30
+ /** PROGRESS, by coverage: the first member of `remainder` that `product` carries
31
+ * a whole quantum of, or null when it carries none. The window is taken from
32
+ * `query`, the asker's own bytes, so the test is "this product restates a
33
+ * quantum of what was left unaccounted", never a similarity score.
34
+ *
35
+ * One witness per step, deterministically the FIRST in remainder order: the
36
+ * measure stays a single member of a finite list, which is what makes the walk
37
+ * reviewable — and, because the remainder is not drained, it is the walker's
38
+ * cycle protection (not this) that terminates a chain. */
39
+ export declare function carries(remainder: ReadonlyArray<Span>, product: Uint8Array, query: Uint8Array, W: number): Array<Witness> | null;
40
+ /** Whether `bytes` RESTATES the question — says nothing the asker did not just
41
+ * say — and is therefore not an answer. This is a closure condition: a
42
+ * derivation whose product is already the question has added nothing, and the
43
+ * engine asks it in five places. ONE definition, asked everywhere, with the
44
+ * DIFFERENCES between those places supplied as WITNESSES by the caller — never
45
+ * as a mechanism or a producer. If this function ever needs to know who
46
+ * produced the bytes to decide, the right conclusion is that a witness is
47
+ * missing, not that it should dispatch.
48
+ *
49
+ * `floor` is one river-fold quantum: below it, byte overlap is chance, not
50
+ * evidence — the same line `identityBar`, the bridge's `attestedQ` and
51
+ * recognition's site floor all draw. `0` disables the floor, which is the
52
+ * reading the callers that ask before any structure exists use.
53
+ *
54
+ * THE THREE READINGS the callers need, and why each is a witness rather than a
55
+ * branch here:
56
+ *
57
+ * • `proper` — a PROPER part of the question (strictly shorter). This is the
58
+ * reading every tier that rejects a fragment uses.
59
+ * • `whole` — the EQUALITY reading: only "the answer IS the question" counts,
60
+ * because the caller has already handled a proper fragment elsewhere (a
61
+ * recall tier's own subspan tests).
62
+ * • `equate` — the response's own notion of "the same text" (whatever
63
+ * `src/canon.ts` equates: case, width, whitespace). A caller that has one
64
+ * passes it; a caller that does not gets the byte-exact reading. It is the
65
+ * same fallback `resolve` already makes when an exact lookup misses.
66
+ *
67
+ * The LITERAL EXEMPTION is deliberately NOT here: whether a span is the site's
68
+ * own bytes at its own position is the CALLER's knowledge, and a caller states
69
+ * it by not asking (see `segRestatesQuery` in types.ts, which returns false for
70
+ * a literal span before reaching this). */
71
+ export declare function restates(query: Uint8Array, bytes: Uint8Array, floor?: number, witnesses?: {
72
+ equate?: ((b: Uint8Array) => Uint8Array) | null;
73
+ proper?: boolean;
74
+ whole?: boolean;
75
+ /** THE POSITIONAL WITNESS: search from this offset, because the caller has
76
+ * established that only material at or after it counts. A transcript pasted
77
+ * into a single response is the case that needs it — a caller's own prior
78
+ * answer lies LATER in the query, after the root that would restate it — and
79
+ * the reasoner's per-root `alreadyAnswered` guard asks exactly that question.
80
+ * Omitted, the search starts at 0 and the reading is the plain one. */
81
+ from?: number;
82
+ }): boolean;
83
+ /** Whether the query span `[from, to)` lies inside a COMPLETED ASSISTANT TURN —
84
+ * material the engine has already produced, so it is context rather than
85
+ * something the asker is asserting. Recognition and attention still see the
86
+ * full transcript; what excludes these spans is the closure reading "this was
87
+ * already answered", and it is a closure reading rather than a budget: a window
88
+ * inside a prior reply is not a fresh constraint.
89
+ *
90
+ * ONE definition of it. `cursor` is the CALLER's own progress through `turns`
91
+ * (they are ascending and each caller scans its candidates in ascending order),
92
+ * so the amortised search is preserved exactly and a caller passes the same
93
+ * holder for a whole scan: extracting the reading must not cost the scan. */
94
+ export declare function insideAnsweredTurn(turns: ReadonlyArray<Span>, cursor: {
95
+ at: number;
96
+ }, from: number, to: number): boolean;
97
+ /** THE derivation state — the unit that crosses one inference.
98
+ *
99
+ * Every field is read by the law or by the market's one cost ladder, and
100
+ * nothing else travels. A count of steps is a consequence (the cost), and
101
+ * cycle protection belongs to the layer that walks a graph. */
102
+ export interface DerivationState {
103
+ /** PRODUCT — the structure produced: what this derivation stands on. */
104
+ readonly product: Uint8Array;
105
+ /** ACCOUNTED — the asker's spans the producing transition priced. A COST
106
+ * quantity, and the producing mechanism's own judgement of what its answer
107
+ * explains: cover leaves its computed spans out so the PASS-bridged bytes
108
+ * they account for stay charged, while a corroborated substitution DOES
109
+ * account for its span, because the mechanism paid a move for it. */
110
+ readonly accounted: ReadonlyArray<Span>;
111
+ /** REMAINDER — the asker's material no step has accounted for, each member at
112
+ * or above one quantum. Empty means the derivation is CLOSED. */
113
+ readonly remainder: ReadonlyArray<Span>;
114
+ /** COST — position on the one ladder (`graph-search.ts`'s MICRO/STEP/CONCEPT/
115
+ * PASS); the market takes the lattice minimum over it. */
116
+ readonly cost: number;
117
+ /** FIXED — the producer SUPPLIED a fixed point: the query IS the context, so
118
+ * no transition may consume this state. Declared, never inferred. */
119
+ readonly fixed?: boolean;
120
+ /** USED — what the product speaks for. An EMPTY set is itself a declaration
121
+ * ("this answer voices nothing"); omitted means the layer must re-recognise
122
+ * the product to decide for itself. */
123
+ readonly used?: ReadonlySet<number>;
124
+ }
125
+ /** A candidate continuation, as reported by the layer that knows the structure.
126
+ * The layer says what it has; the law decides. */
127
+ export interface Continuation {
128
+ /** The structure the transition would make the derivation's product. */
129
+ readonly product: Uint8Array;
130
+ /** CONTAINS — the transition's structure holds the product: a node in its
131
+ * tree, or one contiguous byte run of it. Resolved by the reporter.
132
+ *
133
+ * SUFFICIENT FOR EVERY DECISION THIS CORE MAKES, and a boolean is the minimum:
134
+ * the law reads it ONCE, as the admission gate, and that decision is binary —
135
+ * may this state be consumed by this transition at all? Every other decision
136
+ * is fed by other witnesses, never by this one: the product's identity is
137
+ * `resolve(product)`, progress is the window a move carries or the
138
+ * `reaches` declaration, accounting is the span. Carrying the reporter's
139
+ * structure here would therefore be a dump of mechanism internals bought for
140
+ * nothing. The producers make it true by construction — a continuation is
141
+ * built from the CURRENT product's own structure, never from a different one
142
+ * — and test/133 pins the refusal when a reporter declares false. */
143
+ readonly contains: boolean;
144
+ /** REACHES — the transition MOVES: it reaches structure this derivation has
145
+ * not consumed
146
+ * (a node outside the walker's own set). The second species of progress: a
147
+ * step need not excuse itself with question material when it moves to new
148
+ * structure. Resolved by the reporter, declared by the transition — never
149
+ * inferred from its producer. */
150
+ readonly reaches?: boolean;
151
+ /** What the transition accounts for, when it declares it. A transition
152
+ * taken from a CLOSED state has nothing to progress on, so it is the one
153
+ * case that must say what it accounts for; a transition that carries the
154
+ * remainder declares nothing and the law's own witness is used. */
155
+ readonly explains?: ReadonlyArray<Span>;
156
+ /** The transition's own moves, in ladder units. */
157
+ readonly cost: number;
158
+ }
159
+ /** CLOSED — nothing of the asker's material is left unaccounted. */
160
+ export declare function closed(d: DerivationState): boolean;
161
+ /** THE LAW, evaluated once.
162
+ *
163
+ * Returns the spans the transition accounts for — the witness that lets it be
164
+ * taken — or `null` when it is inadmissible. Evaluating it once and advancing
165
+ * the state with {@link advance} is the whole of a transition; asking twice for
166
+ * the same pair would repeat the scan, which this module must not make anyone
167
+ * do.
168
+ *
169
+ * ¬FIXED ∧ CONTAINS ∧ ( CLOSED ∨ CARRIES ∨ MOVES )
170
+ *
171
+ * Cost is not a term: it is the lattice order the market minimises over, and
172
+ * neither is any budget — a cap decides with a number of work, this decides
173
+ * with the remainder. */
174
+ export declare function admissible(d: DerivationState, t: Continuation, query: Uint8Array, W: number): ReadonlyArray<Witness> | null;
175
+ export declare function advance(d: DerivationState, t: Continuation, explains: ReadonlyArray<Witness>): DerivationState;
176
+ /** What a layer offers the law: the next continuation of a state, or null when
177
+ * it has none. A layer OFFERS; the law disposes.
178
+ *
179
+ * ONE OFFER, AND IT IS THE LAYER'S LAST: {@link closure} stops when the law
180
+ * refuses what was offered, so a refusal is read as "no continuation exists".
181
+ * A layer must not offer candidates one at a time and expect the walk to
182
+ * continue after a refusal — its own fallbacks belong inside this function.
183
+ * The producers do exactly that: they choose between the forward absorb and the
184
+ * pivot before offering, and return null only when neither exists, which is why
185
+ * the one offer the law can still refuse (a pivot without ownership, whose
186
+ * material the answer does not carry) really is the last one. */
187
+ export type Offer = (d: DerivationState) => Promise<Continuation | null>;
188
+ /** THE CLOSURE — the walk of {@link advance} over the continuations `offer`
189
+ * proposes, run until the layer has nothing further to offer or the law refuses
190
+ * the one it offered.
191
+ *
192
+ * ADMISSION is entirely the law's; TERMINATION is the layer's, and deliberately
193
+ * so. The remainder does not descend (draining it was implemented and refuted
194
+ * — see the module note), so a walk cannot run forever only because the layer
195
+ * offering continuations keeps its own cycle protection over a finite graph.
196
+ * Nothing here counts steps, and nothing here decides admissibility. */
197
+ export declare function closure(d: DerivationState, query: Uint8Array, W: number, offer: Offer,
198
+ /** Called for each step the law admits, with the state before and after. */
199
+ onTaken?: (before: DerivationState, after: DerivationState) => void,
200
+ /** Called when the law refused the continuation the layer offered. */
201
+ onRefused?: (at: DerivationState) => void): Promise<DerivationState>;