@hviana/sema 0.8.2 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +38 -37
- package/README.md +17 -38
- package/TRADEMARKS.md +0 -1
- package/dist/example/demo.js +85 -34
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.js +2 -0
- package/dist/src/geometry.d.ts +21 -10
- package/dist/src/geometry.js +21 -12
- package/dist/src/meter.d.ts +62 -0
- package/dist/src/meter.js +62 -0
- package/dist/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.d.ts +4 -0
- package/dist/src/mind/attention.js +167 -17
- package/dist/src/mind/canonical.d.ts +16 -0
- package/dist/src/mind/canonical.js +41 -0
- package/dist/src/mind/derivation.d.ts +201 -0
- package/dist/src/mind/derivation.js +327 -0
- package/dist/src/mind/graph-search.d.ts +2 -1
- package/dist/src/mind/graph-search.js +70 -29
- package/dist/src/mind/match.d.ts +3 -1
- package/dist/src/mind/match.js +7 -3
- package/dist/src/mind/mechanisms/alu.js +0 -2
- package/dist/src/mind/mechanisms/cast.d.ts +1 -5
- package/dist/src/mind/mechanisms/cast.js +16 -19
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
- package/dist/src/mind/mechanisms/confluence.js +27 -9
- package/dist/src/mind/mechanisms/cover.js +17 -20
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
- package/dist/src/mind/mechanisms/extraction.js +13 -8
- package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
- package/dist/src/mind/mechanisms/recall.d.ts +0 -1
- package/dist/src/mind/mechanisms/recall.js +40 -13
- package/dist/src/mind/mechanisms/reference.js +3 -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 -3
- package/dist/src/mind/pipeline.js +136 -44
- package/dist/src/mind/primitives.js +9 -1
- package/dist/src/mind/rationale.d.ts +21 -5
- package/dist/src/mind/rationale.js +16 -21
- package/dist/src/mind/reasoning.d.ts +12 -20
- package/dist/src/mind/reasoning.js +190 -106
- 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/dist/src/mind/types.js +6 -3
- package/docs/INDEX.md +23 -24
- package/docs/INVARIANTS.md +16 -17
- package/docs/architecture/bounded-reads.md +5 -5
- package/docs/architecture/closure.md +65 -0
- package/docs/architecture/commonality.md +29 -20
- package/docs/architecture/cost-model.md +7 -7
- package/docs/architecture/determinism.md +7 -7
- package/docs/architecture/exact-vs-approximate.md +4 -4
- package/docs/architecture/factored-machinery.md +14 -14
- package/docs/architecture/match-project.md +2 -3
- package/docs/architecture/mechanism-market.md +16 -16
- package/docs/architecture/meter.md +10 -11
- package/docs/architecture/store.md +4 -4
- package/docs/architecture/thresholds.md +1 -1
- package/docs/failures/tempting-but-wrong.md +14 -5
- package/docs/harness/gates.md +7 -7
- package/docs/mechanisms/cast.md +2 -2
- package/docs/mechanisms/cover.md +4 -5
- package/docs/mechanisms/extraction.md +7 -7
- package/docs/mechanisms/recall.md +8 -9
- package/example/demo.ts +90 -37
- 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 -13
- package/src/meter.ts +62 -0
- package/src/mind/articulation.ts +0 -1
- package/src/mind/attention.ts +169 -17
- package/src/mind/canonical.ts +43 -0
- package/src/mind/derivation.ts +473 -0
- package/src/mind/graph-search.ts +76 -34
- package/src/mind/match.ts +7 -3
- package/src/mind/mechanisms/alu.ts +0 -2
- package/src/mind/mechanisms/cast.ts +20 -22
- package/src/mind/mechanisms/confluence.ts +27 -13
- package/src/mind/mechanisms/cover.ts +17 -20
- package/src/mind/mechanisms/extraction.ts +13 -9
- package/src/mind/mechanisms/prefix-completion.ts +0 -1
- package/src/mind/mechanisms/recall.ts +39 -13
- package/src/mind/mechanisms/reference.ts +2 -3
- package/src/mind/mind.ts +6 -4
- package/src/mind/pipeline-mechanism.ts +7 -3
- package/src/mind/pipeline.ts +160 -52
- package/src/mind/primitives.ts +9 -1
- package/src/mind/rationale.ts +27 -23
- package/src/mind/reasoning.ts +227 -120
- 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 +41 -15
- 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/133-the-decision-point-renders-the-state.test.mjs +204 -0
- package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
- package/test/135-one-law-any-producer.test.mjs +289 -0
- package/test/136-the-two-named-limits.test.mjs +205 -0
- package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
- package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
- package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
- package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
- package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
- package/test/32-confluence.test.mjs +68 -0
- package/test/36-already-answered-fusion.test.mjs +20 -2
- package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
- package/test/38-reason-restate-guard.test.mjs +28 -2
- package/test/43-cast-analog-seat.test.mjs +10 -0
- package/test/55-cost-meter.test.mjs +862 -0
package/src/mind/attention.ts
CHANGED
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
} from "./junction.js";
|
|
65
65
|
import type { Vec } from "../vec.js";
|
|
66
66
|
import { indexOf } from "../bytes.js";
|
|
67
|
+
import { restates } from "./derivation.js";
|
|
67
68
|
import type { RationaleItem } from "./rationale.js";
|
|
68
69
|
import { rDeriv, rItem, rNode, traceDerivation } from "./trace.js";
|
|
69
70
|
|
|
@@ -180,6 +181,10 @@ export interface ConsensusAnchorTrace {
|
|
|
180
181
|
|
|
181
182
|
pooledVote: number;
|
|
182
183
|
idfVote: number;
|
|
184
|
+
/** The LARGEST single-region contribution behind this anchor — the bar
|
|
185
|
+
* recall's own gate reads (mechanisms/recall.ts). Published so the one
|
|
186
|
+
* decision-making quantity the climb computes is not invisible. */
|
|
187
|
+
peak: number;
|
|
183
188
|
|
|
184
189
|
candidateBreadth: number;
|
|
185
190
|
contributingVotes: number;
|
|
@@ -1243,15 +1248,18 @@ export async function voteRegions(
|
|
|
1243
1248
|
}
|
|
1244
1249
|
contrastiveMargin = margin;
|
|
1245
1250
|
// Scaled by what this region does NOT address — see `cov` above.
|
|
1246
|
-
|
|
1247
|
-
|
|
1251
|
+
// The bar THIS gate applies: the estimator's noise scaled by what the
|
|
1252
|
+
// region does NOT address (`cov`). ONE definition, used by the rejection
|
|
1253
|
+
// path below and by the voted payload — the trace reports the applied bar.
|
|
1254
|
+
const appliedFloor = estimatorNoise(ctx.store.D) * (1 - cov);
|
|
1255
|
+
if (margin <= appliedFloor) {
|
|
1248
1256
|
recordRegion("contrastive-margin-rejection", {
|
|
1249
1257
|
selected,
|
|
1250
1258
|
reachNode: voterId,
|
|
1251
1259
|
idf,
|
|
1252
1260
|
dfWeight: wf,
|
|
1253
1261
|
contrastiveMargin: margin,
|
|
1254
|
-
contrastiveNoiseFloor:
|
|
1262
|
+
contrastiveNoiseFloor: appliedFloor,
|
|
1255
1263
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
1256
1264
|
});
|
|
1257
1265
|
continue;
|
|
@@ -1305,7 +1313,12 @@ export async function voteRegions(
|
|
|
1305
1313
|
...(contrastiveMargin !== undefined
|
|
1306
1314
|
? {
|
|
1307
1315
|
contrastiveMargin,
|
|
1308
|
-
|
|
1316
|
+
// THE BAR THE GATE ACTUALLY APPLIED — the same expression
|
|
1317
|
+
// the rejection path's `appliedFloor` defines, inline here because
|
|
1318
|
+
// this payload is built in a scope that does not carry that local.
|
|
1319
|
+
// Publishing the raw estimatorNoise(D) instead made a region that
|
|
1320
|
+
// PASSED look closer to its limit than it was.
|
|
1321
|
+
contrastiveNoiseFloor: estimatorNoise(ctx.store.D) * (1 - cov),
|
|
1309
1322
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
1310
1323
|
}
|
|
1311
1324
|
: {}),
|
|
@@ -1438,7 +1451,15 @@ export function poolVotes(
|
|
|
1438
1451
|
},
|
|
1439
1452
|
pool,
|
|
1440
1453
|
};
|
|
1441
|
-
|
|
1454
|
+
// THE SEARCH WAS THE ONE LAYER WITH NO TIME. The climb's phases are timed
|
|
1455
|
+
// (voteRegions, structuralResonance, crossRegion) but the pooled derivation
|
|
1456
|
+
// was not, so any cost or gain inside it stayed invisible.
|
|
1457
|
+
// `timeSync`, not `time`: the search is SYNCHRONOUS, and wrapping it in a
|
|
1458
|
+
// promise only to time it would make the profiled path wait where the
|
|
1459
|
+
// unprofiled one does not (meter.ts's own contract).
|
|
1460
|
+
if (ctx.meter) {
|
|
1461
|
+
ctx.meter.timeSync("climb.derivation", () => lightestDerivation(system));
|
|
1462
|
+
} else lightestDerivation(system);
|
|
1442
1463
|
|
|
1443
1464
|
const votes = new Map<number, number>();
|
|
1444
1465
|
const votesIdf = new Map<number, number>();
|
|
@@ -1476,12 +1497,24 @@ export function poolVotes(
|
|
|
1476
1497
|
// The LARGEST single region's contribution to this anchor's pooled vote.
|
|
1477
1498
|
// The pool is a SUM (deliberately — see the pooling note above), so it says
|
|
1478
1499
|
// 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
|
-
//
|
|
1500
|
+
// query carries evidence on its own. Recorded here, beside the count,
|
|
1501
|
+
// because this is the only place the per-region contributions are still
|
|
1502
|
+
// separable.
|
|
1503
|
+
//
|
|
1504
|
+
// THE BAR IS THE POOLED FLOOR, AND IT WAS ONCE CLAIMED OTHERWISE HERE.
|
|
1505
|
+
// This comment used to say that holding an anchor to consensusFloor(N)
|
|
1506
|
+
// "prices ONE region's evidence", so comparing a six-region sum against it
|
|
1507
|
+
// was "a dimensional error". THAT WAS FALSE. `thresholds.md` §2 derives
|
|
1508
|
+
// `consensusFloor` as the POOLED-vote significance floor ("each region
|
|
1509
|
+
// contributes at most ln(N/c) <= ln(N); ln(N) + 1/2 demands ..."), and the
|
|
1510
|
+
// climb weights by IDF, so the sum and the floor are in ONE dimension —
|
|
1511
|
+
// which is exactly why `recall.ts` gates `forest[0].idfVote` against it and
|
|
1512
|
+
// why `commitVotes` does too. The other two weighting modes DO leave that
|
|
1513
|
+
// dimension (by at most ln 2, two-sided: `direct` deflates a region and
|
|
1514
|
+
// `combined` inflates it), and the gates therefore read the IDF sum, which
|
|
1515
|
+
// is mode-independent; `test/55` tests 19 and 20 pin both halves — the sum
|
|
1516
|
+
// as the reading the bar is derived for, and the absence of any gate
|
|
1517
|
+
// inversion across the three modes.
|
|
1485
1518
|
const regionPeak = new Map<number, number>();
|
|
1486
1519
|
const steps: DerivationStep[] = [];
|
|
1487
1520
|
let order = 0;
|
|
@@ -1665,6 +1698,7 @@ export function commitVotes(
|
|
|
1665
1698
|
anchor,
|
|
1666
1699
|
vote,
|
|
1667
1700
|
peak: regionPeak.get(anchor) ?? 0,
|
|
1701
|
+
idfVote: votesIdf.get(anchor) ?? 0,
|
|
1668
1702
|
start: s.start,
|
|
1669
1703
|
end: s.end,
|
|
1670
1704
|
breadth: (regionSupport.get(anchor) ?? 0) / totalRegions,
|
|
@@ -1674,6 +1708,16 @@ export function commitVotes(
|
|
|
1674
1708
|
),
|
|
1675
1709
|
};
|
|
1676
1710
|
})
|
|
1711
|
+
// THE ORDER IS NOT A PREFERENCE: with equal evidence it decides ADMISSION,
|
|
1712
|
+
// through the stable sort and the first-come overlap absorption below.
|
|
1713
|
+
// Measured on test/34's corpus, query "red": the two candidates (`red
|
|
1714
|
+
// circle` and `red square`) carry IDENTICAL `vote` and IDENTICAL `idfVote`
|
|
1715
|
+
// (1.3863 each, three seeds), so this comparator leaves them tied and the
|
|
1716
|
+
// stable sort keeps the ENUMERATION order — which is corpus-determined and
|
|
1717
|
+
// admits `red square`, 60/60 seeds. Adding an id tie-break (`|| a.anchor -
|
|
1718
|
+
// b.anchor`) picks `red circle` instead and makes a single region reach the
|
|
1719
|
+
// JOINT context, which is the premise `test/34` exists to protect. The
|
|
1720
|
+
// gates read IDF; this line only decides who gets looked at first.
|
|
1677
1721
|
.sort((a, b) => b.vote - a.vote);
|
|
1678
1722
|
const overlaps = (a: Attention, b: Attention) =>
|
|
1679
1723
|
a.start < b.end && b.start < a.end;
|
|
@@ -1719,6 +1763,13 @@ export function commitVotes(
|
|
|
1719
1763
|
rank,
|
|
1720
1764
|
pooledVote: point.vote,
|
|
1721
1765
|
idfVote: votesIdf.get(point.anchor) ?? 0,
|
|
1766
|
+
// The LARGEST single-region contribution behind this anchor — the bar
|
|
1767
|
+
// recall's own gate reads (mechanisms/recall.ts: forest[0].peak > LN2),
|
|
1768
|
+
// and until now the only decision-making quantity the climb computed and
|
|
1769
|
+
// did not publish. `regionPeak` reached `ranked` (see its build below)
|
|
1770
|
+
// and stopped there. Published, not recomputed: the value is the one the
|
|
1771
|
+
// climb already carries.
|
|
1772
|
+
peak: point.peak,
|
|
1722
1773
|
candidateBreadth: regions.length,
|
|
1723
1774
|
contributingVotes: regionAxioms.get(point.anchor) ?? 0,
|
|
1724
1775
|
contributingEvidence: regionSupport.get(point.anchor) ?? 0,
|
|
@@ -1748,6 +1799,18 @@ export function commitVotes(
|
|
|
1748
1799
|
let passesConsensusFloor: boolean | undefined;
|
|
1749
1800
|
let pastLeadingSaturation: boolean | undefined;
|
|
1750
1801
|
let tiedWithDominant: boolean | undefined;
|
|
1802
|
+
// ── ONE OF THREE ADMISSIONS, AND THEY ARE NOT THE SAME READING ────────
|
|
1803
|
+
// This block admits by VOTES: per-region evidence pooled, gated on the
|
|
1804
|
+
// natural break and on consensusFloor, with the dominant allowed to bypass
|
|
1805
|
+
// both. `structuralResonance` admits by a MARGIN over the estimator's own
|
|
1806
|
+
// noise, and `crossRegionVotes` admits by STRUCTURE (which regions may pair
|
|
1807
|
+
// at all, with at least one side individually discriminative). Read
|
|
1808
|
+
// together they look like one policy written three times; they are three
|
|
1809
|
+
// different measurements of the same question ("is this evidence?"), and
|
|
1810
|
+
// unifying them would average three readings into one — the mistake
|
|
1811
|
+
// `extraction.ts` records as "do not unify the two into one machine".
|
|
1812
|
+
// What they DO share, and must keep sharing, is the discipline of deriving
|
|
1813
|
+
// every bar from D/W/N rather than choosing it (thresholds.md).
|
|
1751
1814
|
const rejectionReasons: AnchorRejectionReason[] = [];
|
|
1752
1815
|
if (absorbed) {
|
|
1753
1816
|
status = "overlap";
|
|
@@ -1757,9 +1820,75 @@ export function commitVotes(
|
|
|
1757
1820
|
pastLeadingSaturation = pastLeading;
|
|
1758
1821
|
const vote = votesIdf.get(point.anchor) ?? 0;
|
|
1759
1822
|
if (roots.length === 0) {
|
|
1760
|
-
//
|
|
1761
|
-
//
|
|
1762
|
-
//
|
|
1823
|
+
// THE DOMINANCE PRIVILEGE, AND THE TENSION IT CARRIES (measured).
|
|
1824
|
+
//
|
|
1825
|
+
// The first non-overlapping candidate is DOMINANT: it bypasses both
|
|
1826
|
+
// vote gates below and grounds on its own; only the leading-saturation
|
|
1827
|
+
// gate still applies to it. The privilege is load-bearing — analogies,
|
|
1828
|
+
// substitutions and composed contexts are precisely candidates the
|
|
1829
|
+
// query does NOT contain, and the engine loses them without it.
|
|
1830
|
+
//
|
|
1831
|
+
// WHICH candidate receives it, though, is decided by this loop's ORDER.
|
|
1832
|
+
// That order comes from `ranked`, and when two candidates carry equal
|
|
1833
|
+
// evidence the stable sort preserves the ENUMERATION order, so the
|
|
1834
|
+
// privilege is allocated by an ordering rather than by a rule.
|
|
1835
|
+
//
|
|
1836
|
+
// Measured on test/34's corpus, query "red":
|
|
1837
|
+
//
|
|
1838
|
+
// 0:#77 vote=1.3863 idf=1.3863 [0,3) | 1:#49 vote=1.3863 idf=1.3863 [0,3)
|
|
1839
|
+
//
|
|
1840
|
+
// Both candidates (`red square` #77, `red circle` #49) have IDENTICAL
|
|
1841
|
+
// `vote` AND IDENTICAL `idfVote` over the SAME support span, so the
|
|
1842
|
+
// comparator leaves them tied, the second is absorbed as "overlap", and
|
|
1843
|
+
// the first grounds. With this build's enumeration order that first is
|
|
1844
|
+
// `red square`, 60/60 seeds, and `red circle` — the JOINT context — is
|
|
1845
|
+
// never reached by "red" alone. That is the premise test/34 exists to
|
|
1846
|
+
// protect: no single region reaches the joint context, which is what
|
|
1847
|
+
// makes the binding query unreachable without direct region
|
|
1848
|
+
// interaction.
|
|
1849
|
+
//
|
|
1850
|
+
// THE TENSION: the premise therefore holds BY ENUMERATION ORDER, not by
|
|
1851
|
+
// a rule, so any change to this ordering can move the privilege onto the
|
|
1852
|
+
// joint context and let one region reach it. Measured: adding
|
|
1853
|
+
// `|| a.anchor - b.anchor` — the lowest-id tie-break that AGENTS.md §2
|
|
1854
|
+
// sanctions as an equivalent corpus-determined tie-break — does exactly
|
|
1855
|
+
// that: "red" then attends to `red circle`, test/34 fails 6/1, and the
|
|
1856
|
+
// canonical suite reports 1 failure.
|
|
1857
|
+
//
|
|
1858
|
+
// TWO ATTEMPTS TO MAKE IT A RULE, BOTH REFUTED BY MEASUREMENT:
|
|
1859
|
+
//
|
|
1860
|
+
// 1. EVIDENCE SEPARATION. Grant the privilege only when the first
|
|
1861
|
+
// candidate's evidence is separated from the next distinct
|
|
1862
|
+
// candidate's by more than the co-dominant band (sqrt(k) *
|
|
1863
|
+
// estimatorNoise(D)). Refuted: that band exists to ADMIT the
|
|
1864
|
+
// anchors the estimator cannot separate from the dominant — its own
|
|
1865
|
+
// documented purpose — so withholding the privilege on ties removes
|
|
1866
|
+
// the very case it was written for. Suite: 4 failures (the two
|
|
1867
|
+
// co-dominant band laws, breadth/scale invariance, test/29 D2).
|
|
1868
|
+
//
|
|
1869
|
+
// 2. QUERY-OWNED CONTENT. Grant the privilege only to a candidate
|
|
1870
|
+
// that IS a recognised region's identity (regions.some(r => r.id ===
|
|
1871
|
+
// point.anchor)). Measured: for "circle" that identity IS the
|
|
1872
|
+
// ranked candidate, so the privilege stays and `circle` grounds; for
|
|
1873
|
+
// "red" the identity is the `red` node itself while the candidates
|
|
1874
|
+
// are the conjunctions, so neither is privileged; for "red then
|
|
1875
|
+
// circle" the composed context carries idf 3.958 and clears both
|
|
1876
|
+
// gates on its own evidence. All four control queries came out
|
|
1877
|
+
// right — and the suite: 10 failures, six of them in the
|
|
1878
|
+
// analogy/counterfactual/CAST suites ("an analogy still transfers
|
|
1879
|
+
// from a structure the query never names"; "a substitute the query
|
|
1880
|
+
// NAMES may still be voiced"). Refuted: the privilege exists to
|
|
1881
|
+
// admit what the query does NOT contain, so identity is the wrong
|
|
1882
|
+
// axis.
|
|
1883
|
+
//
|
|
1884
|
+
// WHAT A FUTURE ATTEMPT MUST RESPECT: whatever allocates this privilege
|
|
1885
|
+
// has to (a) keep it available to candidates the query does not contain
|
|
1886
|
+
// — analogies, substitutions, compositions — and (b) not depend on the
|
|
1887
|
+
// estimator's ordering among anchors of equal evidence, because that
|
|
1888
|
+
// ordering is not a fact about the corpus. No lever satisfying both has
|
|
1889
|
+
// been found. Until one is, this premise rests on the enumeration order
|
|
1890
|
+
// recorded above, and test/34 is the only test that notices if it
|
|
1891
|
+
// moves.
|
|
1763
1892
|
dominant = true;
|
|
1764
1893
|
if (pastLeading) {
|
|
1765
1894
|
status = "root";
|
|
@@ -1768,8 +1897,15 @@ export function commitVotes(
|
|
|
1768
1897
|
rejectionReasons.push("leading-saturation");
|
|
1769
1898
|
}
|
|
1770
1899
|
} else {
|
|
1771
|
-
|
|
1772
|
-
|
|
1900
|
+
// THE FLOOR AND THE BREAK READ THE IDF WEIGHTING. `floor` is derived
|
|
1901
|
+
// for pooled IDF-weighted votes, and `rootCut` comes from the IDF
|
|
1902
|
+
// distribution (`idfDesc`), so gating the mode-dependent `vote` against
|
|
1903
|
+
// either let a weighting mode change an admission (measured: anchor 87,
|
|
1904
|
+
// inverse 2.682 admitted vs direct 1.468 refused). Reading the IDF sum
|
|
1905
|
+
// makes the verdict mode-independent, and changes nothing in the
|
|
1906
|
+
// engine's own mode, where the two readings coincide.
|
|
1907
|
+
passesNaturalBreak = point.idfVote >= rootCut;
|
|
1908
|
+
passesConsensusFloor = point.idfVote >= floor;
|
|
1773
1909
|
// CO-DOMINANT — an anchor the estimator cannot separate from the
|
|
1774
1910
|
// dominant inherits the dominant's exemption, because that exemption's
|
|
1775
1911
|
// only warrant is being TOP, and "top" is not a fact about the corpus
|
|
@@ -2467,6 +2603,13 @@ export async function structuralResonance(
|
|
|
2467
2603
|
});
|
|
2468
2604
|
};
|
|
2469
2605
|
|
|
2606
|
+
// ── ADMISSION BY MARGIN, not by votes (see voteRegions' note) ─────────
|
|
2607
|
+
// What this site measures: how far the best ANN proposal's effective score
|
|
2608
|
+
// (score × semanticConfidence) stands above the runner-up's, against
|
|
2609
|
+
// `estimatorNoise(D)`. What it does NOT measure: how many regions voted,
|
|
2610
|
+
// or whether the query's regions agree — that is voteRegions' question, and
|
|
2611
|
+
// here a synthetic gist has already replaced them. The two bars are both
|
|
2612
|
+
// derived (thresholds.md), and neither is a tuning of the other.
|
|
2470
2613
|
let selected: StructuralResonanceProposal | null = null;
|
|
2471
2614
|
let selectedReach: AncestorReach | null = null;
|
|
2472
2615
|
let selectedIdf = 0;
|
|
@@ -2582,6 +2725,15 @@ async function crossRegionVotes(
|
|
|
2582
2725
|
// successfully reconstructed while probing one pair must not be read and
|
|
2583
2726
|
// perceived again while probing another pair in the same climb.
|
|
2584
2727
|
const siblingGistMemo = new Map<number, CachedSiblingGist>();
|
|
2728
|
+
// ── ADMISSION BY STRUCTURE, not by a bar (see voteRegions' note) ──────
|
|
2729
|
+
// What this site decides: WHICH regions may pair at all — a region that
|
|
2730
|
+
// already voted (individually discriminative), or a KNOWN non-voting one as
|
|
2731
|
+
// the weak side of a pair whose other side voted; never two non-voting
|
|
2732
|
+
// regions, and never a span contained in a maximal one whose reading is
|
|
2733
|
+
// exact. The bar (the container's idf) comes later, on the candidate. So
|
|
2734
|
+
// its "rejection reasons" name structural disqualifications — a different
|
|
2735
|
+
// vocabulary because it answers a different question, and the three
|
|
2736
|
+
// taxonomies stay separate for the same reason the readings do.
|
|
2585
2737
|
const votedSpans = new Set<string>();
|
|
2586
2738
|
for (const rv of rvs.votes) votedSpans.add(`${rv.start},${rv.end}`);
|
|
2587
2739
|
const seen = new Set<string>();
|
|
@@ -2978,7 +3130,7 @@ async function crossRegionVotes(
|
|
|
2978
3130
|
Math.min(li, ri),
|
|
2979
3131
|
Math.max(li + left.length, ri + right.length),
|
|
2980
3132
|
);
|
|
2981
|
-
if (
|
|
3133
|
+
if (restates(query, joined, 0)) {
|
|
2982
3134
|
if (structuralTrace) structuralTrace.selfEvidenceRejected++;
|
|
2983
3135
|
continue; // query says it itself
|
|
2984
3136
|
}
|
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
|