@hviana/sema 0.2.5 → 0.2.6
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/package.json +1 -1
- package/src/mind/attention.ts +355 -19
- package/test/53-cross-region-probe-instrumentation.test.mjs +356 -0
- package/dist/example/demo.d.ts +0 -1
- package/dist/example/demo.js +0 -39
- package/dist/example/train_base.d.ts +0 -87
- package/dist/example/train_base.js +0 -2252
- package/dist/src/alphabet.d.ts +0 -7
- package/dist/src/alphabet.js +0 -33
- package/dist/src/alu/src/alu.d.ts +0 -185
- package/dist/src/alu/src/alu.js +0 -440
- package/dist/src/alu/src/expr.d.ts +0 -61
- package/dist/src/alu/src/expr.js +0 -318
- package/dist/src/alu/src/index.d.ts +0 -11
- package/dist/src/alu/src/index.js +0 -19
- package/dist/src/alu/src/kernel-arith.d.ts +0 -16
- package/dist/src/alu/src/kernel-arith.js +0 -264
- package/dist/src/alu/src/kernel-bits.d.ts +0 -19
- package/dist/src/alu/src/kernel-bits.js +0 -152
- package/dist/src/alu/src/kernel-logic.d.ts +0 -4
- package/dist/src/alu/src/kernel-logic.js +0 -60
- package/dist/src/alu/src/kernel-nd.d.ts +0 -3
- package/dist/src/alu/src/kernel-nd.js +0 -208
- package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
- package/dist/src/alu/src/kernel-numeric.js +0 -366
- package/dist/src/alu/src/operation.d.ts +0 -168
- package/dist/src/alu/src/operation.js +0 -189
- package/dist/src/alu/src/parser.d.ts +0 -221
- package/dist/src/alu/src/parser.js +0 -577
- package/dist/src/alu/src/resonance.d.ts +0 -55
- package/dist/src/alu/src/resonance.js +0 -126
- package/dist/src/alu/src/text.d.ts +0 -31
- package/dist/src/alu/src/text.js +0 -73
- package/dist/src/alu/src/value.d.ts +0 -109
- package/dist/src/alu/src/value.js +0 -300
- package/dist/src/alu/test/alu.test.d.ts +0 -1
- package/dist/src/alu/test/alu.test.js +0 -764
- package/dist/src/bytes.d.ts +0 -14
- package/dist/src/bytes.js +0 -59
- package/dist/src/canon.d.ts +0 -26
- package/dist/src/canon.js +0 -57
- package/dist/src/config.d.ts +0 -111
- package/dist/src/config.js +0 -91
- package/dist/src/derive/src/deduction.d.ts +0 -125
- package/dist/src/derive/src/deduction.js +0 -155
- package/dist/src/derive/src/index.d.ts +0 -7
- package/dist/src/derive/src/index.js +0 -11
- package/dist/src/derive/src/priority-queue.d.ts +0 -20
- package/dist/src/derive/src/priority-queue.js +0 -73
- package/dist/src/derive/src/rewrite.d.ts +0 -56
- package/dist/src/derive/src/rewrite.js +0 -100
- package/dist/src/derive/src/trie.d.ts +0 -90
- package/dist/src/derive/src/trie.js +0 -217
- package/dist/src/derive/test/derive.test.d.ts +0 -1
- package/dist/src/derive/test/derive.test.js +0 -122
- package/dist/src/extension.d.ts +0 -37
- package/dist/src/extension.js +0 -7
- package/dist/src/geometry.d.ts +0 -202
- package/dist/src/geometry.js +0 -575
- package/dist/src/index.d.ts +0 -16
- package/dist/src/index.js +0 -18
- package/dist/src/ingest-cache.d.ts +0 -41
- package/dist/src/ingest-cache.js +0 -161
- package/dist/src/mind/articulation.d.ts +0 -6
- package/dist/src/mind/articulation.js +0 -99
- package/dist/src/mind/attention.d.ts +0 -315
- package/dist/src/mind/attention.js +0 -1549
- package/dist/src/mind/bridge.d.ts +0 -30
- package/dist/src/mind/bridge.js +0 -569
- package/dist/src/mind/canonical.d.ts +0 -29
- package/dist/src/mind/canonical.js +0 -88
- package/dist/src/mind/graph-search.d.ts +0 -285
- package/dist/src/mind/graph-search.js +0 -956
- package/dist/src/mind/index.d.ts +0 -7
- package/dist/src/mind/index.js +0 -5
- package/dist/src/mind/junction.d.ts +0 -124
- package/dist/src/mind/junction.js +0 -317
- package/dist/src/mind/learning.d.ts +0 -47
- package/dist/src/mind/learning.js +0 -248
- package/dist/src/mind/match.d.ts +0 -126
- package/dist/src/mind/match.js +0 -441
- package/dist/src/mind/mechanisms/alu.d.ts +0 -4
- package/dist/src/mind/mechanisms/alu.js +0 -36
- package/dist/src/mind/mechanisms/cast.d.ts +0 -89
- package/dist/src/mind/mechanisms/cast.js +0 -716
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
- package/dist/src/mind/mechanisms/confluence.js +0 -225
- package/dist/src/mind/mechanisms/cover.d.ts +0 -6
- package/dist/src/mind/mechanisms/cover.js +0 -203
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -67
- package/dist/src/mind/mechanisms/extraction.js +0 -387
- package/dist/src/mind/mechanisms/recall.d.ts +0 -13
- package/dist/src/mind/mechanisms/recall.js +0 -325
- package/dist/src/mind/mind.d.ts +0 -276
- package/dist/src/mind/mind.js +0 -572
- package/dist/src/mind/pipeline-mechanism.d.ts +0 -142
- package/dist/src/mind/pipeline-mechanism.js +0 -213
- package/dist/src/mind/pipeline.d.ts +0 -20
- package/dist/src/mind/pipeline.js +0 -217
- package/dist/src/mind/primitives.d.ts +0 -70
- package/dist/src/mind/primitives.js +0 -287
- package/dist/src/mind/rationale.d.ts +0 -139
- package/dist/src/mind/rationale.js +0 -163
- package/dist/src/mind/reasoning.d.ts +0 -34
- package/dist/src/mind/reasoning.js +0 -240
- package/dist/src/mind/recognition.d.ts +0 -20
- package/dist/src/mind/recognition.js +0 -390
- package/dist/src/mind/resonance.d.ts +0 -23
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +0 -15
- package/dist/src/mind/trace.js +0 -73
- package/dist/src/mind/traverse.d.ts +0 -113
- package/dist/src/mind/traverse.js +0 -568
- package/dist/src/mind/types.d.ts +0 -289
- package/dist/src/mind/types.js +0 -130
- package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
- package/dist/src/rabitq-ivf/src/database.js +0 -201
- package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
- package/dist/src/rabitq-ivf/src/index.js +0 -4
- package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
- package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
- package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
- package/dist/src/rabitq-ivf/src/prng.js +0 -36
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-ivf/src/rabitq.js +0 -283
- package/dist/src/sema.d.ts +0 -31
- package/dist/src/sema.js +0 -63
- package/dist/src/store-sqlite.d.ts +0 -184
- package/dist/src/store-sqlite.js +0 -942
- package/dist/src/store.d.ts +0 -663
- package/dist/src/store.js +0 -1618
- package/dist/src/vec.d.ts +0 -31
- package/dist/src/vec.js +0 -109
package/package.json
CHANGED
package/src/mind/attention.ts
CHANGED
|
@@ -92,6 +92,16 @@ export type RegionOutcome =
|
|
|
92
92
|
| "nonpositive-df-weight"
|
|
93
93
|
| "contrastive-margin-rejection";
|
|
94
94
|
|
|
95
|
+
/** The best DIFFERENT-conclusion rival the contrastive-margin gate found
|
|
96
|
+
* while scanning an ordinary (approximate) region's ANN hits — spec §1.
|
|
97
|
+
* Its roots/saturation/contextsReached are already available through
|
|
98
|
+
* `reaches` (serialiseReaches) for `node`; not duplicated here. */
|
|
99
|
+
export interface ConsensusContrastiveRivalTrace {
|
|
100
|
+
node: number;
|
|
101
|
+
rank: number;
|
|
102
|
+
score: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
95
105
|
export interface ConsensusRegionTrace {
|
|
96
106
|
index: number;
|
|
97
107
|
source: "perceived" | "recognised";
|
|
@@ -123,6 +133,7 @@ export interface ConsensusRegionTrace {
|
|
|
123
133
|
|
|
124
134
|
contrastiveMargin?: number;
|
|
125
135
|
contrastiveNoiseFloor?: number;
|
|
136
|
+
contrastiveRival?: ConsensusContrastiveRivalTrace;
|
|
126
137
|
|
|
127
138
|
mutualWeight?: number;
|
|
128
139
|
voteWeightPerRoot?: number;
|
|
@@ -177,6 +188,107 @@ export interface JunctionVoteTrace {
|
|
|
177
188
|
explainedAwayRegionIndices: number[];
|
|
178
189
|
absorbed: number;
|
|
179
190
|
tier?: ClimbConsensusJunctionTier;
|
|
191
|
+
|
|
192
|
+
/** Zero-based index into `crossRegion.probes` — the probe this vote was
|
|
193
|
+
* produced from (spec §8). */
|
|
194
|
+
probe: number;
|
|
195
|
+
confidence: number;
|
|
196
|
+
/** "Evidence bytes" — the container-coverage byte count (the existing
|
|
197
|
+
* `bestCov` variable at the push site). */
|
|
198
|
+
evidenceBytes: number;
|
|
199
|
+
mutualWeight: number;
|
|
200
|
+
voteWeightPerRoot: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Whether one DAG/synonym tier attempt was even made for a probe, and how
|
|
204
|
+
* many candidate containers it returned — spec §2/§3. */
|
|
205
|
+
export interface CrossRegionTierAttemptTrace {
|
|
206
|
+
attempted: boolean;
|
|
207
|
+
candidatesReturned: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Aggregate outcome of the container-selection loop for a DAG/synonym tier
|
|
211
|
+
* that returned at least one container — spec §4. Only aggregate counts
|
|
212
|
+
* and the final outcome are recorded, never every candidate. */
|
|
213
|
+
export interface CrossRegionStructuralTrace {
|
|
214
|
+
tier: "exact" | "single-synonym" | "double-synonym";
|
|
215
|
+
selfEvidenceRejected: number;
|
|
216
|
+
contradictionRejected: number;
|
|
217
|
+
passedGuards: number;
|
|
218
|
+
selectedNode?: number;
|
|
219
|
+
outcome:
|
|
220
|
+
| "all-rejected"
|
|
221
|
+
| "saturated"
|
|
222
|
+
| "no-roots"
|
|
223
|
+
| "nonpositive-idf"
|
|
224
|
+
| "accepted";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** One retained structural-resonance variant that actually issued its own
|
|
228
|
+
* ANN query — spec §5. */
|
|
229
|
+
export interface StructuralResonanceVariantTrace {
|
|
230
|
+
kind: StructuralVariant["kind"];
|
|
231
|
+
semanticConfidence: number;
|
|
232
|
+
leftSiblingId?: number;
|
|
233
|
+
rightSiblingId?: number;
|
|
234
|
+
annHitsReturned: number;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** One merged structural-resonance proposal actually examined via
|
|
238
|
+
* edgeAncestors — spec §5. Retains node/variant/scores, but NOT
|
|
239
|
+
* roots/saturation/contextsReached/idf (already in `reaches`). */
|
|
240
|
+
export interface StructuralResonanceCandidateTrace {
|
|
241
|
+
node: number;
|
|
242
|
+
variant: StructuralVariant["kind"];
|
|
243
|
+
leftSiblingId?: number;
|
|
244
|
+
rightSiblingId?: number;
|
|
245
|
+
annScore: number;
|
|
246
|
+
semanticConfidence: number;
|
|
247
|
+
effectiveScore: number;
|
|
248
|
+
outcome:
|
|
249
|
+
| "saturated"
|
|
250
|
+
| "no-roots"
|
|
251
|
+
| "nonpositive-idf"
|
|
252
|
+
| "same-as-endpoint"
|
|
253
|
+
| "same-as-selected"
|
|
254
|
+
| "selected"
|
|
255
|
+
| "contrastive-rival";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface StructuralResonanceTrace {
|
|
259
|
+
variantBudget: number;
|
|
260
|
+
variants: StructuralResonanceVariantTrace[];
|
|
261
|
+
mergedProposals: number;
|
|
262
|
+
examined: StructuralResonanceCandidateTrace[];
|
|
263
|
+
contrastiveMargin?: number;
|
|
264
|
+
noiseFloor: number;
|
|
265
|
+
outcome:
|
|
266
|
+
| "ineligible"
|
|
267
|
+
| "empty"
|
|
268
|
+
| "no-valid-proposal"
|
|
269
|
+
| "margin-rejected"
|
|
270
|
+
| "accepted";
|
|
271
|
+
ineligibleReasons?: Array<
|
|
272
|
+
"between-region" | "not-both-strong" | "not-both-known" | "gap-too-large"
|
|
273
|
+
>;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** One cross-region pair the ladder actually probed — spec §2. Exactly one
|
|
277
|
+
* of these is pushed per pair that incremented `probes`. */
|
|
278
|
+
export interface CrossRegionProbeTrace {
|
|
279
|
+
leftRegionIndex: number;
|
|
280
|
+
rightRegionIndex: number;
|
|
281
|
+
betweenRegionIndices: number[];
|
|
282
|
+
exact: CrossRegionTierAttemptTrace;
|
|
283
|
+
singleSynonym: CrossRegionTierAttemptTrace;
|
|
284
|
+
doubleSynonym: CrossRegionTierAttemptTrace;
|
|
285
|
+
structural?: CrossRegionStructuralTrace;
|
|
286
|
+
resonance?: StructuralResonanceTrace;
|
|
287
|
+
outcome:
|
|
288
|
+
| "accepted"
|
|
289
|
+
| "structural-rejected"
|
|
290
|
+
| "resonance-ineligible"
|
|
291
|
+
| "resonance-rejected";
|
|
180
292
|
}
|
|
181
293
|
|
|
182
294
|
export interface ClimbConsensusData {
|
|
@@ -215,6 +327,8 @@ export interface ClimbConsensusData {
|
|
|
215
327
|
probesAttempted: number;
|
|
216
328
|
junctionVotes: JunctionVoteTrace[];
|
|
217
329
|
supersededOrdinaryVotes: number;
|
|
330
|
+
probes: CrossRegionProbeTrace[];
|
|
331
|
+
stopReason: "insufficient-regions" | "probe-limit" | "pairs-exhausted";
|
|
218
332
|
};
|
|
219
333
|
|
|
220
334
|
saturation?: {
|
|
@@ -248,7 +362,9 @@ interface TraceDraft {
|
|
|
248
362
|
maximalRegions: number;
|
|
249
363
|
probeLimit: number;
|
|
250
364
|
probesAttempted: number;
|
|
365
|
+
stopReason?: "insufficient-regions" | "probe-limit" | "pairs-exhausted";
|
|
251
366
|
};
|
|
367
|
+
crossRegionProbes: CrossRegionProbeTrace[];
|
|
252
368
|
supersededOrdinaryVotes: number;
|
|
253
369
|
saturation?: {
|
|
254
370
|
regionIntervals: Array<{ start: number; end: number }>;
|
|
@@ -283,6 +399,7 @@ function newTraceDraft(perceivedCount: number): TraceDraft {
|
|
|
283
399
|
perceivedCount,
|
|
284
400
|
regions: [],
|
|
285
401
|
crossRegionJunctionVotes: [],
|
|
402
|
+
crossRegionProbes: [],
|
|
286
403
|
supersededOrdinaryVotes: 0,
|
|
287
404
|
anchors: [],
|
|
288
405
|
};
|
|
@@ -756,15 +873,21 @@ export async function voteRegions(
|
|
|
756
873
|
// nothing. Gating at the noise floor keeps frame-echo suppression (a frame
|
|
757
874
|
// region's margin ≈ 0 is gated out) without penalising honest evidence.
|
|
758
875
|
let contrastiveMargin: number | undefined;
|
|
876
|
+
let contrastiveRival: ConsensusContrastiveRivalTrace | undefined;
|
|
759
877
|
if (!known) {
|
|
760
878
|
let margin = score;
|
|
761
|
-
|
|
879
|
+
const hitsForRival = await ensureHits();
|
|
880
|
+
for (let hi = 0; hi < hitsForRival.length; hi++) {
|
|
881
|
+
const h = hitsForRival[hi];
|
|
762
882
|
if (h.id === voterId) continue;
|
|
763
883
|
const r2 = edgeAncestors(ctx, h.id, N, reachMemo);
|
|
764
884
|
examinedIds?.add(h.id);
|
|
765
885
|
if (r2.saturated || r2.roots.length === 0) continue; // concludes nothing
|
|
766
886
|
if (sameRoots(r2.roots, reach.roots)) continue; // same conclusion
|
|
767
887
|
margin = score - h.score; // hits are nearest-first: the best rival
|
|
888
|
+
if (td) {
|
|
889
|
+
contrastiveRival = { node: h.id, rank: hi, score: h.score };
|
|
890
|
+
}
|
|
768
891
|
break;
|
|
769
892
|
}
|
|
770
893
|
contrastiveMargin = margin;
|
|
@@ -777,6 +900,7 @@ export async function voteRegions(
|
|
|
777
900
|
dfWeight: wf,
|
|
778
901
|
contrastiveMargin: margin,
|
|
779
902
|
contrastiveNoiseFloor: noiseFloor,
|
|
903
|
+
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
780
904
|
});
|
|
781
905
|
continue;
|
|
782
906
|
}
|
|
@@ -828,6 +952,7 @@ export async function voteRegions(
|
|
|
828
952
|
? {
|
|
829
953
|
contrastiveMargin,
|
|
830
954
|
contrastiveNoiseFloor: estimatorNoise(ctx.store.D),
|
|
955
|
+
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
831
956
|
}
|
|
832
957
|
: {}),
|
|
833
958
|
mutualWeight: mutual,
|
|
@@ -1562,11 +1687,13 @@ export async function structuralResonance(
|
|
|
1562
1687
|
* itself through a synthetic gist still dominated by its own direction. */
|
|
1563
1688
|
ownRootsA: readonly number[] | undefined,
|
|
1564
1689
|
ownRootsB: readonly number[] | undefined,
|
|
1690
|
+
trace?: StructuralResonanceTrace,
|
|
1565
1691
|
): Promise<
|
|
1566
1692
|
| { proposal: StructuralResonanceProposal; reach: AncestorReach; idf: number }
|
|
1567
1693
|
| null
|
|
1568
1694
|
> {
|
|
1569
1695
|
const { variants } = buildStructuralVariants(ctx, ra, rb, sides);
|
|
1696
|
+
if (trace) trace.variantBudget = ctx.cfg.haloQueryK;
|
|
1570
1697
|
|
|
1571
1698
|
const middleBytes = query.subarray(ra.end, rb.start);
|
|
1572
1699
|
const middlePart: StructuralPart | null = middleBytes.length === 0
|
|
@@ -1580,6 +1707,15 @@ export async function structuralResonance(
|
|
|
1580
1707
|
parts.push(variant.right);
|
|
1581
1708
|
const synthetic = composeStructuralGist(ctx.space, parts);
|
|
1582
1709
|
const hits = await ctx.store.resonate(synthetic, k);
|
|
1710
|
+
if (trace) {
|
|
1711
|
+
trace.variants.push({
|
|
1712
|
+
kind: variant.kind,
|
|
1713
|
+
semanticConfidence: variant.semanticConfidence,
|
|
1714
|
+
leftSiblingId: variant.leftSiblingId,
|
|
1715
|
+
rightSiblingId: variant.rightSiblingId,
|
|
1716
|
+
annHitsReturned: hits.length,
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1583
1719
|
for (const hit of hits) {
|
|
1584
1720
|
const candidate: StructuralResonanceProposal = {
|
|
1585
1721
|
id: hit.id,
|
|
@@ -1596,44 +1732,98 @@ export async function structuralResonance(
|
|
|
1596
1732
|
}
|
|
1597
1733
|
}
|
|
1598
1734
|
}
|
|
1599
|
-
if (proposals.size
|
|
1735
|
+
if (trace) trace.mergedProposals = proposals.size;
|
|
1736
|
+
if (proposals.size === 0) {
|
|
1737
|
+
if (trace) {
|
|
1738
|
+
trace.noiseFloor = estimatorNoise(ctx.store.D);
|
|
1739
|
+
trace.outcome = "empty";
|
|
1740
|
+
}
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1600
1743
|
|
|
1601
1744
|
const sorted = [...proposals.values()].sort((a, b) =>
|
|
1602
1745
|
b.effectiveScore - a.effectiveScore || a.id - b.id
|
|
1603
1746
|
);
|
|
1604
1747
|
|
|
1748
|
+
// One shared shape for every `examined` entry (spec §5): only `outcome`
|
|
1749
|
+
// varies across the six exit points below, so build it once instead of
|
|
1750
|
+
// repeating the six-field literal at each site.
|
|
1751
|
+
const recordExamined = (
|
|
1752
|
+
p: StructuralResonanceProposal,
|
|
1753
|
+
outcome: StructuralResonanceCandidateTrace["outcome"],
|
|
1754
|
+
) => {
|
|
1755
|
+
if (!trace) return;
|
|
1756
|
+
trace.examined.push({
|
|
1757
|
+
node: p.id,
|
|
1758
|
+
variant: p.variant,
|
|
1759
|
+
leftSiblingId: p.leftSiblingId,
|
|
1760
|
+
rightSiblingId: p.rightSiblingId,
|
|
1761
|
+
annScore: p.annScore,
|
|
1762
|
+
semanticConfidence: p.semanticConfidence,
|
|
1763
|
+
effectiveScore: p.effectiveScore,
|
|
1764
|
+
outcome,
|
|
1765
|
+
});
|
|
1766
|
+
};
|
|
1767
|
+
|
|
1605
1768
|
let selected: StructuralResonanceProposal | null = null;
|
|
1606
1769
|
let selectedReach: AncestorReach | null = null;
|
|
1607
1770
|
let selectedIdf = 0;
|
|
1608
1771
|
let rival: StructuralResonanceProposal | null = null;
|
|
1609
1772
|
for (const p of sorted) {
|
|
1610
1773
|
const reach = edgeAncestors(ctx, p.id, N, reachMemo);
|
|
1611
|
-
if (reach.saturated || reach.roots.length === 0)
|
|
1774
|
+
if (reach.saturated || reach.roots.length === 0) {
|
|
1775
|
+
recordExamined(p, reach.saturated ? "saturated" : "no-roots");
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1612
1778
|
const idf = Math.log(N / Math.max(1, reach.contextsReached));
|
|
1613
|
-
if (idf <= 0)
|
|
1779
|
+
if (idf <= 0) {
|
|
1780
|
+
recordExamined(p, "nonpositive-idf");
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1614
1783
|
// Self-evidence backstop (see the param doc above): a candidate that is
|
|
1615
1784
|
// exactly one side's own already-voted conclusion carries no JOINT
|
|
1616
1785
|
// evidence — skip it as if it never survived.
|
|
1617
1786
|
if (
|
|
1618
1787
|
(ownRootsA && sameRoots(reach.roots, ownRootsA)) ||
|
|
1619
1788
|
(ownRootsB && sameRoots(reach.roots, ownRootsB))
|
|
1620
|
-
)
|
|
1789
|
+
) {
|
|
1790
|
+
recordExamined(p, "same-as-endpoint");
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1621
1793
|
if (selected === null) {
|
|
1622
1794
|
selected = p;
|
|
1623
1795
|
selectedReach = reach;
|
|
1624
1796
|
selectedIdf = idf;
|
|
1797
|
+
recordExamined(p, "selected");
|
|
1625
1798
|
} else if (!sameRoots(reach.roots, selectedReach!.roots)) {
|
|
1626
1799
|
rival = p;
|
|
1800
|
+
recordExamined(p, "contrastive-rival");
|
|
1627
1801
|
break;
|
|
1802
|
+
} else {
|
|
1803
|
+
recordExamined(p, "same-as-selected");
|
|
1628
1804
|
}
|
|
1629
1805
|
}
|
|
1630
|
-
if (selected === null || selectedReach === null)
|
|
1806
|
+
if (selected === null || selectedReach === null) {
|
|
1807
|
+
if (trace) {
|
|
1808
|
+
trace.noiseFloor = estimatorNoise(ctx.store.D);
|
|
1809
|
+
trace.outcome = "no-valid-proposal";
|
|
1810
|
+
}
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1631
1813
|
|
|
1632
1814
|
const margin = rival
|
|
1633
1815
|
? selected.effectiveScore - rival.effectiveScore
|
|
1634
1816
|
: selected.effectiveScore;
|
|
1635
|
-
if (
|
|
1817
|
+
if (trace) {
|
|
1818
|
+
trace.contrastiveMargin = margin;
|
|
1819
|
+
trace.noiseFloor = estimatorNoise(ctx.store.D);
|
|
1820
|
+
}
|
|
1821
|
+
if (margin <= estimatorNoise(ctx.store.D)) {
|
|
1822
|
+
if (trace) trace.outcome = "margin-rejected";
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1636
1825
|
|
|
1826
|
+
if (trace) trace.outcome = "accepted";
|
|
1637
1827
|
return { proposal: selected, reach: selectedReach, idf: selectedIdf };
|
|
1638
1828
|
}
|
|
1639
1829
|
|
|
@@ -1703,6 +1893,7 @@ async function crossRegionVotes(
|
|
|
1703
1893
|
maximalRegions: cand.length,
|
|
1704
1894
|
probeLimit: k,
|
|
1705
1895
|
probesAttempted: 0, // updated below as probes accrue
|
|
1896
|
+
stopReason: cand.length < 2 ? "insufficient-regions" : undefined,
|
|
1706
1897
|
};
|
|
1707
1898
|
}
|
|
1708
1899
|
if (cand.length < 2) return none;
|
|
@@ -1778,6 +1969,31 @@ async function crossRegionVotes(
|
|
|
1778
1969
|
if (td?.crossRegionSummary) {
|
|
1779
1970
|
td.crossRegionSummary.probesAttempted = probes;
|
|
1780
1971
|
}
|
|
1972
|
+
// Trace-only per-probe bookkeeping (spec §2-§7) — built incrementally
|
|
1973
|
+
// as the ladder runs, pushed exactly once at whichever exit fires
|
|
1974
|
+
// below. `pushProbe` is called at every continue/success exit for
|
|
1975
|
+
// THIS pair so the invariant `probes.length === probesAttempted`
|
|
1976
|
+
// holds regardless of which tier settled it.
|
|
1977
|
+
const probe: CrossRegionProbeTrace | undefined = td
|
|
1978
|
+
? {
|
|
1979
|
+
leftRegionIndex: cand[a],
|
|
1980
|
+
rightRegionIndex: cand[b],
|
|
1981
|
+
betweenRegionIndices: [...between],
|
|
1982
|
+
exact: { attempted: false, candidatesReturned: 0 },
|
|
1983
|
+
singleSynonym: { attempted: false, candidatesReturned: 0 },
|
|
1984
|
+
doubleSynonym: { attempted: false, candidatesReturned: 0 },
|
|
1985
|
+
outcome: "structural-rejected",
|
|
1986
|
+
}
|
|
1987
|
+
: undefined;
|
|
1988
|
+
let probePushed = false;
|
|
1989
|
+
const pushProbe = (
|
|
1990
|
+
outcome: CrossRegionProbeTrace["outcome"],
|
|
1991
|
+
) => {
|
|
1992
|
+
if (!td || !probe || probePushed) return;
|
|
1993
|
+
probe.outcome = outcome;
|
|
1994
|
+
td.crossRegionProbes.push(probe);
|
|
1995
|
+
probePushed = true;
|
|
1996
|
+
};
|
|
1781
1997
|
|
|
1782
1998
|
const left = query.subarray(ra.start, ra.end);
|
|
1783
1999
|
const right = query.subarray(rb.start, rb.end);
|
|
@@ -1805,6 +2021,12 @@ async function crossRegionVotes(
|
|
|
1805
2021
|
undefined,
|
|
1806
2022
|
true,
|
|
1807
2023
|
);
|
|
2024
|
+
if (probe) {
|
|
2025
|
+
probe.exact = {
|
|
2026
|
+
attempted: true,
|
|
2027
|
+
candidatesReturned: containers.length,
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
1808
2030
|
if (containers.length === 0) {
|
|
1809
2031
|
// Tiers 2-4 — synonym containers (junctionSynonyms itself runs
|
|
1810
2032
|
// single-synonym first, falling to double-synonym only when
|
|
@@ -1817,6 +2039,26 @@ async function crossRegionVotes(
|
|
|
1817
2039
|
true,
|
|
1818
2040
|
sides,
|
|
1819
2041
|
);
|
|
2042
|
+
if (probe) {
|
|
2043
|
+
const singleAttempted = sides.leftSiblings.length > 0 ||
|
|
2044
|
+
sides.rightSiblings.length > 0;
|
|
2045
|
+
const singleReturned = syn[0]?.tier === "single-synonym"
|
|
2046
|
+
? syn.length
|
|
2047
|
+
: 0;
|
|
2048
|
+
const doubleAttempted = singleAttempted && singleReturned === 0 &&
|
|
2049
|
+
sides.leftSiblings.length > 0 && sides.rightSiblings.length > 0;
|
|
2050
|
+
const doubleReturned = syn[0]?.tier === "double-synonym"
|
|
2051
|
+
? syn.length
|
|
2052
|
+
: 0;
|
|
2053
|
+
probe.singleSynonym = {
|
|
2054
|
+
attempted: singleAttempted,
|
|
2055
|
+
candidatesReturned: singleReturned,
|
|
2056
|
+
};
|
|
2057
|
+
probe.doubleSynonym = {
|
|
2058
|
+
attempted: doubleAttempted,
|
|
2059
|
+
candidatesReturned: doubleReturned,
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
1820
2062
|
if (syn.length > 0) {
|
|
1821
2063
|
containers = syn;
|
|
1822
2064
|
tier = syn[0].tier;
|
|
@@ -1862,12 +2104,45 @@ async function crossRegionVotes(
|
|
|
1862
2104
|
// backstop, so both sides earn their place here the same way an
|
|
1863
2105
|
// ordinary approximate region earns its individual vote.
|
|
1864
2106
|
const gap = rb.start - ra.end;
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
2107
|
+
const reasons: NonNullable<
|
|
2108
|
+
StructuralResonanceTrace["ineligibleReasons"]
|
|
2109
|
+
> = [];
|
|
2110
|
+
if (between.length > 0) reasons.push("between-region");
|
|
2111
|
+
if (!strong.has(cand[a]) || !strong.has(cand[b])) {
|
|
2112
|
+
reasons.push("not-both-strong");
|
|
2113
|
+
}
|
|
2114
|
+
if (!ra.known || !rb.known) reasons.push("not-both-known");
|
|
2115
|
+
if (gap > maxInterior) reasons.push("gap-too-large");
|
|
2116
|
+
let resonanceTrace: StructuralResonanceTrace | undefined;
|
|
2117
|
+
if (reasons.length > 0) {
|
|
2118
|
+
if (probe) {
|
|
2119
|
+
resonanceTrace = {
|
|
2120
|
+
variantBudget: ctx.cfg.haloQueryK,
|
|
2121
|
+
variants: [],
|
|
2122
|
+
mergedProposals: 0,
|
|
2123
|
+
examined: [],
|
|
2124
|
+
noiseFloor: estimatorNoise(ctx.store.D),
|
|
2125
|
+
outcome: "ineligible",
|
|
2126
|
+
ineligibleReasons: reasons,
|
|
2127
|
+
};
|
|
2128
|
+
probe.resonance = resonanceTrace;
|
|
2129
|
+
}
|
|
2130
|
+
} else {
|
|
2131
|
+
if (probe) {
|
|
2132
|
+
// `outcome`/`noiseFloor` are required fields with no natural
|
|
2133
|
+
// "unset" value; structuralResonance (called just below) always
|
|
2134
|
+
// overwrites both before returning, on every one of its exit
|
|
2135
|
+
// paths — these are never read in their initial form.
|
|
2136
|
+
resonanceTrace = {
|
|
2137
|
+
variantBudget: ctx.cfg.haloQueryK,
|
|
2138
|
+
variants: [],
|
|
2139
|
+
mergedProposals: 0,
|
|
2140
|
+
examined: [],
|
|
2141
|
+
noiseFloor: 0,
|
|
2142
|
+
outcome: "empty",
|
|
2143
|
+
};
|
|
2144
|
+
probe.resonance = resonanceTrace;
|
|
2145
|
+
}
|
|
1871
2146
|
const ownRootsA = rvs.votes.find((v) =>
|
|
1872
2147
|
v.start === ra.start && v.end === ra.end
|
|
1873
2148
|
)?.roots;
|
|
@@ -1885,9 +2160,15 @@ async function crossRegionVotes(
|
|
|
1885
2160
|
reachMemo,
|
|
1886
2161
|
ownRootsA,
|
|
1887
2162
|
ownRootsB,
|
|
2163
|
+
resonanceTrace,
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
if (structuralPick === null) {
|
|
2167
|
+
pushProbe(
|
|
2168
|
+
reasons.length > 0 ? "resonance-ineligible" : "resonance-rejected",
|
|
1888
2169
|
);
|
|
2170
|
+
continue;
|
|
1889
2171
|
}
|
|
1890
|
-
if (structuralPick === null) continue;
|
|
1891
2172
|
tier = "structural-resonance";
|
|
1892
2173
|
}
|
|
1893
2174
|
|
|
@@ -1911,6 +2192,21 @@ async function crossRegionVotes(
|
|
|
1911
2192
|
idf = structuralPick.idf;
|
|
1912
2193
|
confidence = structuralPick.proposal.effectiveScore;
|
|
1913
2194
|
} else {
|
|
2195
|
+
// Aggregate structural-tier trace (spec §4) — one per DAG tier that
|
|
2196
|
+
// returned at least one container (exact, single-synonym or
|
|
2197
|
+
// double-synonym); only aggregate counts and the final outcome are
|
|
2198
|
+
// recorded, never every candidate.
|
|
2199
|
+
const structuralTrace: CrossRegionStructuralTrace | undefined = probe
|
|
2200
|
+
? {
|
|
2201
|
+
tier: tier as "exact" | "single-synonym" | "double-synonym",
|
|
2202
|
+
selfEvidenceRejected: 0,
|
|
2203
|
+
contradictionRejected: 0,
|
|
2204
|
+
passedGuards: 0,
|
|
2205
|
+
outcome: "all-rejected",
|
|
2206
|
+
}
|
|
2207
|
+
: undefined;
|
|
2208
|
+
if (probe) probe.structural = structuralTrace;
|
|
2209
|
+
|
|
1914
2210
|
// N-ARY selection: the container covering the MOST remaining candidate
|
|
1915
2211
|
// forms wins (then tightest interior, then lowest id). Reads are
|
|
1916
2212
|
// cache hits — every container's bytes were already read by the walk.
|
|
@@ -1931,7 +2227,10 @@ async function crossRegionVotes(
|
|
|
1931
2227
|
Math.min(li, ri),
|
|
1932
2228
|
Math.max(li + left.length, ri + right.length),
|
|
1933
2229
|
);
|
|
1934
|
-
if (indexOf(query, joined, 0) >= 0)
|
|
2230
|
+
if (indexOf(query, joined, 0) >= 0) {
|
|
2231
|
+
if (structuralTrace) structuralTrace.selfEvidenceRejected++;
|
|
2232
|
+
continue; // query says it itself
|
|
2233
|
+
}
|
|
1935
2234
|
}
|
|
1936
2235
|
// CONTRADICTION GUARD: a between-region already carrying its own
|
|
1937
2236
|
// vote must actually recur in this container's bytes — otherwise
|
|
@@ -1947,8 +2246,10 @@ async function crossRegionVotes(
|
|
|
1947
2246
|
) < 0
|
|
1948
2247
|
)
|
|
1949
2248
|
) {
|
|
2249
|
+
if (structuralTrace) structuralTrace.contradictionRejected++;
|
|
1950
2250
|
continue;
|
|
1951
2251
|
}
|
|
2252
|
+
if (structuralTrace) structuralTrace.passedGuards++;
|
|
1952
2253
|
let cov = left.length + right.length;
|
|
1953
2254
|
const extras: number[] = [];
|
|
1954
2255
|
for (const ei of cand) {
|
|
@@ -1972,12 +2273,31 @@ async function crossRegionVotes(
|
|
|
1972
2273
|
bestCov = cov;
|
|
1973
2274
|
}
|
|
1974
2275
|
}
|
|
1975
|
-
if (best === null)
|
|
2276
|
+
if (best === null) {
|
|
2277
|
+
// every container was self-evidence / contradiction — outcome
|
|
2278
|
+
// stays "all-rejected".
|
|
2279
|
+
pushProbe("structural-rejected");
|
|
2280
|
+
continue;
|
|
2281
|
+
}
|
|
1976
2282
|
|
|
1977
2283
|
const r = edgeAncestors(ctx, best.id, N, reachMemo);
|
|
1978
|
-
if (r.saturated || r.roots.length === 0)
|
|
2284
|
+
if (r.saturated || r.roots.length === 0) {
|
|
2285
|
+
if (structuralTrace) {
|
|
2286
|
+
structuralTrace.outcome = r.saturated ? "saturated" : "no-roots";
|
|
2287
|
+
}
|
|
2288
|
+
pushProbe("structural-rejected");
|
|
2289
|
+
continue;
|
|
2290
|
+
}
|
|
1979
2291
|
const df = Math.log(N / Math.max(1, r.contextsReached));
|
|
1980
|
-
if (df <= 0)
|
|
2292
|
+
if (df <= 0) {
|
|
2293
|
+
if (structuralTrace) structuralTrace.outcome = "nonpositive-idf";
|
|
2294
|
+
pushProbe("structural-rejected");
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2297
|
+
if (structuralTrace) {
|
|
2298
|
+
structuralTrace.outcome = "accepted";
|
|
2299
|
+
structuralTrace.selectedNode = best.id;
|
|
2300
|
+
}
|
|
1981
2301
|
reach = r;
|
|
1982
2302
|
idf = df;
|
|
1983
2303
|
// Confidence used by voting (spec §13): exact junction = 1;
|
|
@@ -2058,6 +2378,7 @@ async function crossRegionVotes(
|
|
|
2058
2378
|
wFocus: w,
|
|
2059
2379
|
absorbed: 1 + explainedAway,
|
|
2060
2380
|
});
|
|
2381
|
+
pushProbe("accepted");
|
|
2061
2382
|
if (td) {
|
|
2062
2383
|
td.crossRegionJunctionVotes.push({
|
|
2063
2384
|
container: best.id,
|
|
@@ -2067,6 +2388,11 @@ async function crossRegionVotes(
|
|
|
2067
2388
|
explainedAwayRegionIndices: explainedAwayIndices,
|
|
2068
2389
|
absorbed: 1 + explainedAway,
|
|
2069
2390
|
tier,
|
|
2391
|
+
probe: td.crossRegionProbes.length - 1,
|
|
2392
|
+
confidence,
|
|
2393
|
+
evidenceBytes: bestCov,
|
|
2394
|
+
mutualWeight: mutual,
|
|
2395
|
+
voteWeightPerRoot: w,
|
|
2070
2396
|
});
|
|
2071
2397
|
}
|
|
2072
2398
|
|
|
@@ -2114,6 +2440,11 @@ async function crossRegionVotes(
|
|
|
2114
2440
|
}
|
|
2115
2441
|
|
|
2116
2442
|
if (td) td.supersededOrdinaryVotes = superseded.size;
|
|
2443
|
+
if (td?.crossRegionSummary) {
|
|
2444
|
+
td.crossRegionSummary.stopReason = probes >= k
|
|
2445
|
+
? "probe-limit"
|
|
2446
|
+
: "pairs-exhausted";
|
|
2447
|
+
}
|
|
2117
2448
|
return { votes: out, superseded };
|
|
2118
2449
|
}
|
|
2119
2450
|
|
|
@@ -2176,9 +2507,14 @@ export function traceAttention(
|
|
|
2176
2507
|
...(td.crossRegionSummary
|
|
2177
2508
|
? {
|
|
2178
2509
|
crossRegion: {
|
|
2179
|
-
|
|
2510
|
+
eligibleRegions: td.crossRegionSummary.eligibleRegions,
|
|
2511
|
+
maximalRegions: td.crossRegionSummary.maximalRegions,
|
|
2512
|
+
probeLimit: td.crossRegionSummary.probeLimit,
|
|
2513
|
+
probesAttempted: td.crossRegionSummary.probesAttempted,
|
|
2180
2514
|
junctionVotes: td.crossRegionJunctionVotes,
|
|
2181
2515
|
supersededOrdinaryVotes: td.supersededOrdinaryVotes,
|
|
2516
|
+
probes: td.crossRegionProbes,
|
|
2517
|
+
stopReason: td.crossRegionSummary.stopReason ?? "pairs-exhausted",
|
|
2182
2518
|
},
|
|
2183
2519
|
}
|
|
2184
2520
|
: {}),
|