@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
package/AGENTS.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  The working manual for anyone (human or AI agent) changing Sema. For pattern
4
4
  detail, see `docs/INDEX.md` → `docs/architecture/*.md`. You should be able to
5
- develop against this document and docs/ alone; read the theory in
6
- `docs/architecture/` when you need why a pattern holds, not to get work done.
5
+ develop against this document and docs/ alone; read `docs/architecture/` for why
6
+ a pattern holds.
7
7
 
8
8
  ## 1. Orientation
9
9
 
@@ -14,7 +14,7 @@ accelerators), and a cost-based search that composes answers from stored facts
14
14
  the only runtime dependency.
15
15
 
16
16
  ```bash
17
- npm install # dev tooling + parquet reader used by one example
17
+ npm install # dev tooling + parquet reader for one example
18
18
  npm run build # tsc → dist/
19
19
  npm test # tsc && node --test test/**/*.test.mjs
20
20
  npm run demo # example/demo.ts — the four-note README demo
@@ -62,6 +62,7 @@ shared content-addressed ascent; `Precomputed` in
62
62
  `src/mind/pipeline-mechanism.ts` is the per-response lazy memo; `src/meter.ts`
63
63
  is the write-only work accounting surface. See `docs/INDEX.md` for the full
64
64
  contract table and `docs/architecture/factored-machinery.md` for ownership.
65
+ Tie-breaks are corpus-determined, but not interchangeable (`determinism.md`).
65
66
 
66
67
  ## 3. Where things live
67
68
 
@@ -89,7 +90,7 @@ contract table and `docs/architecture/factored-machinery.md` for ownership.
89
90
  | Sublibraries (own READMEs) | `src/derive/`, `src/alu/`, `src/rabitq-ivf/` |
90
91
 
91
92
  Mind functions are free functions over `MindContext` (`src/mind/types.ts`), not
92
- methods — `mind.ts` is a thin assembly that delegates.
93
+ methods; `mind.ts` is a thin assembly.
93
94
 
94
95
  ## 4. Recipes
95
96
 
@@ -97,7 +98,7 @@ methods — `mind.ts` is a thin assembly that delegates.
97
98
 
98
99
  Implement `PipelineMechanism` (`floor` → admissible bound or `null`; `run` →
99
100
  candidates with `bytes`/`accounted`/`moves`/`unexplained` + optional
100
- `scaffolding`/`complete`). Register via
101
+ `scaffolding`/`complete`/`used`). Register via
101
102
  `new Mind({ mechanismFactories: [host => yourMechanism(host)] })`. Verify the
102
103
  four market constraints (decoupled, declared competence, visible budget,
103
104
  evidence travels). → `docs/architecture/mechanism-market.md`
@@ -125,14 +126,13 @@ Run the full suite with your store substituted. → `docs/architecture/store.md`
125
126
  ## 5. Testing norms
126
127
 
127
128
  Tests are `node:test` suites in `test/*.test.mjs`, numbered by theme, run
128
- against built `dist/` (`npm test`; single suite:
129
- `node --test test/22-multihop.test.mjs` after `tsc`). New behaviour ⇒ test in
130
- the matching numbered suite or a new one. Many tests pin contracts that look
131
- like implementation details (ladder order, span-shape readings,
132
- `MechanismResult.complete`, fold invariance, recognition idempotence, honest
133
- silence). A simplification that fails an existing test is wrong until the test
134
- is proven wrong. Sublibraries test themselves in
135
- `src/{alu,derive,rabitq-ivf}/test/` with zero Sema dependency.
129
+ against built `dist/` (`npm test`; one suite:
130
+ `node --test test/22-multihop.test.mjs`). New behaviour ⇒ a test in the matching
131
+ numbered suite. Many tests pin contracts that look like implementation details
132
+ (ladder order, span-shape readings, `MechanismResult.complete`, fold invariance,
133
+ recognition idempotence, honest silence). A simplification that fails an
134
+ existing test is wrong until the test is proven wrong. Sublibraries test
135
+ themselves in `src/{alu,derive,rabitq-ivf}/test/` with zero Sema dependency.
136
136
 
137
137
  ## 6. Instrumentation — the meter and the rationale ARE the dev surface
138
138
 
@@ -143,17 +143,17 @@ instrumentation, and the only ones. Both are read through the public path —
143
143
  and the `inspectRationale` callback on `respond`/`respondText`/`respondTurn`.
144
144
 
145
145
  When a change needs to be seen, measured, or proved, EXTEND THEM: a counter in
146
- `meter.ts` (the one place a counter name exists — keep its four contracts true),
147
- a step or note where the mechanism emits it (`src/mind/trace.ts` holds the move
148
- vocabulary). A gap in instrumentation is a defect IN the instrumentation: close
149
- it there, once, so the next person sees it too. Never add a parallel channel for
150
- a single investigation — no ad-hoc logging or timing probes left in `src/`
151
- (`performance.now()` belongs in `meter.ts`, not at a call site), no private
152
- per-layer counter where a `meter.ts` field belongs, and no trace channel of your
153
- own: a callback threaded through a call chain must FEED the rationale, the way
154
- `GraphSearch`'s `onDerivation` feeds `traceDerivation`. (`store.ts`'s
155
- `danglingReads`/`compactFailures` and the `console.warn`s that report them
156
- predate this and stay: session-lifetime HEALTH counters, not per-response work.)
146
+ `meter.ts` (the one place a counter name exists), a step or note where the
147
+ mechanism emits it (`src/mind/trace.ts` holds the move vocabulary). A gap in
148
+ instrumentation is a defect IN the instrumentation: close it there, once, so the
149
+ next person sees it too. Never add a parallel channel for a single investigation
150
+ — no ad-hoc logging or timing probes left in `src/` (`performance.now()` belongs
151
+ in `meter.ts`, not at a call site), no private per-layer counter where a
152
+ `meter.ts` field belongs, and no trace channel of your own: a callback threaded
153
+ through a call chain must FEED the rationale, the way `GraphSearch`'s
154
+ `onDerivation` feeds `traceDerivation`. (`store.ts`'s
155
+ `danglingReads`/`compactFailures` and the `console.warn`s that report them stay:
156
+ session-lifetime HEALTH counters, not per-response work.)
157
157
 
158
158
  ## 7. Dependencies and licensing
159
159
 
@@ -161,8 +161,8 @@ PolyForm Noncommercial 1.0.0 with separate commercial licensing (see
161
161
  `LICENSE.md`, `COMMERCIAL-LICENSE.md`, `TRADEMARKS.md`). The library has **no
162
162
  runtime dependencies** — pinned by `test/88-dependency-footprint.test.mjs`
163
163
  (`dist/src` may import only `node:` builtins and relative paths; `package.json`
164
- has no `dependencies`). Examples may use dev dependencies lazily via dynamic
165
- import only on the code path that needs them (`example/train_base` + `hyparquet`
166
- is the reference). Training corpora: a store retains text verbatim, so upstream
167
- licences apply in full — NonCommercial and ShareAlike corpora cannot enter a
168
- trainer; see `DATASETS.md`.
164
+ has no `dependencies`). Examples may use dev dependencies via dynamic import
165
+ only where needed (`example/train_base` + `hyparquet` is the reference).
166
+ Training corpora: a store retains text verbatim, so upstream licences apply in
167
+ full — NonCommercial and ShareAlike corpora cannot enter a trainer; see
168
+ `DATASETS.md`.
package/TRADEMARKS.md CHANGED
@@ -6,7 +6,6 @@ The following are not licensed under the software license:
6
6
 
7
7
  - redistribute its algorithmic logic: that is, how algorithms and mathematical
8
8
  techniques are combined to develop its machinery;
9
- - visual identity;
10
9
  - project name;
11
10
  - logos;
12
11
  - icons;
@@ -100,6 +100,34 @@ export interface MindConfig {
100
100
  seed: number;
101
101
  recallQueryK: number;
102
102
  haloQueryK: number;
103
+ /** Branch nodes the pivot sweep may PROBE looking for the learnt context an
104
+ * answer contains — the pivot's own shortlist capacity, separate from
105
+ * `recallQueryK` because they are different quantities: this one bounds a
106
+ * MECHANICAL sweep over the answer's tree (breadth-first, largest regions
107
+ * first, so an exhausted allowance drops the far ones and never the near
108
+ * ones), while `recallQueryK` bounds the bridge's candidate reads. Sharing
109
+ * one number for both meant that tightening either silently starved the
110
+ * other — measured: at `recallQueryK: 1` the pivot cannot find a pivot at
111
+ * all. (`rationaleSampleK` was split out of `recallQueryK` for the same
112
+ * reason, found by an adversarial review.) */
113
+ pivotProbeK: number;
114
+ /** Corpus reading (see src/mind/corpus.ts): results per call, resolved
115
+ * nodes climbed from, contexts requested per climb, probes used to stride
116
+ * the id space when browsing, bytes of each side a preview keeps, and the
117
+ * smallest deposited note browsing will show. Capacities and budgets only —
118
+ * the one material floor (a resolved node must account for W bytes) is
119
+ * derived from the geometry, not declared here. */
120
+ corpusLimitMax: number;
121
+ corpusClimbs: number;
122
+ corpusContextsPerClimb: number;
123
+ corpusSampleProbes: number;
124
+ corpusPreviewBytes: number;
125
+ corpusSampleFloorBytes: number;
126
+ /** Items one rationale step may ITEMISE (the whole field is still counted in
127
+ * the step's note). A capacity of the rationale, not of recall: sharing
128
+ * `recallQueryK` meant `new Mind({recallQueryK: 100000})` un-bounded the very
129
+ * payload the bound exists for (found by an adversarial review). */
130
+ rationaleSampleK: number;
103
131
  normalizeEpsilon: number;
104
132
  cosineEpsilon: number;
105
133
  alu: AluConfig;
@@ -5,6 +5,14 @@ export const DEFAULT_CONFIG = {
5
5
  seed: 42,
6
6
  recallQueryK: 12,
7
7
  haloQueryK: 12,
8
+ pivotProbeK: 12,
9
+ rationaleSampleK: 12,
10
+ corpusLimitMax: 24,
11
+ corpusClimbs: 24,
12
+ corpusContextsPerClimb: 6,
13
+ corpusSampleProbes: 6000,
14
+ corpusPreviewBytes: 220,
15
+ corpusSampleFloorBytes: 12,
8
16
  normalizeEpsilon: 1e-12,
9
17
  cosineEpsilon: 1e-12,
10
18
  alu: {
@@ -44,6 +52,18 @@ export function resolveConfig(opts = {}) {
44
52
  seed: opts.seed ?? DEFAULT_CONFIG.seed,
45
53
  recallQueryK: opts.recallQueryK ?? DEFAULT_CONFIG.recallQueryK,
46
54
  haloQueryK: opts.haloQueryK ?? DEFAULT_CONFIG.haloQueryK,
55
+ pivotProbeK: opts.pivotProbeK ?? DEFAULT_CONFIG.pivotProbeK,
56
+ rationaleSampleK: opts.rationaleSampleK ?? DEFAULT_CONFIG.rationaleSampleK,
57
+ corpusLimitMax: opts.corpusLimitMax ?? DEFAULT_CONFIG.corpusLimitMax,
58
+ corpusClimbs: opts.corpusClimbs ?? DEFAULT_CONFIG.corpusClimbs,
59
+ corpusContextsPerClimb: opts.corpusContextsPerClimb ??
60
+ DEFAULT_CONFIG.corpusContextsPerClimb,
61
+ corpusSampleProbes: opts.corpusSampleProbes ??
62
+ DEFAULT_CONFIG.corpusSampleProbes,
63
+ corpusPreviewBytes: opts.corpusPreviewBytes ??
64
+ DEFAULT_CONFIG.corpusPreviewBytes,
65
+ corpusSampleFloorBytes: opts.corpusSampleFloorBytes ??
66
+ DEFAULT_CONFIG.corpusSampleFloorBytes,
47
67
  normalizeEpsilon: opts.normalizeEpsilon ?? DEFAULT_CONFIG.normalizeEpsilon,
48
68
  cosineEpsilon: opts.cosineEpsilon ?? DEFAULT_CONFIG.cosineEpsilon,
49
69
  alu: {
@@ -106,6 +106,27 @@ export declare function dominates(partLen: number, wholeLen: number): boolean;
106
106
  * Consumer: `companyProfile` (mind/learning.ts), which sizes its constituent
107
107
  * sketch at this capacity instead of a visit budget. */
108
108
  export declare function profileCapacity(D: number): number;
109
+ /**
110
+ * The POOLED-vote significance floor, and the derivation lives here because
111
+ * its PREMISE is a property of the caller's weighting.
112
+ *
113
+ * DERIVATION (docs/architecture/thresholds.md §2): a maximally-specific region
114
+ * contributes at most `ln N` to a pooled vote, so `ln(N) + 1/2` sits half a
115
+ * unit above ONE region's ceiling — it demands corroboration BEYOND a single
116
+ * region, which is what makes it a consensus bar rather than a resonance bar.
117
+ *
118
+ * PREMISE: that per-region ceiling is an IDF, `ln(N/c)` — attention.ts's
119
+ * `inverse` mode, the mode every non-test caller runs. The other two modes
120
+ * weight a region by `ln(1+c)` (`direct`) or `ln(N/c) + ln(1+c)` (`combined`),
121
+ * i.e. `ln N + ln(1 + 1/c)`, so they exceed the premise's ceiling by at most
122
+ * `ln 2` — a DERIVED bound, not a hole: the floor stays within `ln 2` of its
123
+ * own premise in every mode, and exactly on it in `inverse`.
124
+ *
125
+ * MEASURED: the floor is read on the pooled vote (`commitVotes`, `recall`,
126
+ * `cast`). Across 27 anchors on 6 queries, 11 cleared it by the sum and NONE
127
+ * by a single region's peak — gating on one region would refuse every elected
128
+ * root.
129
+ */
109
130
  export declare function consensusFloor(N: number): number;
110
131
  /** The coverage bar for the reach (interior) index, when vector-similarity
111
132
  * gating is used. Returns the concept threshold — the structural midpoint
@@ -143,6 +143,27 @@ export function dominates(partLen, wholeLen) {
143
143
  export function profileCapacity(D) {
144
144
  return Math.max(1, Math.floor(Math.sqrt(D)));
145
145
  }
146
+ /**
147
+ * The POOLED-vote significance floor, and the derivation lives here because
148
+ * its PREMISE is a property of the caller's weighting.
149
+ *
150
+ * DERIVATION (docs/architecture/thresholds.md §2): a maximally-specific region
151
+ * contributes at most `ln N` to a pooled vote, so `ln(N) + 1/2` sits half a
152
+ * unit above ONE region's ceiling — it demands corroboration BEYOND a single
153
+ * region, which is what makes it a consensus bar rather than a resonance bar.
154
+ *
155
+ * PREMISE: that per-region ceiling is an IDF, `ln(N/c)` — attention.ts's
156
+ * `inverse` mode, the mode every non-test caller runs. The other two modes
157
+ * weight a region by `ln(1+c)` (`direct`) or `ln(N/c) + ln(1+c)` (`combined`),
158
+ * i.e. `ln N + ln(1 + 1/c)`, so they exceed the premise's ceiling by at most
159
+ * `ln 2` — a DERIVED bound, not a hole: the floor stays within `ln 2` of its
160
+ * own premise in every mode, and exactly on it in `inverse`.
161
+ *
162
+ * MEASURED: the floor is read on the pooled vote (`commitVotes`, `recall`,
163
+ * `cast`). Across 27 anchors on 6 queries, 11 cleared it by the sum and NONE
164
+ * by a single region's peak — gating on one region would refuse every elected
165
+ * root.
166
+ */
146
167
  export function consensusFloor(N) {
147
168
  return Math.log(N) + 1 / 2;
148
169
  }
@@ -152,6 +152,82 @@ export declare class Meter {
152
152
  mechanismRuns: number;
153
153
  /** Candidates the decider weighed. */
154
154
  candidates: number;
155
+ /** `deriveThrough` yielded — a fact was reached through the subject the query
156
+ * never named. */
157
+ joinFired: number;
158
+ /** Refused: no key names the entity and the tail together. (A key that
159
+ * resolves but leads nowhere is not "refused" — it is not the relation, so
160
+ * the scan simply moves on; there is no counter for a case the loop cannot
161
+ * reach.) */
162
+ joinNoKey: number;
163
+ /** Refused: the fact contains no entity that leads anywhere. */
164
+ joinNoEntity: number;
165
+ /** `recompleteNode` re-covered a produced form — the descent that decomposes
166
+ * a completion by ITS OWN kids. Without this the descent is invisible: a
167
+ * caller could see the chain's result but not whether the recomposition
168
+ * happened, so "the recursion stopped" and "the recursion never ran" were
169
+ * indistinguishable from the counters alone. */
170
+ recompletes: number;
171
+ /** Times the reasoner pivoted on a span its answer contains and stepped
172
+ * across that fact. */
173
+ pivotSteps: number;
174
+ /** Canon probes REFUSED because the canon budget ran out — the one thing the
175
+ * budget does that nothing could see. The budget itself is derived
176
+ * (`bytes.length · chainReach(W)²`, recognition.ts), and the cheap exact route
177
+ * is deliberately unbudgeted, so this counter says exactly when the expensive
178
+ * route was priced out. Counted where the fact happens (the `!canonBudget`
179
+ * refusal), not where the probe is called. */
180
+ canonProbesDenied: number;
181
+ /** The pipeline's remainder AT THE DECISION POINT, in bytes: what the grounded
182
+ * answer plus the pre-computed spans left unexplained, after the same W floor
183
+ * the fuse gate uses. This is the quantity that licenses (or refuses) the
184
+ * post-grounding extension and the fusion — it was computed, used, and never
185
+ * published, so nothing could measure what a search had LEFT when it decided.
186
+ * Read with {@link postGroundingRemainderSpans}. */
187
+ postGroundingRemainderBytes: number;
188
+ /** How many spans that remainder consists of (each at least one W window). */
189
+ postGroundingRemainderSpans: number;
190
+ /** Times `fuseAttention` produced a FUSED answer — not times it was called.
191
+ * It is entered whenever the query has a remainder ≥ W and returns early when
192
+ * there is nothing to bridge (`containsSpan`, a lone root, an empty pass), so
193
+ * the call and the fact are different things and only the fact is counted.
194
+ * Its own rationale step reports the fusion; this is the untraced view, and
195
+ * its cost is one bridging edge: `fuseRuns · STEP`. */
196
+ fuseRuns: number;
197
+ /** Steps the post-grounding EXTENSION took — pivots plus forward-absorbs.
198
+ * `pivotSteps` counts only the former, so before this the extension's COST was
199
+ * not computable at all. With it, the price of extending the answer is
200
+ * `reasonSteps · STEP`, the ladder's own value for following an edge. */
201
+ reasonSteps: number;
202
+ /** Bytes of the grounding's UNCOVERED material the extension was justified by
203
+ * — the union of the spans each step carried a `W`-window of. The gate
204
+ * already computed WHICH span carried it per step and kept only a boolean;
205
+ * this is that fact, accumulated. Read with {@link reasonSteps}: one is the
206
+ * price, the other the explanation. */
207
+ reasonCarriedBytes: number;
208
+ /** Branch-node probes the pivot sweep actually spent looking for the learnt
209
+ * context an answer contains (one `resonate` per probe). The untraced view
210
+ * of what the multi-hop's shortlist costs. */
211
+ pivotProbes: number;
212
+ /** Branch nodes the pivot's probe cap withheld (`branchCount − probeCap`, over
213
+ * every call). A capacity fact, not a verdict: the sweep is breadth-first,
214
+ * so the probes it DOES spend are the largest regions, and recognition still
215
+ * contributes every exact containment candidate regardless of the budget.
216
+ * Read it with {@link pivotProbes} — one says the work, the other the
217
+ * shortfall. */
218
+ pivotBranchesUnprobed: number;
219
+ /** `bridge` calls the cover makes assembling connectors (pairwise + n-ary). */
220
+ coverBridges: number;
221
+ /** Continuations a CHAIN hop offered the search. Bounded by the question
222
+ * (`ceil(queryLen / W)`) rather than by the corpus's fan-out — measured on a
223
+ * hub of degree 1083, offering every continuation grew the chart to 3113 outs
224
+ * and cost a 270 MB peak / 256 MB OOM for a two-word question. */
225
+ chainOffers: number;
226
+ /** Σ byte-allowance the n-ary interior passes those bridges. The allowance
227
+ * is `middleBytes + (m + 1) * W` — every intermediate answer's bytes plus
228
+ * one window of glue per joint — so it is the quantity that grows with a hub
229
+ * query's answers, and the first thing to read when the peak moves. */
230
+ coverAllowanceBytes: number;
155
231
  private readonly _phases;
156
232
  private readonly _t0;
157
233
  /** Every work counter's current value, by name — the snapshot `time`
package/dist/src/meter.js CHANGED
@@ -149,6 +149,101 @@ export class Meter {
149
149
  mechanismRuns = 0;
150
150
  /** Candidates the decider weighed. */
151
151
  candidates = 0;
152
+ // ── Graph search: the fact join (DIRECTION) ─────────────────────────────
153
+ //
154
+ // The join's outcome was observable ONLY through the rationale, and the
155
+ // rationale PERTURBS the search (measured: appending text to a refusal note
156
+ // changed a traced answer). These four counters are the untraced view — the
157
+ // same surface every other work counter uses, incremented where the decision
158
+ // is made, never behind a trace guard.
159
+ /** `deriveThrough` yielded — a fact was reached through the subject the query
160
+ * never named. */
161
+ joinFired = 0;
162
+ /** Refused: no key names the entity and the tail together. (A key that
163
+ * resolves but leads nowhere is not "refused" — it is not the relation, so
164
+ * the scan simply moves on; there is no counter for a case the loop cannot
165
+ * reach.) */
166
+ joinNoKey = 0;
167
+ /** Refused: the fact contains no entity that leads anywhere. */
168
+ joinNoEntity = 0;
169
+ /** `recompleteNode` re-covered a produced form — the descent that decomposes
170
+ * a completion by ITS OWN kids. Without this the descent is invisible: a
171
+ * caller could see the chain's result but not whether the recomposition
172
+ * happened, so "the recursion stopped" and "the recursion never ran" were
173
+ * indistinguishable from the counters alone. */
174
+ recompletes = 0;
175
+ // ── Mind: the multi-hop pivot (EXTENSION) ───────────────────────────────
176
+ //
177
+ // `pivotStep` was observable only through the rationale, and the rationale
178
+ // perturbs the search (measured). How far the reasoner hopped is a
179
+ // BEHAVIOUR, so it needs an untraced view: one counter, incremented where the
180
+ // step is emitted.
181
+ /** Times the reasoner pivoted on a span its answer contains and stepped
182
+ * across that fact. */
183
+ pivotSteps = 0;
184
+ /** Canon probes REFUSED because the canon budget ran out — the one thing the
185
+ * budget does that nothing could see. The budget itself is derived
186
+ * (`bytes.length · chainReach(W)²`, recognition.ts), and the cheap exact route
187
+ * is deliberately unbudgeted, so this counter says exactly when the expensive
188
+ * route was priced out. Counted where the fact happens (the `!canonBudget`
189
+ * refusal), not where the probe is called. */
190
+ canonProbesDenied = 0;
191
+ /** The pipeline's remainder AT THE DECISION POINT, in bytes: what the grounded
192
+ * answer plus the pre-computed spans left unexplained, after the same W floor
193
+ * the fuse gate uses. This is the quantity that licenses (or refuses) the
194
+ * post-grounding extension and the fusion — it was computed, used, and never
195
+ * published, so nothing could measure what a search had LEFT when it decided.
196
+ * Read with {@link postGroundingRemainderSpans}. */
197
+ postGroundingRemainderBytes = 0;
198
+ /** How many spans that remainder consists of (each at least one W window). */
199
+ postGroundingRemainderSpans = 0;
200
+ /** Times `fuseAttention` produced a FUSED answer — not times it was called.
201
+ * It is entered whenever the query has a remainder ≥ W and returns early when
202
+ * there is nothing to bridge (`containsSpan`, a lone root, an empty pass), so
203
+ * the call and the fact are different things and only the fact is counted.
204
+ * Its own rationale step reports the fusion; this is the untraced view, and
205
+ * its cost is one bridging edge: `fuseRuns · STEP`. */
206
+ fuseRuns = 0;
207
+ /** Steps the post-grounding EXTENSION took — pivots plus forward-absorbs.
208
+ * `pivotSteps` counts only the former, so before this the extension's COST was
209
+ * not computable at all. With it, the price of extending the answer is
210
+ * `reasonSteps · STEP`, the ladder's own value for following an edge. */
211
+ reasonSteps = 0;
212
+ /** Bytes of the grounding's UNCOVERED material the extension was justified by
213
+ * — the union of the spans each step carried a `W`-window of. The gate
214
+ * already computed WHICH span carried it per step and kept only a boolean;
215
+ * this is that fact, accumulated. Read with {@link reasonSteps}: one is the
216
+ * price, the other the explanation. */
217
+ reasonCarriedBytes = 0;
218
+ /** Branch-node probes the pivot sweep actually spent looking for the learnt
219
+ * context an answer contains (one `resonate` per probe). The untraced view
220
+ * of what the multi-hop's shortlist costs. */
221
+ pivotProbes = 0;
222
+ /** Branch nodes the pivot's probe cap withheld (`branchCount − probeCap`, over
223
+ * every call). A capacity fact, not a verdict: the sweep is breadth-first,
224
+ * so the probes it DOES spend are the largest regions, and recognition still
225
+ * contributes every exact containment candidate regardless of the budget.
226
+ * Read it with {@link pivotProbes} — one says the work, the other the
227
+ * shortfall. */
228
+ pivotBranchesUnprobed = 0;
229
+ // ── Mind: the cover's connector assembly (LIMIT) ────────────────────────
230
+ //
231
+ // The cover's `run` is 91% of a hub query's time (`"Hello."`: 2.7 s of 3.0 s)
232
+ // and holds its ~270 MB peak, and none of it was countable: `searchPushes`
233
+ // and `candidates` do not see the connector assembly. These two counters are
234
+ // the untraced view of it.
235
+ /** `bridge` calls the cover makes assembling connectors (pairwise + n-ary). */
236
+ coverBridges = 0;
237
+ /** Continuations a CHAIN hop offered the search. Bounded by the question
238
+ * (`ceil(queryLen / W)`) rather than by the corpus's fan-out — measured on a
239
+ * hub of degree 1083, offering every continuation grew the chart to 3113 outs
240
+ * and cost a 270 MB peak / 256 MB OOM for a two-word question. */
241
+ chainOffers = 0;
242
+ /** Σ byte-allowance the n-ary interior passes those bridges. The allowance
243
+ * is `middleBytes + (m + 1) * W` — every intermediate answer's bytes plus
244
+ * one window of glue per joint — so it is the quantity that grows with a hub
245
+ * query's answers, and the first thing to read when the peak moves. */
246
+ coverAllowanceBytes = 0;
152
247
  // ── Phases ──────────────────────────────────────────────────────────────
153
248
  _phases = new Map();
154
249
  _t0 = performance.now();
@@ -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;