@hviana/sema 0.8.1 → 0.8.3

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 (114) hide show
  1. package/AGENTS.md +29 -29
  2. package/TRADEMARKS.md +0 -1
  3. package/dist/src/config.d.ts +28 -0
  4. package/dist/src/config.js +20 -0
  5. package/dist/src/geometry.d.ts +21 -0
  6. package/dist/src/geometry.js +21 -0
  7. package/dist/src/meter.d.ts +76 -0
  8. package/dist/src/meter.js +95 -0
  9. package/dist/src/mind/attention.d.ts +4 -0
  10. package/dist/src/mind/attention.js +165 -16
  11. package/dist/src/mind/canonical.d.ts +16 -0
  12. package/dist/src/mind/canonical.js +41 -0
  13. package/dist/src/mind/corpus.d.ts +40 -0
  14. package/dist/src/mind/corpus.js +149 -0
  15. package/dist/src/mind/graph-search.d.ts +7 -0
  16. package/dist/src/mind/graph-search.js +254 -24
  17. package/dist/src/mind/index.d.ts +3 -1
  18. package/dist/src/mind/index.js +1 -0
  19. package/dist/src/mind/match.d.ts +9 -4
  20. package/dist/src/mind/match.js +147 -61
  21. package/dist/src/mind/mechanisms/cast.js +19 -3
  22. package/dist/src/mind/mechanisms/confluence.js +24 -0
  23. package/dist/src/mind/mechanisms/cover.js +6 -0
  24. package/dist/src/mind/mechanisms/recall.js +32 -4
  25. package/dist/src/mind/mind.d.ts +57 -0
  26. package/dist/src/mind/mind.js +72 -1
  27. package/dist/src/mind/pipeline-mechanism.d.ts +7 -0
  28. package/dist/src/mind/pipeline.js +66 -20
  29. package/dist/src/mind/primitives.js +9 -1
  30. package/dist/src/mind/rationale.d.ts +28 -1
  31. package/dist/src/mind/rationale.js +22 -1
  32. package/dist/src/mind/reasoning.d.ts +25 -3
  33. package/dist/src/mind/reasoning.js +125 -20
  34. package/dist/src/mind/recognition.js +4 -8
  35. package/dist/src/mind/resonance.js +20 -1
  36. package/dist/src/mind/trace.js +1 -0
  37. package/dist/src/mind/traverse.js +15 -3
  38. package/dist/src/mind/types.d.ts +49 -4
  39. package/docs/INVARIANTS.md +2 -2
  40. package/docs/architecture/bounded-reads.md +1 -1
  41. package/docs/architecture/commonality.md +2 -2
  42. package/docs/architecture/cost-model.md +2 -2
  43. package/docs/architecture/determinism.md +7 -7
  44. package/docs/architecture/match-project.md +2 -3
  45. package/docs/architecture/mechanism-market.md +10 -10
  46. package/docs/architecture/meter.md +5 -5
  47. package/docs/architecture/store.md +3 -3
  48. package/docs/failures/tempting-but-wrong.md +34 -6
  49. package/docs/harness/gates.md +2 -2
  50. package/docs/mechanisms/cast.md +2 -2
  51. package/docs/mechanisms/cover.md +2 -3
  52. package/docs/mechanisms/extraction.md +7 -7
  53. package/docs/mechanisms/recall.md +8 -9
  54. package/jsr.json +1 -1
  55. package/package.json +1 -1
  56. package/src/alu/README.md +11 -12
  57. package/src/config.ts +48 -0
  58. package/src/geometry.ts +21 -0
  59. package/src/meter.ts +98 -0
  60. package/src/mind/attention.ts +167 -16
  61. package/src/mind/canonical.ts +43 -0
  62. package/src/mind/corpus.ts +202 -0
  63. package/src/mind/graph-search.ts +277 -23
  64. package/src/mind/index.ts +8 -1
  65. package/src/mind/match.ts +148 -57
  66. package/src/mind/mechanisms/cast.ts +20 -2
  67. package/src/mind/mechanisms/confluence.ts +24 -0
  68. package/src/mind/mechanisms/cover.ts +5 -0
  69. package/src/mind/mechanisms/recall.ts +32 -4
  70. package/src/mind/mind.ts +125 -0
  71. package/src/mind/pipeline-mechanism.ts +7 -0
  72. package/src/mind/pipeline.ts +79 -22
  73. package/src/mind/primitives.ts +9 -1
  74. package/src/mind/rationale.ts +35 -1
  75. package/src/mind/reasoning.ts +145 -13
  76. package/src/mind/recognition.ts +4 -8
  77. package/src/mind/resonance.ts +19 -1
  78. package/src/mind/trace.ts +1 -0
  79. package/src/mind/traverse.ts +16 -6
  80. package/src/mind/types.ts +53 -4
  81. package/test/100-complete-grounding-trace.test.mjs +109 -0
  82. package/test/101-alignment-gap-bound.test.mjs +106 -0
  83. package/test/102-production-composes-at-scale.test.mjs +110 -0
  84. package/test/103-alignment-gap-budget.test.mjs +89 -0
  85. package/test/104-composition-is-reported.test.mjs +90 -0
  86. package/test/105-derive-through-reports-its-refusal.test.mjs +137 -0
  87. package/test/106-the-join-fires.test.mjs +94 -0
  88. package/test/107-the-join-is-counted.test.mjs +81 -0
  89. package/test/108-the-join-chains.test.mjs +78 -0
  90. package/test/109-the-pivot-is-counted.test.mjs +60 -0
  91. package/test/110-the-reasoner-stops-when-the-question-is-answered.test.mjs +91 -0
  92. package/test/111-the-cover-assembly-is-counted.test.mjs +74 -0
  93. package/test/112-the-exploration-does-not-grow-with-the-hub.test.mjs +89 -0
  94. package/test/113-the-rationale-payload-is-bounded.test.mjs +84 -0
  95. package/test/114-alignment-budget-is-per-sweep.test.mjs +93 -0
  96. package/test/116-the-extension-is-gated-by-the-pipelines-own-remainder.test.mjs +100 -0
  97. package/test/117-corpus-search.test.mjs +171 -0
  98. package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
  99. package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
  100. package/test/120-composition-is-consequence.test.mjs +132 -0
  101. package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
  102. package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
  103. package/test/123-the-paired-formulas-agree.test.mjs +90 -0
  104. package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
  105. package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
  106. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
  107. package/test/129-the-trace-payload-shape.test.mjs +164 -0
  108. package/test/14-scaling.test.mjs +10 -7
  109. package/test/32-confluence.test.mjs +68 -0
  110. package/test/38-reason-restate-guard.test.mjs +8 -2
  111. package/test/43-cast-analog-seat.test.mjs +10 -0
  112. package/test/55-cost-meter.test.mjs +859 -0
  113. package/test/76-reference-binding.test.mjs +6 -1
  114. package/test/89-completion-recursion.test.mjs +30 -5
@@ -35,12 +35,40 @@ export interface GraphSearchHost {
35
35
  starts: ReadonlySet<number>;
36
36
  };
37
37
  chooseNext?(node: number): number | undefined;
38
+ /** The lengths `p` for which `prefix ‖ tail[0..p]` IS A STORED NODE, ascending
39
+ * — the join's candidate set, in the tail's own coordinates. Optional: a host
40
+ * that cannot answer makes the join fall back to every prefix, which is exact
41
+ * and complete but pays a `resolve` per offset.
42
+ *
43
+ * WHY NOT THE FOLD'S CUTS. A key names a relation exactly when the
44
+ * concatenation is a node, and a node's end is the end of ITS OWN stream —
45
+ * where the fold never emits a cut (geometry's `emit` guards `at >= n`). So a
46
+ * key can end strictly inside the tail with no boundary anywhere near it:
47
+ * measured, "stockholm mayor" exists, leads on to the mayor fact, and its
48
+ * boundary 6 is in neither the tail's cuts ([4,7]) nor the concatenation's.
49
+ * The fold's boundaries are a SUBSET of the real ends, not a proxy for them,
50
+ * and using them skipped the shortest names first — which is a semantic law,
51
+ * not an optimisation (test/106, test/108 pin it). */
52
+ contentKeyEnds?(prefix: Uint8Array, tail: Uint8Array): readonly number[];
38
53
  /** The admission predicate — `traverse.ts`'s `leadsSomewhere`, its ONE
39
54
  * definition: does this node bear an edge or a halo? Optional, so a bare
40
55
  * host (a raw Store and nothing else) still works; when present, the search
41
56
  * uses it rather than re-probing the store, which keeps the predicate
42
57
  * single-defined AND memoised on the response-scoped struct cache. */
43
58
  leadsSomewhere?(id: number): boolean;
59
+ /** Report a SEARCH REFUSAL into the rationale — the channel AGENTS §6
60
+ * requires: a callback threaded through a call chain must FEED the
61
+ * rationale, the way `GraphSearch`'s `onDerivation` feeds `traceDerivation`,
62
+ * never a channel of its own. Optional, so a bare host stays silent rather
63
+ * than crashing. */
64
+ reportSearch?(name: string, parts: ReadonlyArray<Uint8Array>, note: string): void;
65
+ /** The CANONICAL resolver ({@link canonResolve}), optional like
66
+ * {@link leadsSomewhere}. The store's keys were written through the
67
+ * canonical fold, so a fact's `Gustaf Molander` and the deposited
68
+ * `gustaf molander` are the SAME node (measured inside a response: the
69
+ * canonical resolver maps the surface form to the deposited node while a raw
70
+ * resolve returns null). A bare host falls back to the plain probe. */
71
+ canonResolve?(bytes: Uint8Array): number | null;
44
72
  }
45
73
  export interface Recognition {
46
74
  /** Forms that can lead somewhere — they have an edge or a halo. */
@@ -82,11 +110,28 @@ export interface Attention {
82
110
  * strength and its place.
83
111
  * `vote` is a sum over every region that agreed, so it grows with how many
84
112
  * places corroborated; `peak` is what the strongest one of them said on its
85
- * own. A consumer holding this point to consensusFloor(N) — a bar that
86
- * prices ONE region's maximally-discriminative evidence — must read `peak`,
87
- * not `vote`: six scaffolding regions summing past the floor is not the
88
- * same claim as one region clearing it. */
113
+ * own. THIS USED TO PRESCRIBE THE WRONG OPERAND. It read: "a consumer
114
+ * holding this point to consensusFloor(N) — a bar that prices ONE region's
115
+ * maximally-discriminative evidence — must read `peak`, not `vote`." The
116
+ * engine reads the POOLED vote, and thresholds.md §2 derives the floor for
117
+ * exactly that ("Pooled-vote significance floor": one maximally-specific
118
+ * region contributes at most ln N, and ln(N)+1/2 demands corroboration
119
+ * BEYOND one region). MEASURED across 27 anchors on 6 queries: all 11
120
+ * admissions cleared the floor by the sum and NONE by `peak` alone — a gate
121
+ * reading `peak` would refuse every root the engine elects. `peak` remains
122
+ * what it is: the strongest SINGLE region's contribution. */
89
123
  peak: number;
124
+ /** The IDF-WEIGHTED sum behind this point — the quantity `consensusFloor` is
125
+ * derived for, and therefore the one the floor gates must read. It is
126
+ * MODE-INDEPENDENT by construction (its per-region weight is
127
+ * `mutual · idf / roots`, never the mode-dependent `wf`), so gating on it
128
+ * makes an anchor's admission the same in `inverse`, `direct` and `combined`.
129
+ * In `inverse` — the only mode the engine runs — it equals `vote` exactly
130
+ * (measured, test/55 test 17), so nothing about today's verdicts changes.
131
+ * MEASURED before this field existed: gating on `vote` DID flip a verdict,
132
+ * anchor 87 of test/55's query (inverse 2.682 admitted, direct 1.468
133
+ * refused, floor 2.292). */
134
+ idfVote: number;
90
135
  /** SCALE-INVARIANT confidence: the fraction of the query's OWN regions
91
136
  * whose evidence this point accounts for (Σ RegionVote.absorbed among
92
137
  * its contributors, over the query's total region count) — read PER-
@@ -11,9 +11,9 @@
11
11
  | 5 | Bounded reads | `src/store.ts:AbstractStore:nextFirst,parentsFirst,containersSlice,hasNext,bytesPrefix` `src/mind/traverse.ts:hubBound,hubCap` | `test/90` `test/14` | `bounded-reads.md` |
12
12
  | 6 | Fold contract | `src/geometry.ts:contentLevels` `src/mind/canonical.ts:canonicalWindows,chainReach` `src/canon.ts:canonicalizer` | `test/59` `test/63` | `fold-contract.md` |
13
13
  | 7 | Mechanism market | `src/mind/pipeline-mechanism.ts:PipelineMechanism,Precomputed` `src/mind/pipeline.ts:think,worthRunning` | `test/01` `test/04` | `mechanism-market.md` |
14
- | 8 | Two commonality measures | `src/mind/traverse.ts:reachOf,dominates,corpusN` (global) `src/mind/match.ts:depth[],MIN_WEAVE` (weave-local) | `test/17` `test/34` | `commonality.md` |
14
+ | 8 | Two commonality measures | `src/mind/traverse.ts:reachOf,dominates,corpusN` (global) `cast.ts:depth[],MIN_WEAVE` (weave-local) | `test/17` `test/34` | `commonality.md` |
15
15
  | 9 | Memoization idempotence | `src/mind/pipeline-mechanism.ts:Precomputed` `src/mind/mind.ts:beginResponse,endResponse,_resolvedSubtrees` | `test/42` | `memoization.md` |
16
16
  | 10 | Caches as budgets | `src/store.ts:BoundedMap` `src/config.ts:StoreConfig:bytesCacheMax,recCacheBytes,haloCacheBytes` | `test/96` `test/91` | `caches.md` |
17
17
  | 11 | Honest degradation | `src/mind/pipeline.ts:weight=moves+PASS*unaccounted` `src/store.ts:BoundedMap:miss→re-derive` | `test/28` `test/84` | `store.md`+`caches.md` |
18
18
  | 12 | Meter contracts | `src/meter.ts:Meter,PhaseCost,time` `src/mind/pipeline-mechanism.ts:Precomputed.shared` | `test/55` | `meter.md` |
19
- | 13 | Saturation | `src/mind/traverse.ts:edgeAncestors:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto` | `test/27` `test/16` | `saturation.md` |
19
+ | 13 | Saturation | `traverse.ts:edgeAncestors,types.ts:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto` | `test/27` `test/16` | `saturation.md` |
@@ -18,7 +18,7 @@ boundFor(n) = ceil(sqrt(max(2, n))) // ctx-free reading
18
18
  ```
19
19
 
20
20
  Defined once in `mind/traverse.ts` (`corpusN`, `hubBound`, `hubCap`,
21
- `boundFor`). Every consumer imports them; never spell `Math.sqrt` inline.
21
+ `boundFor`). Every consumer imports them; never re-derive them inline.
22
22
 
23
23
  ## Enforcement at the store level
24
24
 
@@ -19,8 +19,8 @@ scaffolding. Powers the consensus climb, edge following, and vote pooling.
19
19
 
20
20
  ## Weave-local — `depth[]` + `MIN_WEAVE` + `dominates`
21
21
 
22
- _Defined in `src/mind/match.ts` (`depth[]`, `MIN_WEAVE`, `frame`) and gated in
23
- `src/mind/match.ts:frame`; used by CAST._
22
+ _Defined and gated in `src/mind/mechanisms/cast.ts` (`depth[]` from the shared
23
+ weave, `MIN_WEAVE`); used by CAST._
24
24
 
25
25
  For an alignment weave, `depth[i]` counts how many aligned structures cover byte
26
26
  `i` of the query. `MIN_WEAVE = 2` requires agreement beyond a pair (pair columns
@@ -59,8 +59,8 @@ exceeds the true remaining cost.
59
59
  ## Policy is not cost
60
60
 
61
61
  "Computation always wins" is **not** priced into the ladder (a computed result
62
- costs `STEP`, same as a learned edge). It is enforced by masking: `pipeline.ts`
63
- removes recognised sites overlapped by a `ComputedResult` so the computation is
62
+ costs `STEP`, same as a learned edge). It is enforced by masking: `cover.ts`
63
+ removes recognised sites overlapped by a `ComputedResult`, so the computation is
64
64
  the sole completion there. Keep policy in callers; keep the engine neutral.
65
65
 
66
66
  ## Pins
@@ -20,7 +20,7 @@ flaky, the contract was broken, not the test.
20
20
  entropy root. Subsystems derive deterministically:
21
21
 
22
22
  - **Alphabet** — `Alphabet` (`src/alphabet.ts`) via `rng` (`src/vec.ts:rng`)
23
- seeded as `seed ^ seedMask`; builds 16→64→256 vectors by refinement.
23
+ seeded as `seed ^ seedMask`; builds 16→64→256 vectors.
24
24
  - **Keyring / Space** — `Space.seats` (`src/sema.ts:Space`) via `makeKeyring`
25
25
  (`src/vec.ts:makeKeyring`) and `rng` seeded from `seed` in `Mind`
26
26
  (`src/mind/mind.ts`); `fold`/`twoEndedSeat`/`companySignature` are pure over
@@ -34,8 +34,8 @@ derived from `D`/`W`/`N`, not sampled.
34
34
 
35
35
  ## Tie-breaks are corpus-determined
36
36
 
37
- Every choice among equals bottoms out in a fixed ordering — insertion order or
38
- lowest node id. The universal no-evidence fallback is **first-inserted**:
37
+ Every choice bottoms out in a fixed ordering — insertion order or lowest node id
38
+ — not interchangeable (`test/34`). The fallback is **first-inserted**:
39
39
 
40
40
  - `guidedFirst` (`src/mind/traverse.ts:guidedFirst`) — guided pick via
41
41
  `chooseNext` else first-inserted edge (`nextFirst` LIMIT 1).
@@ -46,7 +46,7 @@ lowest node id. The universal no-evidence fallback is **first-inserted**:
46
46
  - `companySignature` (`src/sema.ts:companySignature`) — `rng(id ^ 0x9e3779b9)`,
47
47
  i.e. seeded by node id, not observation order.
48
48
 
49
- Last-inserted was once used in one place; it was a bug. Never reintroduce it.
49
+ Never use last-inserted.
50
50
 
51
51
  ## Memoization and trace must not break identity
52
52
 
@@ -56,7 +56,7 @@ Per-response memos (`Precomputed`, `perceiveMemo`, `recogniseMemo`, `climbMemo`,
56
56
  `src/mind/primitives.ts`) are sound because asking never writes. Only
57
57
  `guidedNext`/`sharedReachMemo` are trace-bypassed;
58
58
  `perceiveMemo`/`recogniseMemo`/`climbMemo` are always consulted — `foldTree`'s
59
- subtree fast path skips `visit` (and thus site emission) for cached subtrees, so
59
+ subtree fast path skips `visit` (and site emission) for cached subtrees, so
60
60
  bypassing makes `recognise` non-idempotent.
61
61
 
62
62
  ## Follow it
@@ -69,5 +69,5 @@ call `Math.random`/`Date.now` on a behavioural path.
69
69
 
70
70
  - `test/42` pins recognition idempotence under trace — traced and untraced
71
71
  `recognise` must return the same cached object and site count.
72
- - Determinism suites — `test/03`, `test/04`, `test/08`, `test/20` and others
73
- assert same seed + same training ⇒ byte-identical answers and stores.
72
+ - Determinism suites — `test/03`, `test/04`, `test/08`, `test/20` — assert same
73
+ seed + same training ⇒ byte-identical answers and stores.
@@ -16,7 +16,7 @@ functions over bytes and the store — no mechanism owns a private copy.
16
16
  | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
17
17
  | **Match** (locate structure) | `locate` (exact → halo → gist ladder), `alignRuns` (literal W-gram weave), `alignGraded` (literal + halo gaps), `alignAround` / `frameSlots` (seeded frame with contracted gaps), `bestHaloMate` (in-list halo), `analogyStrength` / `sharedFrameStrength` (distributional + structural analogy) | Finds where a query sits in a learnt form. |
18
18
  | **Project** (direction) | `follow` (forward to fixpoint, first hop may `conceptHop`), `reverseContext` (reverse to context), `project` (forward else reverse), `conceptHop` (halo sibling with edge) | Moves along the store from the match — forward toward answers, reverse toward contexts. |
19
- | **Gate** (structural licence) | `isSpanShaped` (sparse subsequence — open reading), `carriesFillers` (substitution carriage — strict voicing licence) | Decides whether the shape licences voicing. |
19
+ | **Gate** (structural licence) | `isSpanShaped` (OPEN reading — sparse subsequence), `containsSpan` (STRICT reading — contiguous run or resolved node), `skillExemplar` (anchor → context + answer), `carriesFillers` (substitution carriage — strict voicing licence) | Two readings; not interchangeable. |
20
20
 
21
21
  Mechanisms declare only `(matcher, direction, gate)`. Thresholds behind gates
22
22
  live in `src/geometry.ts` — the match layer never invents a cutoff.
@@ -52,8 +52,7 @@ The shared layer never refuses on a consumer's behalf. Reference owns its four
52
52
  gates: frame dominates the query, each slot reaches `W` on both sides, no
53
53
  insertion/deletion, fillers pairwise distinct — plus `carriesFillers` on the
54
54
  chosen pair. CAST, recall, and cover each apply their own gate over the same
55
- shared inventory. Moving a consumer's gate into `match.ts` would hide who is
56
- responsible for the refusal.
55
+ shared inventory. Moving a gate into `match.ts` would hide who owns the refusal.
57
56
 
58
57
  ## Pins
59
58
 
@@ -19,6 +19,7 @@ interface MechanismResult {
19
19
  unexplained: string;
20
20
  scaffolding?: number;
21
21
  complete?: boolean;
22
+ used?: ReadonlySet<number>;
22
23
  }
23
24
  ```
24
25
 
@@ -30,7 +31,7 @@ interface MechanismResult {
30
31
 
31
32
  ## Decider
32
33
 
33
- `think` in `mind/pipeline.ts` iterates `defaultMechanisms` in list order:
34
+ `think` iterates `defaultMechanisms` in list order:
34
35
 
35
36
  ```
36
37
  defaultMechanisms = [cover, cast, confluence, extraction, reference, recall,
@@ -39,8 +40,7 @@ defaultMechanisms = [cover, cast, confluence, extraction, reference, recall,
39
40
 
40
41
  Weight is one currency: `weight = moves + PASS · unaccountedBytes` where
41
42
  `unaccountedBytes = unexplainedSpans(query.length, accounted)`. Comparison is at
42
- `STEP` grade (`grade = floor(weight/STEP)`); equal grade prefers fewer
43
- `scaffolding` bytes, then list order.
43
+ `STEP` grade ; equal grade prefers fewer `scaffolding` bytes, then list order.
44
44
 
45
45
  ## Four constraints
46
46
 
@@ -48,16 +48,16 @@ Weight is one currency: `weight = moves + PASS · unaccountedBytes` where
48
48
  never touches another; no mechanism asks what already decided.
49
49
  2. **Declared competence** — binary structural gates inside `floor`/`run` (query
50
50
  length, anchor shape, weave existence). Never a learned score; rationale
51
- states exactly why a mechanism abstained.
51
+ states why a mechanism abstained.
52
52
  3. **Visible budget** — every corpus-scale loop is capped at a named constant:
53
53
  `√N` via `hubBound`/`hubCap` and `k = 2·recallQueryK` (`Precomputed.k`).
54
- Enforced at the store level.
54
+ Enforced at the store.
55
55
  4. **Evidence travels** — every candidate carries `accounted` (query spans
56
56
  explained), `moves` (priced on `MICRO/STEP/CONCEPT/PASS`), `unexplained`
57
57
  (diagnostic label); optionally `scaffolding` (answer bytes from unrecognised
58
58
  spans — equal-grade tie-break) and `complete` (trained-form continuation
59
59
  reached via identity; post-grounding must not extend). The decider honours
60
- both without knowing who set them.
60
+ all three without knowing who set them.
61
61
 
62
62
  ## Two disciplines
63
63
 
@@ -70,8 +70,8 @@ Weight is one currency: `weight = moves + PASS · unaccountedBytes` where
70
70
  - **Investment discipline.** `worthRunning` is passed _into_ `floor`. A floor
71
71
  that would first-touch an expensive shared analysis (`pre.attention()` climb,
72
72
  `pre.weave()`, `pre.resonance()`) checks `worthRunning(cheapestBound)` first
73
- and returns the uninvested bound when it already loses. Never compute a shared
74
- analysis just to discard it. `cast.ts`/`extraction.ts` are the references.
73
+ and returns the uninvested bound if it loses. Never compute a shared analysis
74
+ just to discard it. `cast.ts`/`extraction.ts` are the references.
75
75
 
76
76
  ## Accounting
77
77
 
@@ -86,8 +86,8 @@ Weight is one currency: `weight = moves + PASS · unaccountedBytes` where
86
86
  same act is charged twice (`PASS`/byte dominates).
87
87
 
88
88
  `accounted` is a cost-ladder quantity; `cover.ts` leaves masked computed spans
89
- out of it so `PASS`-bridged bytes are still charged. `unexplained`,
90
- `narrowDecision`, `thinGrounding` are observational only.
89
+ out so `PASS`-bridged bytes are still charged. `unexplained`, `narrowDecision`,
90
+ `thinGrounding` are observational only.
91
91
 
92
92
  ## Pins
93
93
 
@@ -17,11 +17,11 @@ it. Harness: `bench/profile-inference.mjs`.
17
17
  non-deterministic hints reported separately — never use them to gate
18
18
  behaviour.
19
19
 
20
- 3. **Phases nest, they do not partition.** `think` contains every mechanism
21
- phase; a mechanism's `floor` contains whatever shared analysis it
22
- first-touched; `recall.run` contains `substitutionBridge`. Read a phase as
23
- inclusive wall-clock — never sum phases and expect the total.
24
- `CostReport.elapsedMs` is the only whole.
20
+ 3. **Phases nest, they do not partition.** Each phase is charged by the layer
21
+ doing the work (`recognise`, the climb's two, the bridge), and a mechanism's
22
+ `floor` contains whatever shared analysis it first-touched. Read a phase as
23
+ inclusive wall-clock; never sum phases. `CostReport.elapsedMs` is the only
24
+ whole.
25
25
 
26
26
  4. **Count once.** Off by default and free when off
27
27
  (`new Mind({ profile:
@@ -36,8 +36,8 @@ root never costs a full walk.
36
36
 
37
37
  ## Gist, halo, dedup
38
38
 
39
- On `put*`, content dedup (`hashOf`→probe→mint) gates first. `DedupKey` caches
40
- short keys (`DEDUP_KEY_MAX` bypass). Near-dedup merges by `mergeThreshold(D)` on
39
+ On `put*`, content dedup (`hashOf`→probe→mint) gates first. Short keys are
40
+ cached (`DEDUP_KEY_MAX` bypass). Near-dedup merges by `mergeThreshold(D)` on
41
41
  unit gist cosine. Gists sit in `_pendingGist` (byte-budgeted `BoundedMap`);
42
42
  `indexSubtree` & `pourHalo` promote via `_vecContentUpsert`/`_vecHaloUpsert` in
43
43
  `batchSize` batches. Buffers flush on cadence, `commit()`, and close. Halo mass
@@ -55,7 +55,7 @@ deferred transaction.
55
55
  Every in-memory cache is a `BoundedMap` with byte accounting and eviction (`lru`
56
56
  vs `smallest` + `clock`/`reorder` recency). ANN reads
57
57
  (`resonate`/`resonateHalo`) are content-addressed (`vecKey`) and dropped on any
58
- index mutation; `RESonate_CACHE_MAX=4096`.
58
+ index mutation; `RESONATE_CACHE_MAX=4096`.
59
59
 
60
60
  ## Maintenance (incremental)
61
61
 
@@ -1,6 +1,6 @@
1
- # Tempting but Wrong — 12 Traps
1
+ # Tempting but Wrong — 13 Traps
2
2
 
3
- Twelve shortcuts that look plausible and break an invariant. Each states what
3
+ Thirteen shortcuts that look plausible and break an invariant. Each states what
4
4
  not to do, why it fails, and what to do instead.
5
5
 
6
6
  ### 1. `score >= threshold` decides identity
@@ -30,8 +30,7 @@ not to do, why it fails, and what to do instead.
30
30
  - **WRONG:** Break equal-rank ties by picking the most recently inserted
31
31
  edge/node.
32
32
  - **WHY:** Tie-breaks must be corpus-determined and stable; last-inserted is
33
- recency-dependent and was fixed as a bug (`AGENTS §2` Invariant 1 —
34
- first-inserted fallback).
33
+ recency-dependent (`AGENTS §2` Invariant 1 — first-inserted fallback).
35
34
  - **CORRECT:** `guidedFirst`/`chooseNext`/`chooseAmong`: rank then
36
35
  first-inserted (lowest node id / `LIMIT 1` insertion order). Pinned by
37
36
  `test/03-recall.test.mjs` determinism suites.
@@ -55,8 +54,8 @@ not to do, why it fails, and what to do instead.
55
54
  policy is enforced by masking, not pricing (`AGENTS §2` Invariant 4 — One cost
56
55
  currency; `docs/architecture/cost-model.md` § Policy is not cost).
57
56
  - **CORRECT:** Keep `PASS` dominating; enforce precedence in the caller (e.g.
58
- `pipeline.ts` masks recognised sites overlapped by `ComputedResult`). Pinned
59
- by `test/04-think.test.mjs` and `test/55-cost-meter.test.mjs`.
57
+ `cover.ts` masks recognised sites overlapped by `ComputedResult`). Pinned by
58
+ `test/04-think.test.mjs` and `test/55-cost-meter.test.mjs`.
60
59
 
61
60
  ### 6. Reimplementing `locate`/`align` inside a mechanism
62
61
 
@@ -141,3 +140,32 @@ not to do, why it fails, and what to do instead.
141
140
  `twoEndedSeat`); turns are API state in `mind/mind.ts`, not segmentation.
142
141
  Pinned by `test/59-fold-invariance.test.mjs` and
143
142
  `test/63-fold-invariants.test.mjs`.
143
+
144
+ ### 13. Capping a combinatorial explosion instead of budgeting it
145
+
146
+ - **WRONG:** Answer a combinatorial explosion with a geometry-derived limit — a
147
+ cap on the pairs a sweep enumerates, the continuations a hop may offer, the
148
+ candidates a scan probes. A derived limit is the right cutoff for a DECISION;
149
+ used as the answer to explosion it is a short-circuit.
150
+ - **WHY:** It stops the computation silently. Reach is lost, the capability that
151
+ depended on it goes with it, and no test fails, because the tests were written
152
+ against the capped behaviour. Capping and removing the cap are both wrong:
153
+ capping truncates, removing lets the cost run, and the two failure modes hide
154
+ each other.
155
+ - **CORRECT:** BUDGET it. The work is charged in the one currency
156
+ (`MICRO`/`STEP`/`CONCEPT`/`PASS`; `weight = moves + PASS·unaccounted`, see
157
+ `docs/architecture/cost-model.md`), the charge is visible in the meter and the
158
+ rationale, and the SEARCH decides whether the work is worth paying — so
159
+ inference is never locked by a limit and nothing is truncated in silence.
160
+ Where the work is mechanical rather than evidential — enumeration, scans,
161
+ sweeps — the answer is an algorithm whose cost is structural in the bytes it
162
+ is given, not a smaller cap.
163
+ - **THE IDEAL:** a universal **closure engine** — one law of closure, stated in
164
+ the quantities the machine already has (`leadsSomewhere`, the
165
+ exact-then-canonical identity, `accounted` bytes, the ladder, `hubBound`),
166
+ from which the reach of a gap, the offer of a hop, the depth of a join and the
167
+ scope of a substitution are CONSEQUENCES, not four separate decisions. Nothing
168
+ in this repository is that today.
169
+ - **THE STANDARD A CHANGE MUST MEET:** state which consequence it is, and show
170
+ it following from the law. A change that cannot be stated that way is not
171
+ ready.
@@ -3,7 +3,7 @@
3
3
  Four executable gates. Each: run the command, check what it guards, follow its
4
4
  §.
5
5
 
6
- ## 1 — Correctness (all 90 suites)
6
+ ## 1 — Correctness (all suites)
7
7
 
8
8
  ```bash
9
9
  npm test
@@ -24,7 +24,7 @@ node bench/profile-inference.mjs --trace # trace is a debugging aid, not product
24
24
  ```
25
25
 
26
26
  Guards without trace: counters deterministic and diffable between runs; phases
27
- nest (not disjoint — `think` contains every mechanism phase); shared analyses
27
+ nest (not disjoint — each phase is charged by its own layer); shared analyses
28
28
  charged to themselves, not to the first toucher; millisecond fields are
29
29
  non-deterministic hints only. With `--trace`, recognition idempotence still
30
30
  holds (`test/42`). `src/meter.ts`, `docs/architecture/meter.md`, §55,
@@ -12,7 +12,7 @@ schema yields its own candidate and `think`'s single weight comparison picks.
12
12
  halo-matched `pre.rec.sites`. The product is `pre.weave()` — `points[]` (each
13
13
  with graded `runs[]`) and a per-query-byte `depth[]` (how many structures cover
14
14
  that byte). CAST's single-vs-multi test is measured from those runs: a second
15
- point must add ≥ one perception quantum of coverage the widest point does not.
15
+ point must add ≥ one perception quantum of coverage the widest does not.
16
16
 
17
17
  ## Gate — weave-local discriminative frame
18
18
 
@@ -76,5 +76,5 @@ redirection, or analogical comparison), not from a literal continuation.
76
76
  ## Source
77
77
 
78
78
  `src/mind/mechanisms/cast.ts` (`counterfactualTransfer`, `seatOfNode`,
79
- `MIN_WEAVE`), `src/mind/match.ts` (`alignGraded`, `project`, `depth`),
79
+ `MIN_WEAVE`, `weave.depth`), `src/mind/match.ts` (`alignGraded`, `project`),
80
80
  `src/geometry.ts` (`dominates`), `src/mind/graph-search.ts` (`STEP`).
@@ -15,9 +15,8 @@ consumes them directly; any site whose bytes overlap a computed span is masked
15
15
  - `formRules` follow continuation edges (`GraphSearch.formRules`): each hop
16
16
  costs `STEP` (1). Forks across all continuations up to the hub bound;
17
17
  disambiguation is distributional, not heuristic.
18
- - Edge-less forms may hop via a halo sibling (`conceptHop` / `resolveConcepts`
19
- in `src/mind/mechanisms/cover.ts`) at `CONCEPT` (10), borrowing a synonym's
20
- continuation.
18
+ - Edge-less forms may hop via a halo sibling (`conceptHop` / `resolveConcepts`)
19
+ at `CONCEPT` (10), borrowing a synonym's continuation.
21
20
 
22
21
  ## Gate — `leadsSomewhere` (`src/mind/traverse.ts`)
23
22
 
@@ -7,13 +7,13 @@ what sits between them.
7
7
 
8
8
  ## Matcher — `skillExemplar` / `isSpanShaped` / `containsSpan` (`src/mind/match.ts`)
9
9
 
10
- An exemplar is span-shaped when its answer is an in-order embedding of its
11
- context. `isSpanShaped` is the open reading (sparse subsequence, any gaps) used
12
- to accept candidates; `answerRunsInContext` is the strong reading (greedy
13
- longest contiguous runs) used to decompose the answer for projection. Candidates
14
- are ranked anchors from `climbAttentionAll` (`Precomputed.spanShapedOf`), tried
15
- in order up to `pre.k`; sub-quantum (`< W = maxGroup`) or unanchored results are
16
- skipped.
10
+ An exemplar is span-shaped when its answer embeds in order. `isSpanShaped` is
11
+ the open reading (sparse subsequence, any gaps) for acceptance; `containsSpan`
12
+ is the strict reading (contiguous run, or a resolved node) that fusion gates on,
13
+ extraction decomposes with `answerRunsInContext` (greedy longest runs).
14
+ Candidates are ranked anchors from `climbAttentionAll`
15
+ (`Precomputed.spanShapedOf`), tried up to `pre.k`; sub-quantum (`< W`) or
16
+ unanchored results are skipped.
17
17
 
18
18
  ## Projection — read between located frames (`src/mind/mechanisms/extraction.ts`)
19
19
 
@@ -24,16 +24,15 @@ W = `maxGroup` (river window); bars from `src/geometry.ts`.
24
24
  ## Echo — the refusing tail
25
25
 
26
26
  If no tier grounded, the exact cosine of the top hit is re-folded (`gistOf` on
27
- its bytes). Decision uses that exact value in the same query-relative,
28
- chance-corrected fraction — never the RaBitQ estimate. Below `reach` → silence;
29
- restating → silence; otherwise the hit's own bytes are returned as an ungrounded
30
- echo.
27
+ its bytes). It uses that exact value in the same chance-corrected fraction —
28
+ never the RaBitQ estimate. Below `reach` → silence; restating → silence;
29
+ otherwise the hit's own bytes are returned as an ungrounded echo.
31
30
 
32
31
  ## Provenance
33
32
 
34
33
  Grounded answers carry `recall`; the echo carries `recall-echo` (`echoed: true`
35
- on `RecallResult`). Consumers distinguish a continuation through learned edges
36
- from a near-identity echo.
34
+ on `RecallResult`); it declares `used: ∅`. Consumers distinguish a continuation
35
+ through learned edges from a near-identity echo.
37
36
 
38
37
  ## Substitution bridge — refusal-path only (`src/mind/bridge.ts`)
39
38
 
@@ -47,13 +46,13 @@ unanimous, and the raw gap is length-balanced. Coverage must dominate the query
47
46
  and no dismissed gap may hide known content (`dismissedKnownContent` gate). Cost
48
47
  is `CONCEPT` per substitution plus `STEP`; accounted spans include matched and
49
48
  substituted ranges (so a 28/29-byte paraphrase is not charged `PASS` per
50
- substituted byte — observed double-charge that let `cast` outbid the bridge).
49
+ substituted byte — the double-charge that let `cast` outbid the bridge).
51
50
  Zero-substitution identity bridges carry `complete: true` (the whole read-out);
52
51
  substituted bridges do not.
53
52
 
54
- Scaffolding-only queries abstain: when every stored window that could anchor is
53
+ Scaffolding-only queries abstain: when every window that could anchor is
55
54
  saturated (corpus-global scaffolding, `allWindowsAreScaffolding`), the bridge
56
- returns nothing — a single substituted word cannot carry the semantic load.
55
+ returns nothing — one substituted word cannot carry the load.
57
56
 
58
57
  ## Cost
59
58
 
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://jsr.io/schema/config-file.v1.json",
3
3
  "name": "@hviana/sema",
4
- "version": "0.8.1",
4
+ "version": "0.8.3",
5
5
  "exports": "./src/index.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hviana/sema",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Sema: a non-parametric, instance-based reasoning system.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/alu/README.md CHANGED
@@ -8,10 +8,10 @@ a truth value) are declared here once.
8
8
 
9
9
  It joins the mind as a `PipelineMechanism`
10
10
  ([`../mind/pipeline-mechanism.ts`](../mind/pipeline-mechanism.ts)) whose only
11
- special role is the optional `parse(query)` method every mechanism may
12
- implement. The mind knows nothing about what the ALU computes; it only knows
13
- that `parse` returns `ComputedSpan[]`, which enter the one lightest-derivation
14
- search as authoritative axioms (at `STEP` cost, like a learned edge).
11
+ special role is the optional `parse(query)` every mechanism may implement. The
12
+ mind knows nothing about what the ALU computes; it only knows that `parse`
13
+ returns `ComputedSpan[]`, which enter the one lightest-derivation search as
14
+ authoritative axioms (at `STEP`, like a learned edge).
15
15
 
16
16
  It has no dependency on the rest of the codebase except the pure byte helpers in
17
17
  `../bytes.ts`, and is intended to be reused as a self-contained sublibrary in
@@ -166,7 +166,7 @@ The ALU is completely decoupled from Sema. It joins the mind through
166
166
  re-exported from [`../mind/pipeline.ts`](../mind/pipeline.ts)), a thin adapter
167
167
  that wraps the ALU's `parse` in a `PipelineMechanism` — the same uniform
168
168
  interface every grounding mechanism (CAST, confluence, cover, extraction,
169
- recall) implements, so nothing about the ALU is special-cased in the pipeline.
169
+ recall) implements, so nothing about the ALU is special-cased.
170
170
 
171
171
  ### The contract
172
172
 
@@ -175,7 +175,7 @@ recall) implements, so nothing about the ALU is special-cased in the pipeline.
175
175
  ```ts
176
176
  interface PipelineMechanism {
177
177
  parse?(query: Uint8Array): Promise<ComputedSpan[]>;
178
- floor(ctx, query, pre): Promise<number | null>;
178
+ floor(ctx, query, pre, worthRunning): Promise<number | null>;
179
179
  run(ctx, query, pre): Promise<MechanismResult[]>;
180
180
  }
181
181
  ```
@@ -259,7 +259,7 @@ whose span overlaps a computed span is **masked** before the search. This is the
259
259
  the computed `4` is the cover's sole completion there. The search itself stays a
260
260
  neutral cost engine (a computed `Out` and a learned edge both cost `STEP`);
261
261
  precedence lives entirely in the masking step, which is in
262
- `src/mind/pipeline.ts`, not in the search and not in the ALU.
262
+ `src/mind/mechanisms/cover.ts`, not in the search and not in the ALU.
263
263
 
264
264
  A computation and an _unrelated_ rewrite still compose in one answer
265
265
  (`"ice 2+2"` → `"cold 4"`) because the masking is scoped to the colliding span
@@ -302,11 +302,10 @@ registry.derive("hypot", 2, ["hypot"], (args, ctx) =>
302
302
  ]));
303
303
  ```
304
304
 
305
- No kernel edit, no graph-search edit, no resonance edit — name it, list its
306
- surface forms, write the body in terms of existing ops. A scalar op broadcasts
307
- over `nd` automatically; pass `structural = true` (the trailing flag on
308
- `prim`/`derive`) only for an op that consumes a list _whole_, like the `nd`
309
- kernel's own.
305
+ No kernel, graph-search or resonance edit — name it, list its surface forms,
306
+ write the body from existing ops. A scalar op broadcasts over `nd`
307
+ automatically; pass `structural = true` (the trailing flag on `prim`/`derive`)
308
+ only for an op that consumes a list _whole_, like the `nd` kernel's own.
310
309
 
311
310
  ## Layout
312
311
 
package/src/config.ts CHANGED
@@ -116,6 +116,34 @@ export interface MindConfig {
116
116
  seed: number;
117
117
  recallQueryK: number;
118
118
  haloQueryK: number;
119
+ /** Branch nodes the pivot sweep may PROBE looking for the learnt context an
120
+ * answer contains — the pivot's own shortlist capacity, separate from
121
+ * `recallQueryK` because they are different quantities: this one bounds a
122
+ * MECHANICAL sweep over the answer's tree (breadth-first, largest regions
123
+ * first, so an exhausted allowance drops the far ones and never the near
124
+ * ones), while `recallQueryK` bounds the bridge's candidate reads. Sharing
125
+ * one number for both meant that tightening either silently starved the
126
+ * other — measured: at `recallQueryK: 1` the pivot cannot find a pivot at
127
+ * all. (`rationaleSampleK` was split out of `recallQueryK` for the same
128
+ * reason, found by an adversarial review.) */
129
+ pivotProbeK: number;
130
+ /** Corpus reading (see src/mind/corpus.ts): results per call, resolved
131
+ * nodes climbed from, contexts requested per climb, probes used to stride
132
+ * the id space when browsing, bytes of each side a preview keeps, and the
133
+ * smallest deposited note browsing will show. Capacities and budgets only —
134
+ * the one material floor (a resolved node must account for W bytes) is
135
+ * derived from the geometry, not declared here. */
136
+ corpusLimitMax: number;
137
+ corpusClimbs: number;
138
+ corpusContextsPerClimb: number;
139
+ corpusSampleProbes: number;
140
+ corpusPreviewBytes: number;
141
+ corpusSampleFloorBytes: number;
142
+ /** Items one rationale step may ITEMISE (the whole field is still counted in
143
+ * the step's note). A capacity of the rationale, not of recall: sharing
144
+ * `recallQueryK` meant `new Mind({recallQueryK: 100000})` un-bounded the very
145
+ * payload the bound exists for (found by an adversarial review). */
146
+ rationaleSampleK: number;
119
147
  normalizeEpsilon: number;
120
148
  cosineEpsilon: number;
121
149
 
@@ -131,6 +159,14 @@ export const DEFAULT_CONFIG: MindConfig = {
131
159
  seed: 42,
132
160
  recallQueryK: 12,
133
161
  haloQueryK: 12,
162
+ pivotProbeK: 12,
163
+ rationaleSampleK: 12,
164
+ corpusLimitMax: 24,
165
+ corpusClimbs: 24,
166
+ corpusContextsPerClimb: 6,
167
+ corpusSampleProbes: 6000,
168
+ corpusPreviewBytes: 220,
169
+ corpusSampleFloorBytes: 12,
134
170
  normalizeEpsilon: 1e-12,
135
171
  cosineEpsilon: 1e-12,
136
172
  alu: {
@@ -172,6 +208,18 @@ export function resolveConfig(opts: Partial<MindConfig> = {}): MindConfig {
172
208
  seed: opts.seed ?? DEFAULT_CONFIG.seed,
173
209
  recallQueryK: opts.recallQueryK ?? DEFAULT_CONFIG.recallQueryK,
174
210
  haloQueryK: opts.haloQueryK ?? DEFAULT_CONFIG.haloQueryK,
211
+ pivotProbeK: opts.pivotProbeK ?? DEFAULT_CONFIG.pivotProbeK,
212
+ rationaleSampleK: opts.rationaleSampleK ?? DEFAULT_CONFIG.rationaleSampleK,
213
+ corpusLimitMax: opts.corpusLimitMax ?? DEFAULT_CONFIG.corpusLimitMax,
214
+ corpusClimbs: opts.corpusClimbs ?? DEFAULT_CONFIG.corpusClimbs,
215
+ corpusContextsPerClimb: opts.corpusContextsPerClimb ??
216
+ DEFAULT_CONFIG.corpusContextsPerClimb,
217
+ corpusSampleProbes: opts.corpusSampleProbes ??
218
+ DEFAULT_CONFIG.corpusSampleProbes,
219
+ corpusPreviewBytes: opts.corpusPreviewBytes ??
220
+ DEFAULT_CONFIG.corpusPreviewBytes,
221
+ corpusSampleFloorBytes: opts.corpusSampleFloorBytes ??
222
+ DEFAULT_CONFIG.corpusSampleFloorBytes,
175
223
  normalizeEpsilon: opts.normalizeEpsilon ?? DEFAULT_CONFIG.normalizeEpsilon,
176
224
  cosineEpsilon: opts.cosineEpsilon ?? DEFAULT_CONFIG.cosineEpsilon,
177
225
  alu: {