@hviana/sema 0.8.2 → 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.
- package/AGENTS.md +29 -29
- package/TRADEMARKS.md +0 -1
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.js +2 -0
- package/dist/src/geometry.d.ts +21 -0
- package/dist/src/geometry.js +21 -0
- package/dist/src/meter.d.ts +51 -0
- package/dist/src/meter.js +51 -0
- package/dist/src/mind/attention.d.ts +4 -0
- package/dist/src/mind/attention.js +165 -16
- package/dist/src/mind/canonical.d.ts +16 -0
- package/dist/src/mind/canonical.js +41 -0
- package/dist/src/mind/graph-search.js +33 -14
- package/dist/src/mind/match.d.ts +1 -1
- package/dist/src/mind/match.js +5 -3
- package/dist/src/mind/mechanisms/cast.js +1 -1
- package/dist/src/mind/mechanisms/confluence.js +24 -0
- package/dist/src/mind/mechanisms/recall.js +32 -4
- package/dist/src/mind/mind.d.ts +4 -2
- package/dist/src/mind/mind.js +5 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +7 -0
- package/dist/src/mind/pipeline.js +41 -14
- package/dist/src/mind/primitives.js +9 -1
- package/dist/src/mind/rationale.d.ts +28 -1
- package/dist/src/mind/rationale.js +22 -1
- package/dist/src/mind/reasoning.d.ts +21 -3
- package/dist/src/mind/reasoning.js +73 -21
- package/dist/src/mind/recognition.js +4 -8
- package/dist/src/mind/resonance.js +20 -1
- package/dist/src/mind/trace.js +1 -0
- package/dist/src/mind/traverse.js +6 -2
- package/dist/src/mind/types.d.ts +36 -13
- package/docs/INVARIANTS.md +2 -2
- package/docs/architecture/bounded-reads.md +1 -1
- package/docs/architecture/commonality.md +2 -2
- package/docs/architecture/cost-model.md +2 -2
- package/docs/architecture/determinism.md +7 -7
- package/docs/architecture/match-project.md +2 -3
- package/docs/architecture/mechanism-market.md +10 -10
- package/docs/architecture/meter.md +5 -5
- package/docs/architecture/store.md +3 -3
- package/docs/failures/tempting-but-wrong.md +3 -4
- package/docs/harness/gates.md +2 -2
- package/docs/mechanisms/cast.md +2 -2
- package/docs/mechanisms/cover.md +2 -3
- package/docs/mechanisms/extraction.md +7 -7
- package/docs/mechanisms/recall.md +8 -9
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +11 -12
- package/src/config.ts +13 -0
- package/src/geometry.ts +21 -0
- package/src/meter.ts +51 -0
- package/src/mind/attention.ts +167 -16
- package/src/mind/canonical.ts +43 -0
- package/src/mind/graph-search.ts +39 -14
- package/src/mind/match.ts +5 -3
- package/src/mind/mechanisms/cast.ts +3 -1
- package/src/mind/mechanisms/confluence.ts +24 -0
- package/src/mind/mechanisms/recall.ts +32 -4
- package/src/mind/mind.ts +6 -4
- package/src/mind/pipeline-mechanism.ts +7 -0
- package/src/mind/pipeline.ts +49 -16
- package/src/mind/primitives.ts +9 -1
- package/src/mind/rationale.ts +35 -1
- package/src/mind/reasoning.ts +92 -15
- package/src/mind/recognition.ts +4 -8
- package/src/mind/resonance.ts +19 -1
- package/src/mind/trace.ts +1 -0
- package/src/mind/traverse.ts +7 -5
- package/src/mind/types.ts +36 -13
- package/test/105-derive-through-reports-its-refusal.test.mjs +24 -0
- package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
- package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
- package/test/120-composition-is-consequence.test.mjs +132 -0
- package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
- package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
- package/test/123-the-paired-formulas-agree.test.mjs +90 -0
- package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
- package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
- package/test/129-the-trace-payload-shape.test.mjs +164 -0
- package/test/32-confluence.test.mjs +68 -0
- package/test/38-reason-restate-guard.test.mjs +8 -2
- package/test/43-cast-analog-seat.test.mjs +10 -0
- package/test/55-cost-meter.test.mjs +859 -0
|
@@ -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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
are ranked anchors from `climbAttentionAll`
|
|
15
|
-
|
|
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).
|
|
28
|
-
|
|
29
|
-
|
|
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`)
|
|
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 —
|
|
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
|
|
53
|
+
Scaffolding-only queries abstain: when every window that could anchor is
|
|
55
54
|
saturated (corpus-global scaffolding, `allWindowsAreScaffolding`), the bridge
|
|
56
|
-
returns nothing —
|
|
55
|
+
returns nothing — one substituted word cannot carry the load.
|
|
57
56
|
|
|
58
57
|
## Cost
|
|
59
58
|
|
package/jsr.json
CHANGED
package/package.json
CHANGED
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)`
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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/
|
|
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
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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,17 @@ 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;
|
|
119
130
|
/** Corpus reading (see src/mind/corpus.ts): results per call, resolved
|
|
120
131
|
* nodes climbed from, contexts requested per climb, probes used to stride
|
|
121
132
|
* the id space when browsing, bytes of each side a preview keeps, and the
|
|
@@ -148,6 +159,7 @@ export const DEFAULT_CONFIG: MindConfig = {
|
|
|
148
159
|
seed: 42,
|
|
149
160
|
recallQueryK: 12,
|
|
150
161
|
haloQueryK: 12,
|
|
162
|
+
pivotProbeK: 12,
|
|
151
163
|
rationaleSampleK: 12,
|
|
152
164
|
corpusLimitMax: 24,
|
|
153
165
|
corpusClimbs: 24,
|
|
@@ -196,6 +208,7 @@ export function resolveConfig(opts: Partial<MindConfig> = {}): MindConfig {
|
|
|
196
208
|
seed: opts.seed ?? DEFAULT_CONFIG.seed,
|
|
197
209
|
recallQueryK: opts.recallQueryK ?? DEFAULT_CONFIG.recallQueryK,
|
|
198
210
|
haloQueryK: opts.haloQueryK ?? DEFAULT_CONFIG.haloQueryK,
|
|
211
|
+
pivotProbeK: opts.pivotProbeK ?? DEFAULT_CONFIG.pivotProbeK,
|
|
199
212
|
rationaleSampleK: opts.rationaleSampleK ?? DEFAULT_CONFIG.rationaleSampleK,
|
|
200
213
|
corpusLimitMax: opts.corpusLimitMax ?? DEFAULT_CONFIG.corpusLimitMax,
|
|
201
214
|
corpusClimbs: opts.corpusClimbs ?? DEFAULT_CONFIG.corpusClimbs,
|
package/src/geometry.ts
CHANGED
|
@@ -155,6 +155,27 @@ export function profileCapacity(D: number): number {
|
|
|
155
155
|
return Math.max(1, Math.floor(Math.sqrt(D)));
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The POOLED-vote significance floor, and the derivation lives here because
|
|
160
|
+
* its PREMISE is a property of the caller's weighting.
|
|
161
|
+
*
|
|
162
|
+
* DERIVATION (docs/architecture/thresholds.md §2): a maximally-specific region
|
|
163
|
+
* contributes at most `ln N` to a pooled vote, so `ln(N) + 1/2` sits half a
|
|
164
|
+
* unit above ONE region's ceiling — it demands corroboration BEYOND a single
|
|
165
|
+
* region, which is what makes it a consensus bar rather than a resonance bar.
|
|
166
|
+
*
|
|
167
|
+
* PREMISE: that per-region ceiling is an IDF, `ln(N/c)` — attention.ts's
|
|
168
|
+
* `inverse` mode, the mode every non-test caller runs. The other two modes
|
|
169
|
+
* weight a region by `ln(1+c)` (`direct`) or `ln(N/c) + ln(1+c)` (`combined`),
|
|
170
|
+
* i.e. `ln N + ln(1 + 1/c)`, so they exceed the premise's ceiling by at most
|
|
171
|
+
* `ln 2` — a DERIVED bound, not a hole: the floor stays within `ln 2` of its
|
|
172
|
+
* own premise in every mode, and exactly on it in `inverse`.
|
|
173
|
+
*
|
|
174
|
+
* MEASURED: the floor is read on the pooled vote (`commitVotes`, `recall`,
|
|
175
|
+
* `cast`). Across 27 anchors on 6 queries, 11 cleared it by the sum and NONE
|
|
176
|
+
* by a single region's peak — gating on one region would refuse every elected
|
|
177
|
+
* root.
|
|
178
|
+
*/
|
|
158
179
|
export function consensusFloor(N: number): number {
|
|
159
180
|
return Math.log(N) + 1 / 2;
|
|
160
181
|
}
|
package/src/meter.ts
CHANGED
|
@@ -223,6 +223,12 @@ export class Meter {
|
|
|
223
223
|
joinNoKey = 0;
|
|
224
224
|
/** Refused: the fact contains no entity that leads anywhere. */
|
|
225
225
|
joinNoEntity = 0;
|
|
226
|
+
/** `recompleteNode` re-covered a produced form — the descent that decomposes
|
|
227
|
+
* a completion by ITS OWN kids. Without this the descent is invisible: a
|
|
228
|
+
* caller could see the chain's result but not whether the recomposition
|
|
229
|
+
* happened, so "the recursion stopped" and "the recursion never ran" were
|
|
230
|
+
* indistinguishable from the counters alone. */
|
|
231
|
+
recompletes = 0;
|
|
226
232
|
|
|
227
233
|
// ── Mind: the multi-hop pivot (EXTENSION) ───────────────────────────────
|
|
228
234
|
//
|
|
@@ -233,6 +239,51 @@ export class Meter {
|
|
|
233
239
|
/** Times the reasoner pivoted on a span its answer contains and stepped
|
|
234
240
|
* across that fact. */
|
|
235
241
|
pivotSteps = 0;
|
|
242
|
+
/** Canon probes REFUSED because the canon budget ran out — the one thing the
|
|
243
|
+
* budget does that nothing could see. The budget itself is derived
|
|
244
|
+
* (`bytes.length · chainReach(W)²`, recognition.ts), and the cheap exact route
|
|
245
|
+
* is deliberately unbudgeted, so this counter says exactly when the expensive
|
|
246
|
+
* route was priced out. Counted where the fact happens (the `!canonBudget`
|
|
247
|
+
* refusal), not where the probe is called. */
|
|
248
|
+
canonProbesDenied = 0;
|
|
249
|
+
/** The pipeline's remainder AT THE DECISION POINT, in bytes: what the grounded
|
|
250
|
+
* answer plus the pre-computed spans left unexplained, after the same W floor
|
|
251
|
+
* the fuse gate uses. This is the quantity that licenses (or refuses) the
|
|
252
|
+
* post-grounding extension and the fusion — it was computed, used, and never
|
|
253
|
+
* published, so nothing could measure what a search had LEFT when it decided.
|
|
254
|
+
* Read with {@link postGroundingRemainderSpans}. */
|
|
255
|
+
postGroundingRemainderBytes = 0;
|
|
256
|
+
/** How many spans that remainder consists of (each at least one W window). */
|
|
257
|
+
postGroundingRemainderSpans = 0;
|
|
258
|
+
/** Times `fuseAttention` produced a FUSED answer — not times it was called.
|
|
259
|
+
* It is entered whenever the query has a remainder ≥ W and returns early when
|
|
260
|
+
* there is nothing to bridge (`containsSpan`, a lone root, an empty pass), so
|
|
261
|
+
* the call and the fact are different things and only the fact is counted.
|
|
262
|
+
* Its own rationale step reports the fusion; this is the untraced view, and
|
|
263
|
+
* its cost is one bridging edge: `fuseRuns · STEP`. */
|
|
264
|
+
fuseRuns = 0;
|
|
265
|
+
/** Steps the post-grounding EXTENSION took — pivots plus forward-absorbs.
|
|
266
|
+
* `pivotSteps` counts only the former, so before this the extension's COST was
|
|
267
|
+
* not computable at all. With it, the price of extending the answer is
|
|
268
|
+
* `reasonSteps · STEP`, the ladder's own value for following an edge. */
|
|
269
|
+
reasonSteps = 0;
|
|
270
|
+
/** Bytes of the grounding's UNCOVERED material the extension was justified by
|
|
271
|
+
* — the union of the spans each step carried a `W`-window of. The gate
|
|
272
|
+
* already computed WHICH span carried it per step and kept only a boolean;
|
|
273
|
+
* this is that fact, accumulated. Read with {@link reasonSteps}: one is the
|
|
274
|
+
* price, the other the explanation. */
|
|
275
|
+
reasonCarriedBytes = 0;
|
|
276
|
+
/** Branch-node probes the pivot sweep actually spent looking for the learnt
|
|
277
|
+
* context an answer contains (one `resonate` per probe). The untraced view
|
|
278
|
+
* of what the multi-hop's shortlist costs. */
|
|
279
|
+
pivotProbes = 0;
|
|
280
|
+
/** Branch nodes the pivot's probe cap withheld (`branchCount − probeCap`, over
|
|
281
|
+
* every call). A capacity fact, not a verdict: the sweep is breadth-first,
|
|
282
|
+
* so the probes it DOES spend are the largest regions, and recognition still
|
|
283
|
+
* contributes every exact containment candidate regardless of the budget.
|
|
284
|
+
* Read it with {@link pivotProbes} — one says the work, the other the
|
|
285
|
+
* shortfall. */
|
|
286
|
+
pivotBranchesUnprobed = 0;
|
|
236
287
|
|
|
237
288
|
// ── Mind: the cover's connector assembly (LIMIT) ────────────────────────
|
|
238
289
|
//
|
package/src/mind/attention.ts
CHANGED
|
@@ -180,6 +180,10 @@ export interface ConsensusAnchorTrace {
|
|
|
180
180
|
|
|
181
181
|
pooledVote: number;
|
|
182
182
|
idfVote: number;
|
|
183
|
+
/** The LARGEST single-region contribution behind this anchor — the bar
|
|
184
|
+
* recall's own gate reads (mechanisms/recall.ts). Published so the one
|
|
185
|
+
* decision-making quantity the climb computes is not invisible. */
|
|
186
|
+
peak: number;
|
|
183
187
|
|
|
184
188
|
candidateBreadth: number;
|
|
185
189
|
contributingVotes: number;
|
|
@@ -1243,15 +1247,18 @@ export async function voteRegions(
|
|
|
1243
1247
|
}
|
|
1244
1248
|
contrastiveMargin = margin;
|
|
1245
1249
|
// Scaled by what this region does NOT address — see `cov` above.
|
|
1246
|
-
|
|
1247
|
-
|
|
1250
|
+
// The bar THIS gate applies: the estimator's noise scaled by what the
|
|
1251
|
+
// region does NOT address (`cov`). ONE definition, used by the rejection
|
|
1252
|
+
// path below and by the voted payload — the trace reports the applied bar.
|
|
1253
|
+
const appliedFloor = estimatorNoise(ctx.store.D) * (1 - cov);
|
|
1254
|
+
if (margin <= appliedFloor) {
|
|
1248
1255
|
recordRegion("contrastive-margin-rejection", {
|
|
1249
1256
|
selected,
|
|
1250
1257
|
reachNode: voterId,
|
|
1251
1258
|
idf,
|
|
1252
1259
|
dfWeight: wf,
|
|
1253
1260
|
contrastiveMargin: margin,
|
|
1254
|
-
contrastiveNoiseFloor:
|
|
1261
|
+
contrastiveNoiseFloor: appliedFloor,
|
|
1255
1262
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
1256
1263
|
});
|
|
1257
1264
|
continue;
|
|
@@ -1305,7 +1312,12 @@ export async function voteRegions(
|
|
|
1305
1312
|
...(contrastiveMargin !== undefined
|
|
1306
1313
|
? {
|
|
1307
1314
|
contrastiveMargin,
|
|
1308
|
-
|
|
1315
|
+
// THE BAR THE GATE ACTUALLY APPLIED — the same expression
|
|
1316
|
+
// the rejection path's `appliedFloor` defines, inline here because
|
|
1317
|
+
// this payload is built in a scope that does not carry that local.
|
|
1318
|
+
// Publishing the raw estimatorNoise(D) instead made a region that
|
|
1319
|
+
// PASSED look closer to its limit than it was.
|
|
1320
|
+
contrastiveNoiseFloor: estimatorNoise(ctx.store.D) * (1 - cov),
|
|
1309
1321
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
1310
1322
|
}
|
|
1311
1323
|
: {}),
|
|
@@ -1438,7 +1450,15 @@ export function poolVotes(
|
|
|
1438
1450
|
},
|
|
1439
1451
|
pool,
|
|
1440
1452
|
};
|
|
1441
|
-
|
|
1453
|
+
// THE SEARCH WAS THE ONE LAYER WITH NO TIME. The climb's phases are timed
|
|
1454
|
+
// (voteRegions, structuralResonance, crossRegion) but the pooled derivation
|
|
1455
|
+
// was not, so any cost or gain inside it stayed invisible.
|
|
1456
|
+
// `timeSync`, not `time`: the search is SYNCHRONOUS, and wrapping it in a
|
|
1457
|
+
// promise only to time it would make the profiled path wait where the
|
|
1458
|
+
// unprofiled one does not (meter.ts's own contract).
|
|
1459
|
+
if (ctx.meter) {
|
|
1460
|
+
ctx.meter.timeSync("climb.derivation", () => lightestDerivation(system));
|
|
1461
|
+
} else lightestDerivation(system);
|
|
1442
1462
|
|
|
1443
1463
|
const votes = new Map<number, number>();
|
|
1444
1464
|
const votesIdf = new Map<number, number>();
|
|
@@ -1476,12 +1496,24 @@ export function poolVotes(
|
|
|
1476
1496
|
// The LARGEST single region's contribution to this anchor's pooled vote.
|
|
1477
1497
|
// The pool is a SUM (deliberately — see the pooling note above), so it says
|
|
1478
1498
|
// how much evidence there is in total, never whether any ONE place in the
|
|
1479
|
-
// query carries evidence on its own.
|
|
1480
|
-
//
|
|
1481
|
-
//
|
|
1482
|
-
//
|
|
1483
|
-
//
|
|
1484
|
-
//
|
|
1499
|
+
// query carries evidence on its own. Recorded here, beside the count,
|
|
1500
|
+
// because this is the only place the per-region contributions are still
|
|
1501
|
+
// separable.
|
|
1502
|
+
//
|
|
1503
|
+
// THE BAR IS THE POOLED FLOOR, AND IT WAS ONCE CLAIMED OTHERWISE HERE.
|
|
1504
|
+
// This comment used to say that holding an anchor to consensusFloor(N)
|
|
1505
|
+
// "prices ONE region's evidence", so comparing a six-region sum against it
|
|
1506
|
+
// was "a dimensional error". THAT WAS FALSE. `thresholds.md` §2 derives
|
|
1507
|
+
// `consensusFloor` as the POOLED-vote significance floor ("each region
|
|
1508
|
+
// contributes at most ln(N/c) <= ln(N); ln(N) + 1/2 demands ..."), and the
|
|
1509
|
+
// climb weights by IDF, so the sum and the floor are in ONE dimension —
|
|
1510
|
+
// which is exactly why `recall.ts` gates `forest[0].idfVote` against it and
|
|
1511
|
+
// why `commitVotes` does too. The other two weighting modes DO leave that
|
|
1512
|
+
// dimension (by at most ln 2, two-sided: `direct` deflates a region and
|
|
1513
|
+
// `combined` inflates it), and the gates therefore read the IDF sum, which
|
|
1514
|
+
// is mode-independent; `test/55` tests 19 and 20 pin both halves — the sum
|
|
1515
|
+
// as the reading the bar is derived for, and the absence of any gate
|
|
1516
|
+
// inversion across the three modes.
|
|
1485
1517
|
const regionPeak = new Map<number, number>();
|
|
1486
1518
|
const steps: DerivationStep[] = [];
|
|
1487
1519
|
let order = 0;
|
|
@@ -1665,6 +1697,7 @@ export function commitVotes(
|
|
|
1665
1697
|
anchor,
|
|
1666
1698
|
vote,
|
|
1667
1699
|
peak: regionPeak.get(anchor) ?? 0,
|
|
1700
|
+
idfVote: votesIdf.get(anchor) ?? 0,
|
|
1668
1701
|
start: s.start,
|
|
1669
1702
|
end: s.end,
|
|
1670
1703
|
breadth: (regionSupport.get(anchor) ?? 0) / totalRegions,
|
|
@@ -1674,6 +1707,16 @@ export function commitVotes(
|
|
|
1674
1707
|
),
|
|
1675
1708
|
};
|
|
1676
1709
|
})
|
|
1710
|
+
// THE ORDER IS NOT A PREFERENCE: with equal evidence it decides ADMISSION,
|
|
1711
|
+
// through the stable sort and the first-come overlap absorption below.
|
|
1712
|
+
// Measured on test/34's corpus, query "red": the two candidates (`red
|
|
1713
|
+
// circle` and `red square`) carry IDENTICAL `vote` and IDENTICAL `idfVote`
|
|
1714
|
+
// (1.3863 each, three seeds), so this comparator leaves them tied and the
|
|
1715
|
+
// stable sort keeps the ENUMERATION order — which is corpus-determined and
|
|
1716
|
+
// admits `red square`, 60/60 seeds. Adding an id tie-break (`|| a.anchor -
|
|
1717
|
+
// b.anchor`) picks `red circle` instead and makes a single region reach the
|
|
1718
|
+
// JOINT context, which is the premise `test/34` exists to protect. The
|
|
1719
|
+
// gates read IDF; this line only decides who gets looked at first.
|
|
1677
1720
|
.sort((a, b) => b.vote - a.vote);
|
|
1678
1721
|
const overlaps = (a: Attention, b: Attention) =>
|
|
1679
1722
|
a.start < b.end && b.start < a.end;
|
|
@@ -1719,6 +1762,13 @@ export function commitVotes(
|
|
|
1719
1762
|
rank,
|
|
1720
1763
|
pooledVote: point.vote,
|
|
1721
1764
|
idfVote: votesIdf.get(point.anchor) ?? 0,
|
|
1765
|
+
// The LARGEST single-region contribution behind this anchor — the bar
|
|
1766
|
+
// recall's own gate reads (mechanisms/recall.ts: forest[0].peak > LN2),
|
|
1767
|
+
// and until now the only decision-making quantity the climb computed and
|
|
1768
|
+
// did not publish. `regionPeak` reached `ranked` (see its build below)
|
|
1769
|
+
// and stopped there. Published, not recomputed: the value is the one the
|
|
1770
|
+
// climb already carries.
|
|
1771
|
+
peak: point.peak,
|
|
1722
1772
|
candidateBreadth: regions.length,
|
|
1723
1773
|
contributingVotes: regionAxioms.get(point.anchor) ?? 0,
|
|
1724
1774
|
contributingEvidence: regionSupport.get(point.anchor) ?? 0,
|
|
@@ -1748,6 +1798,18 @@ export function commitVotes(
|
|
|
1748
1798
|
let passesConsensusFloor: boolean | undefined;
|
|
1749
1799
|
let pastLeadingSaturation: boolean | undefined;
|
|
1750
1800
|
let tiedWithDominant: boolean | undefined;
|
|
1801
|
+
// ── ONE OF THREE ADMISSIONS, AND THEY ARE NOT THE SAME READING ────────
|
|
1802
|
+
// This block admits by VOTES: per-region evidence pooled, gated on the
|
|
1803
|
+
// natural break and on consensusFloor, with the dominant allowed to bypass
|
|
1804
|
+
// both. `structuralResonance` admits by a MARGIN over the estimator's own
|
|
1805
|
+
// noise, and `crossRegionVotes` admits by STRUCTURE (which regions may pair
|
|
1806
|
+
// at all, with at least one side individually discriminative). Read
|
|
1807
|
+
// together they look like one policy written three times; they are three
|
|
1808
|
+
// different measurements of the same question ("is this evidence?"), and
|
|
1809
|
+
// unifying them would average three readings into one — the mistake
|
|
1810
|
+
// `extraction.ts` records as "do not unify the two into one machine".
|
|
1811
|
+
// What they DO share, and must keep sharing, is the discipline of deriving
|
|
1812
|
+
// every bar from D/W/N rather than choosing it (thresholds.md).
|
|
1751
1813
|
const rejectionReasons: AnchorRejectionReason[] = [];
|
|
1752
1814
|
if (absorbed) {
|
|
1753
1815
|
status = "overlap";
|
|
@@ -1757,9 +1819,75 @@ export function commitVotes(
|
|
|
1757
1819
|
pastLeadingSaturation = pastLeading;
|
|
1758
1820
|
const vote = votesIdf.get(point.anchor) ?? 0;
|
|
1759
1821
|
if (roots.length === 0) {
|
|
1760
|
-
//
|
|
1761
|
-
//
|
|
1762
|
-
//
|
|
1822
|
+
// THE DOMINANCE PRIVILEGE, AND THE TENSION IT CARRIES (measured).
|
|
1823
|
+
//
|
|
1824
|
+
// The first non-overlapping candidate is DOMINANT: it bypasses both
|
|
1825
|
+
// vote gates below and grounds on its own; only the leading-saturation
|
|
1826
|
+
// gate still applies to it. The privilege is load-bearing — analogies,
|
|
1827
|
+
// substitutions and composed contexts are precisely candidates the
|
|
1828
|
+
// query does NOT contain, and the engine loses them without it.
|
|
1829
|
+
//
|
|
1830
|
+
// WHICH candidate receives it, though, is decided by this loop's ORDER.
|
|
1831
|
+
// That order comes from `ranked`, and when two candidates carry equal
|
|
1832
|
+
// evidence the stable sort preserves the ENUMERATION order, so the
|
|
1833
|
+
// privilege is allocated by an ordering rather than by a rule.
|
|
1834
|
+
//
|
|
1835
|
+
// Measured on test/34's corpus, query "red":
|
|
1836
|
+
//
|
|
1837
|
+
// 0:#77 vote=1.3863 idf=1.3863 [0,3) | 1:#49 vote=1.3863 idf=1.3863 [0,3)
|
|
1838
|
+
//
|
|
1839
|
+
// Both candidates (`red square` #77, `red circle` #49) have IDENTICAL
|
|
1840
|
+
// `vote` AND IDENTICAL `idfVote` over the SAME support span, so the
|
|
1841
|
+
// comparator leaves them tied, the second is absorbed as "overlap", and
|
|
1842
|
+
// the first grounds. With this build's enumeration order that first is
|
|
1843
|
+
// `red square`, 60/60 seeds, and `red circle` — the JOINT context — is
|
|
1844
|
+
// never reached by "red" alone. That is the premise test/34 exists to
|
|
1845
|
+
// protect: no single region reaches the joint context, which is what
|
|
1846
|
+
// makes the binding query unreachable without direct region
|
|
1847
|
+
// interaction.
|
|
1848
|
+
//
|
|
1849
|
+
// THE TENSION: the premise therefore holds BY ENUMERATION ORDER, not by
|
|
1850
|
+
// a rule, so any change to this ordering can move the privilege onto the
|
|
1851
|
+
// joint context and let one region reach it. Measured: adding
|
|
1852
|
+
// `|| a.anchor - b.anchor` — the lowest-id tie-break that AGENTS.md §2
|
|
1853
|
+
// sanctions as an equivalent corpus-determined tie-break — does exactly
|
|
1854
|
+
// that: "red" then attends to `red circle`, test/34 fails 6/1, and the
|
|
1855
|
+
// canonical suite reports 1 failure.
|
|
1856
|
+
//
|
|
1857
|
+
// TWO ATTEMPTS TO MAKE IT A RULE, BOTH REFUTED BY MEASUREMENT:
|
|
1858
|
+
//
|
|
1859
|
+
// 1. EVIDENCE SEPARATION. Grant the privilege only when the first
|
|
1860
|
+
// candidate's evidence is separated from the next distinct
|
|
1861
|
+
// candidate's by more than the co-dominant band (sqrt(k) *
|
|
1862
|
+
// estimatorNoise(D)). Refuted: that band exists to ADMIT the
|
|
1863
|
+
// anchors the estimator cannot separate from the dominant — its own
|
|
1864
|
+
// documented purpose — so withholding the privilege on ties removes
|
|
1865
|
+
// the very case it was written for. Suite: 4 failures (the two
|
|
1866
|
+
// co-dominant band laws, breadth/scale invariance, test/29 D2).
|
|
1867
|
+
//
|
|
1868
|
+
// 2. QUERY-OWNED CONTENT. Grant the privilege only to a candidate
|
|
1869
|
+
// that IS a recognised region's identity (regions.some(r => r.id ===
|
|
1870
|
+
// point.anchor)). Measured: for "circle" that identity IS the
|
|
1871
|
+
// ranked candidate, so the privilege stays and `circle` grounds; for
|
|
1872
|
+
// "red" the identity is the `red` node itself while the candidates
|
|
1873
|
+
// are the conjunctions, so neither is privileged; for "red then
|
|
1874
|
+
// circle" the composed context carries idf 3.958 and clears both
|
|
1875
|
+
// gates on its own evidence. All four control queries came out
|
|
1876
|
+
// right — and the suite: 10 failures, six of them in the
|
|
1877
|
+
// analogy/counterfactual/CAST suites ("an analogy still transfers
|
|
1878
|
+
// from a structure the query never names"; "a substitute the query
|
|
1879
|
+
// NAMES may still be voiced"). Refuted: the privilege exists to
|
|
1880
|
+
// admit what the query does NOT contain, so identity is the wrong
|
|
1881
|
+
// axis.
|
|
1882
|
+
//
|
|
1883
|
+
// WHAT A FUTURE ATTEMPT MUST RESPECT: whatever allocates this privilege
|
|
1884
|
+
// has to (a) keep it available to candidates the query does not contain
|
|
1885
|
+
// — analogies, substitutions, compositions — and (b) not depend on the
|
|
1886
|
+
// estimator's ordering among anchors of equal evidence, because that
|
|
1887
|
+
// ordering is not a fact about the corpus. No lever satisfying both has
|
|
1888
|
+
// been found. Until one is, this premise rests on the enumeration order
|
|
1889
|
+
// recorded above, and test/34 is the only test that notices if it
|
|
1890
|
+
// moves.
|
|
1763
1891
|
dominant = true;
|
|
1764
1892
|
if (pastLeading) {
|
|
1765
1893
|
status = "root";
|
|
@@ -1768,8 +1896,15 @@ export function commitVotes(
|
|
|
1768
1896
|
rejectionReasons.push("leading-saturation");
|
|
1769
1897
|
}
|
|
1770
1898
|
} else {
|
|
1771
|
-
|
|
1772
|
-
|
|
1899
|
+
// THE FLOOR AND THE BREAK READ THE IDF WEIGHTING. `floor` is derived
|
|
1900
|
+
// for pooled IDF-weighted votes, and `rootCut` comes from the IDF
|
|
1901
|
+
// distribution (`idfDesc`), so gating the mode-dependent `vote` against
|
|
1902
|
+
// either let a weighting mode change an admission (measured: anchor 87,
|
|
1903
|
+
// inverse 2.682 admitted vs direct 1.468 refused). Reading the IDF sum
|
|
1904
|
+
// makes the verdict mode-independent, and changes nothing in the
|
|
1905
|
+
// engine's own mode, where the two readings coincide.
|
|
1906
|
+
passesNaturalBreak = point.idfVote >= rootCut;
|
|
1907
|
+
passesConsensusFloor = point.idfVote >= floor;
|
|
1773
1908
|
// CO-DOMINANT — an anchor the estimator cannot separate from the
|
|
1774
1909
|
// dominant inherits the dominant's exemption, because that exemption's
|
|
1775
1910
|
// only warrant is being TOP, and "top" is not a fact about the corpus
|
|
@@ -2467,6 +2602,13 @@ export async function structuralResonance(
|
|
|
2467
2602
|
});
|
|
2468
2603
|
};
|
|
2469
2604
|
|
|
2605
|
+
// ── ADMISSION BY MARGIN, not by votes (see voteRegions' note) ─────────
|
|
2606
|
+
// What this site measures: how far the best ANN proposal's effective score
|
|
2607
|
+
// (score × semanticConfidence) stands above the runner-up's, against
|
|
2608
|
+
// `estimatorNoise(D)`. What it does NOT measure: how many regions voted,
|
|
2609
|
+
// or whether the query's regions agree — that is voteRegions' question, and
|
|
2610
|
+
// here a synthetic gist has already replaced them. The two bars are both
|
|
2611
|
+
// derived (thresholds.md), and neither is a tuning of the other.
|
|
2470
2612
|
let selected: StructuralResonanceProposal | null = null;
|
|
2471
2613
|
let selectedReach: AncestorReach | null = null;
|
|
2472
2614
|
let selectedIdf = 0;
|
|
@@ -2582,6 +2724,15 @@ async function crossRegionVotes(
|
|
|
2582
2724
|
// successfully reconstructed while probing one pair must not be read and
|
|
2583
2725
|
// perceived again while probing another pair in the same climb.
|
|
2584
2726
|
const siblingGistMemo = new Map<number, CachedSiblingGist>();
|
|
2727
|
+
// ── ADMISSION BY STRUCTURE, not by a bar (see voteRegions' note) ──────
|
|
2728
|
+
// What this site decides: WHICH regions may pair at all — a region that
|
|
2729
|
+
// already voted (individually discriminative), or a KNOWN non-voting one as
|
|
2730
|
+
// the weak side of a pair whose other side voted; never two non-voting
|
|
2731
|
+
// regions, and never a span contained in a maximal one whose reading is
|
|
2732
|
+
// exact. The bar (the container's idf) comes later, on the candidate. So
|
|
2733
|
+
// its "rejection reasons" name structural disqualifications — a different
|
|
2734
|
+
// vocabulary because it answers a different question, and the three
|
|
2735
|
+
// taxonomies stay separate for the same reason the readings do.
|
|
2585
2736
|
const votedSpans = new Set<string>();
|
|
2586
2737
|
for (const rv of rvs.votes) votedSpans.add(`${rv.start},${rv.end}`);
|
|
2587
2738
|
const seen = new Set<string>();
|
package/src/mind/canonical.ts
CHANGED
|
@@ -88,6 +88,49 @@ export function leafIdPrefix(
|
|
|
88
88
|
return ids;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/** Which prefixes of `prefix ‖ tail` are STORED NODES — as lengths in the
|
|
92
|
+
* tail's own coordinates, ascending, excluding the empty one. This is the
|
|
93
|
+
* candidate set a rule needs to join an already-stored prefix to a suffix it
|
|
94
|
+
* has not stored: a key names a relation exactly when `prefix ‖ tail[0..p]` IS
|
|
95
|
+
* a node, and a key can end strictly inside the tail without sitting on any
|
|
96
|
+
* fold boundary (a stored member's end is the end of ITS OWN stream, and the
|
|
97
|
+
* fold never emits a cut at a stream's end). Measured: "stockholm mayor"
|
|
98
|
+
* exists, leads on, and its boundary 6 is in neither the tail's cuts nor the
|
|
99
|
+
* concatenation's.
|
|
100
|
+
*
|
|
101
|
+
* ONE cheap content-addressed probe per offset — `leafIdPrefix` walks the bytes
|
|
102
|
+
* once (a point probe each), `findBranch` hashes the growing kid run — and NO
|
|
103
|
+
* `resolve`, which is what keeps this off the O(suffix) vector folds the
|
|
104
|
+
* recognition path pays. It stops at the first byte that was never interned,
|
|
105
|
+
* which costs nothing real: a stored key's bytes are interned by construction. */
|
|
106
|
+
export function keyEnds(
|
|
107
|
+
ctx: MindContext,
|
|
108
|
+
prefix: Uint8Array,
|
|
109
|
+
tail: Uint8Array,
|
|
110
|
+
): number[] {
|
|
111
|
+
if (prefix.length === 0 || tail.length === 0) return [];
|
|
112
|
+
const joined = new Uint8Array(prefix.length + tail.length);
|
|
113
|
+
joined.set(prefix, 0);
|
|
114
|
+
joined.set(tail, prefix.length);
|
|
115
|
+
const ids = leafIdPrefix(ctx, joined);
|
|
116
|
+
if (ids.length < prefix.length) return [];
|
|
117
|
+
const ends: number[] = [];
|
|
118
|
+
// The kid run GROWS by one id per offset; `findBranch` wants an array, so the
|
|
119
|
+
// run is built once and pushed into, never re-sliced. Re-slicing
|
|
120
|
+
// `ids.slice(0, prefix.length + p)` per offset made this O(|tail| ·
|
|
121
|
+
// (|prefix| + |tail|)) — quadratic in the tail, where the learning path this
|
|
122
|
+
// follows slices a run that SHRINKS. Same ends, linear copying.
|
|
123
|
+
const run = ids.slice(0, prefix.length);
|
|
124
|
+
// The loop ENDS at the first byte that was never interned (`ids.length`):
|
|
125
|
+
// every later prefix contains it, so none of them can be a node either — this
|
|
126
|
+
// is where the scan stops, not a silent truncation of the answer.
|
|
127
|
+
for (let p = 1; prefix.length + p <= ids.length; p++) {
|
|
128
|
+
run.push(ids[prefix.length + p - 1]);
|
|
129
|
+
if (ctx.store.findBranch(run) !== null) ends.push(p);
|
|
130
|
+
}
|
|
131
|
+
return ends;
|
|
132
|
+
}
|
|
133
|
+
|
|
91
134
|
/** The canonical W-window node ids of a byte stream, offset → id — the
|
|
92
135
|
* CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
|
|
93
136
|
* two deposits share IS the same node (hash-consing paid the comparison at
|