@hviana/sema 0.7.9 → 0.8.1

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 (86) hide show
  1. package/AGENTS.md +22 -1
  2. package/DATASETS.md +1 -1
  3. package/dist/example/train_base/config.js +2 -2
  4. package/dist/example/train_base/corpora/massive.js +1 -1
  5. package/dist/example/train_base/readers.js +1 -1
  6. package/dist/src/geometry.d.ts +10 -10
  7. package/dist/src/geometry.js +25 -24
  8. package/dist/src/meter.d.ts +4 -12
  9. package/dist/src/meter.js +14 -14
  10. package/dist/src/mind/attention.js +12 -12
  11. package/dist/src/mind/bridge.d.ts +8 -8
  12. package/dist/src/mind/bridge.js +33 -32
  13. package/dist/src/mind/graph-search.d.ts +0 -8
  14. package/dist/src/mind/graph-search.js +38 -25
  15. package/dist/src/mind/junction.d.ts +1 -1
  16. package/dist/src/mind/junction.js +8 -8
  17. package/dist/src/mind/learning.js +36 -35
  18. package/dist/src/mind/match.js +14 -13
  19. package/dist/src/mind/mechanisms/cover.js +13 -12
  20. package/dist/src/mind/mechanisms/prefix-completion.js +24 -24
  21. package/dist/src/mind/mechanisms/recall.js +38 -40
  22. package/dist/src/mind/mechanisms/reference.js +16 -16
  23. package/dist/src/mind/mind.d.ts +6 -7
  24. package/dist/src/mind/pipeline-mechanism.d.ts +10 -8
  25. package/dist/src/mind/pipeline-mechanism.js +25 -21
  26. package/dist/src/mind/pipeline.d.ts +9 -9
  27. package/dist/src/mind/pipeline.js +24 -23
  28. package/dist/src/mind/primitives.d.ts +5 -5
  29. package/dist/src/mind/primitives.js +5 -5
  30. package/dist/src/mind/recognition.d.ts +14 -13
  31. package/dist/src/mind/recognition.js +53 -38
  32. package/dist/src/mind/resonance.js +21 -21
  33. package/dist/src/mind/traverse.d.ts +54 -52
  34. package/dist/src/mind/traverse.js +74 -72
  35. package/dist/src/mind/types.d.ts +4 -4
  36. package/dist/src/store.d.ts +12 -12
  37. package/dist/src/store.js +12 -12
  38. package/docs/INDEX.md +2 -2
  39. package/docs/architecture/exact-vs-approximate.md +2 -1
  40. package/docs/architecture/fold-contract.md +1 -1
  41. package/docs/failures/tempting-but-wrong.md +2 -3
  42. package/docs/harness/gates.md +7 -7
  43. package/example/train_base/config.ts +2 -2
  44. package/example/train_base/corpora/massive.ts +1 -1
  45. package/example/train_base/readers.ts +1 -1
  46. package/jsr.json +1 -1
  47. package/package.json +1 -1
  48. package/src/geometry.ts +25 -24
  49. package/src/meter.ts +14 -14
  50. package/src/mind/attention.ts +12 -12
  51. package/src/mind/bridge.ts +33 -32
  52. package/src/mind/graph-search.ts +43 -24
  53. package/src/mind/junction.ts +8 -8
  54. package/src/mind/learning.ts +36 -35
  55. package/src/mind/match.ts +20 -19
  56. package/src/mind/mechanisms/cover.ts +13 -12
  57. package/src/mind/mechanisms/prefix-completion.ts +24 -24
  58. package/src/mind/mechanisms/recall.ts +38 -40
  59. package/src/mind/mechanisms/reference.ts +16 -16
  60. package/src/mind/mind.ts +6 -7
  61. package/src/mind/pipeline-mechanism.ts +25 -21
  62. package/src/mind/pipeline.ts +33 -32
  63. package/src/mind/primitives.ts +5 -5
  64. package/src/mind/recognition.ts +51 -36
  65. package/src/mind/resonance.ts +21 -21
  66. package/src/mind/traverse.ts +74 -72
  67. package/src/mind/types.ts +4 -4
  68. package/src/store.ts +20 -20
  69. package/test/08-storage.test.mjs +1 -1
  70. package/test/35-prefix-edge.test.mjs +1 -1
  71. package/test/40-choosenext-scale-guard.test.mjs +16 -17
  72. package/test/46-recognise-multibyte-edge.test.mjs +33 -0
  73. package/test/56-bridge-identity-admission.test.mjs +6 -6
  74. package/test/70-prefix-completion.test.mjs +4 -3
  75. package/test/72-prefix-candidate-supply.test.mjs +3 -3
  76. package/test/73-scaffolding-only-bridge-abstains.test.mjs +6 -6
  77. package/test/75-multiturn-context-optimisation.test.mjs +5 -5
  78. package/test/84-composed-answer-honesty.test.mjs +5 -6
  79. package/test/88-dependency-footprint.test.mjs +1 -1
  80. package/test/89-completion-recursion.test.mjs +17 -14
  81. package/test/90-connector-read-cap.test.mjs +10 -8
  82. package/test/93-regime-prediction.test.mjs +10 -10
  83. package/test/94-cross-region-budget.test.mjs +2 -2
  84. package/test/95-wide-resonance-removed.test.mjs +8 -7
  85. package/test/96-bytes-walk-termination.test.mjs +3 -3
  86. package/test/99-fact-join.test.mjs +38 -0
@@ -7,10 +7,10 @@
7
7
  // substring search, so it needs the candidate's bytes; it used to reconstruct
8
8
  // them in FULL via `read(ctx, answer)`, whose maxLen defaults to ALL.
9
9
  //
10
- // AGENTS §2.8, prefix-capped reads: "a candidate that exceeds the cap is
10
+ // bounded-reads.md, prefix-capped reads: "a candidate that exceeds the cap is
11
11
  // rejected without reconstructing it — the weave, the junction walks and the
12
12
  // bridge all read this way, and uncapped reads there cost seconds per query on
13
- // a large store." This probe was the exception, and it runs hubBound(ctx) = √N
13
+ // a large store." This probe was the exception: it runs hubBound(ctx) = √N
14
14
  // times PER SITE.
15
15
  //
16
16
  // The probe's corpus-scale cost was once claimed from a trained-store
@@ -18,14 +18,15 @@
18
18
  // prompt" — but that number was measured on a `respond()` query, where the
19
19
  // probe does NOT execute (`answeredSpans` is empty there, so the enclosing
20
20
  // guard returns first). It is therefore not attributable to the probe and is
21
- // not repeated here (§2.16: a comment asserting a measurement inherits Gate 1).
21
+ // not repeated here (a comment asserting a measurement inherits Gate 1).
22
22
  // The probe runs only on a multi-turn `respondTurn` response; its benefit there
23
23
  // is still unmeasured.
24
24
  //
25
- // WHAT THIS PINS. The cap cannot reduce the read COUNT — only a semantic change
26
- // could (see below). It bounds each read by the QUERY, which is what §2.8 asks
27
- // and what rescues a SHORT query: candidates averaged 231 B reconstructed
28
- // against a 3-byte prompt. So the invariant here is per-read SIZE.
25
+ // WHAT THIS PINS. The cap cannot reduce the read COUNT — only a semantic change
26
+ // could (see below). It bounds each read by the QUERY, which is what
27
+ // bounded-reads.md asks and what rescues a SHORT query: candidates averaged 231
28
+ // B reconstructed against a 3-byte prompt. So the invariant here is per-read
29
+ // SIZE.
29
30
  //
30
31
  // It is measured by calling `resolveConnectors` DIRECTLY and diffing the meter
31
32
  // across it. A whole-response counter cannot express this: `bytesRead` sums
@@ -121,7 +122,8 @@ test("connector probe reads by the query, not by the learnt continuation", async
121
122
  `the connector probe averaged ${perRead.toFixed(0)} B per read for a ` +
122
123
  `${QUERY.length} B query — a candidate longer than the query cannot ` +
123
124
  `occur inside it, so it must be rejected on an overflow probe of ` +
124
- `${QUERY.length + 1} B, not reconstructed in full (AGENTS §2.8)`,
125
+ `${QUERY.length + 1} B, not reconstructed in full ` +
126
+ `(bounded-reads.md)`,
125
127
  );
126
128
  } finally {
127
129
  mind.endResponse();
@@ -1,17 +1,17 @@
1
1
  // 93-regime-prediction.test.mjs — the retrieval/composition regime (R8) is
2
2
  // exposed as a structured trace step, without changing inference.
3
3
  //
4
- // After the FIRST mechanism runs (cover, which §2.6 places first and floors at
5
- // 0), the market's whole outcome is already determined by the one cost ladder:
6
- // the consensus climb runs exactly when `worthRunning(2 * STEP)` is true —
7
- // CAST (floor 2·STEP) is the cheapest mechanism that first-touches it. An
8
- // incumbent at or below that floor prunes CAST and, with it, the climb
4
+ // After the FIRST mechanism runs (cover, which mechanism-market.md places first
5
+ // and floors at 0), the market's whole outcome is already determined by the one
6
+ // cost ladder: the consensus climb runs exactly when `worthRunning(2 * STEP)`
7
+ // is true — CAST (floor 2·STEP) is the cheapest mechanism that first-touches
8
+ // it. An incumbent at or below that floor prunes CAST and, with it, the climb
9
9
  // (retrieval); anything above — or no incumbent — runs the full market and the
10
- // climb (composition). The step is purely observational: it is built only
11
- // under a trace (optional-chaining short-circuits it otherwise), and it never
12
- // alters which candidate wins. The assertions here check the payload's
13
- // STRUCTURE and its consistency with the actual market outcome, never that
14
- // inference itself changed.
10
+ // climb (composition). The step is purely observational: it is built only under
11
+ // a trace (optional-chaining short-circuits it otherwise), and it never alters
12
+ // which candidate wins. The assertions here check the payload's STRUCTURE and
13
+ // its consistency with the actual market outcome, never that inference itself
14
+ // changed.
15
15
 
16
16
  import { test } from "node:test";
17
17
  import assert from "node:assert/strict";
@@ -1,7 +1,7 @@
1
1
  // 94-cross-region-budget.test.mjs — the cross-region junction ladder shares ONE
2
2
  // k·W allowance per evidence tier once atoms are hubs, instead of letting each
3
- // candidate pair spend its own √N·W drift budget (attention.ts crossRegionVotes,
4
- // §2.17's derived gate = traverse.atomIsHub).
3
+ // candidate pair spend its own √N·W drift budget (attention.ts
4
+ // crossRegionVotes, saturation.md's derived gate = traverse.atomIsHub).
5
5
  //
6
6
  // This is a PERFORMANCE regression test, not a behaviour test: the shared
7
7
  // budget is byte-identical at every scale — a pair whose container is not
@@ -4,13 +4,14 @@
4
4
  // pipeline-mechanism.ts's REMOVED note).
5
5
  //
6
6
  // This is a PERFORMANCE regression test, not a behaviour test: the wide list
7
- // was a PROPOSAL source whose consumers byte-verify every candidate (§2.3), so
8
- // removing it is byte-identical wherever the bounded sources supply the same
9
- // candidate set — and the meter's phase map is the only observable that says
10
- // whether the exhaustive machinery still exists. Red-on-revert: re-adding
11
- // wideResonance re-creates the `wideResonance` phase (and, when the query's
12
- // top hit clears conceptThreshold, a full-index ANN scan inside it), so the
13
- // phase-absence assertion below fails.
7
+ // was
8
+ // a PROPOSAL source whose consumers byte-verify every candidate
9
+ // (exact-vs-approximate.md), so removing it is byte-identical wherever the
10
+ // bounded sources supply the same candidate set — and the meter's phase map is
11
+ // the only observable that says whether the exhaustive machinery still exists.
12
+ // Red-on-revert: re-adding wideResonance re-creates the `wideResonance` phase
13
+ // (and, when the query's top hit clears conceptThreshold, a full-index ANN scan
14
+ // inside it), so the phase-absence assertion below fails.
14
15
 
15
16
  import { test } from "node:test";
16
17
  import assert from "node:assert/strict";
@@ -92,12 +92,12 @@ test("bytes() terminates when its memo cannot hold the working set", async () =>
92
92
  });
93
93
 
94
94
  test("differsByOneWindow's reads are capped — no ALL-sentinel read on deposit", async () => {
95
- // §2.8: the near-dedup byte check used to open with
95
+ // bounded-reads.md: the near-dedup byte check used to open with
96
96
  // `bytesPrefix(k, Number.MAX_SAFE_INTEGER)` — the ALL sentinel, which routes
97
- // to the full materialising `bytes()` — and only THEN compare lengths. A
97
+ // to the full materialising `bytes()` — and only THEN compare lengths. A
98
98
  // candidate the length test was about to reject had already been rebuilt byte
99
99
  // for byte, and that read is what dragged the deposit path into the walk
100
- // above. Lengths now decide first, from the `contentLen` memo.
100
+ // above. Lengths now decide first, from the `contentLen` memo.
101
101
  const src = await import("node:fs").then((fs) =>
102
102
  fs.readFileSync(new URL("../src/store.ts", import.meta.url), "utf8")
103
103
  );
@@ -97,3 +97,41 @@ test("naming the intermediate does not need derive-through — the cover reads i
97
97
  );
98
98
  await store.close();
99
99
  });
100
+
101
+ /** The TRAP fixture: the query's OWN subject carries the asked relation too, so
102
+ * a join that follows the subject answers about the subject. */
103
+ const TRAP_CHAIN = [
104
+ ["Eiffel Tower country", "The country of Eiffel Tower is France."],
105
+ ["France capital", "The capital of France is Paris."],
106
+ ["France", "The capital of France is Paris."],
107
+ ["Eiffel Tower capital", "The capital of Eiffel Tower is Berlin."],
108
+ ["Eiffel Tower", "The country of Eiffel Tower is France."],
109
+ ];
110
+
111
+ async function trappedFlip() {
112
+ const store = new SQliteStore({ path: ":memory:", D: 1024 });
113
+ const mind = new Mind({ seed: 7, store });
114
+ await mind.ingest(TRAP_CHAIN);
115
+ await mind.ingest(Array.from({ length: 4300 }, (_, i) => filler(i)));
116
+ return { store, mind };
117
+ }
118
+
119
+ test("the join answers about the entity the query did NOT name, not the query's own subject", async () => {
120
+ // MEASURED on the pre-change tree: the answer was "The capital of Eiffel
121
+ // Tower country is Berlin." — the join followed the query's OWN subject
122
+ // (Eiffel Tower), which carries a "capital" fact of its own, instead of the
123
+ // entity the produced fact introduces (France). Both candidates are contexts
124
+ // joined by the same relation, so only the preference decides.
125
+ const { store, mind } = await trappedFlip();
126
+ const moves = [];
127
+ const out = await mind.respondText(
128
+ "Eiffel Tower country capital",
129
+ (s) => moves.push(s.mechanism[s.mechanism.length - 1]),
130
+ );
131
+ assert.equal(out.trim(), "The capital of France is Paris.");
132
+ assert.ok(
133
+ !out.includes("Eiffel Tower"),
134
+ `the answer must be about the inferred entity, got ${JSON.stringify(out)}`,
135
+ );
136
+ await store.close();
137
+ });