@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
|
@@ -75,6 +75,10 @@ export interface ConsensusAnchorTrace {
|
|
|
75
75
|
rank: number;
|
|
76
76
|
pooledVote: number;
|
|
77
77
|
idfVote: number;
|
|
78
|
+
/** The LARGEST single-region contribution behind this anchor — the bar
|
|
79
|
+
* recall's own gate reads (mechanisms/recall.ts). Published so the one
|
|
80
|
+
* decision-making quantity the climb computes is not invisible. */
|
|
81
|
+
peak: number;
|
|
78
82
|
candidateBreadth: number;
|
|
79
83
|
contributingVotes: number;
|
|
80
84
|
contributingEvidence: number;
|
|
@@ -15,6 +15,7 @@ import { leafIdRun } from "./canonical.js";
|
|
|
15
15
|
import { atomIsHub, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "./traverse.js";
|
|
16
16
|
import { cachedRead, junctionContainersFrom, junctionSeeds, junctionSynonyms, loadJunctionSynonymSides, walkCache, } from "./junction.js";
|
|
17
17
|
import { indexOf } from "../bytes.js";
|
|
18
|
+
import { restates } from "./derivation.js";
|
|
18
19
|
import { rItem, rNode, traceDerivation } from "./trace.js";
|
|
19
20
|
function newTraceDraft(perceivedCount) {
|
|
20
21
|
return {
|
|
@@ -775,15 +776,18 @@ export async function voteRegions(ctx, query, regions, k, mode, N, reachMemo, td
|
|
|
775
776
|
}
|
|
776
777
|
contrastiveMargin = margin;
|
|
777
778
|
// Scaled by what this region does NOT address — see `cov` above.
|
|
778
|
-
|
|
779
|
-
|
|
779
|
+
// The bar THIS gate applies: the estimator's noise scaled by what the
|
|
780
|
+
// region does NOT address (`cov`). ONE definition, used by the rejection
|
|
781
|
+
// path below and by the voted payload — the trace reports the applied bar.
|
|
782
|
+
const appliedFloor = estimatorNoise(ctx.store.D) * (1 - cov);
|
|
783
|
+
if (margin <= appliedFloor) {
|
|
780
784
|
recordRegion("contrastive-margin-rejection", {
|
|
781
785
|
selected,
|
|
782
786
|
reachNode: voterId,
|
|
783
787
|
idf,
|
|
784
788
|
dfWeight: wf,
|
|
785
789
|
contrastiveMargin: margin,
|
|
786
|
-
contrastiveNoiseFloor:
|
|
790
|
+
contrastiveNoiseFloor: appliedFloor,
|
|
787
791
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
788
792
|
});
|
|
789
793
|
continue;
|
|
@@ -834,7 +838,12 @@ export async function voteRegions(ctx, query, regions, k, mode, N, reachMemo, td
|
|
|
834
838
|
...(contrastiveMargin !== undefined
|
|
835
839
|
? {
|
|
836
840
|
contrastiveMargin,
|
|
837
|
-
|
|
841
|
+
// THE BAR THE GATE ACTUALLY APPLIED — the same expression
|
|
842
|
+
// the rejection path's `appliedFloor` defines, inline here because
|
|
843
|
+
// this payload is built in a scope that does not carry that local.
|
|
844
|
+
// Publishing the raw estimatorNoise(D) instead made a region that
|
|
845
|
+
// PASSED look closer to its limit than it was.
|
|
846
|
+
contrastiveNoiseFloor: estimatorNoise(ctx.store.D) * (1 - cov),
|
|
838
847
|
...(contrastiveRival ? { contrastiveRival } : {}),
|
|
839
848
|
}
|
|
840
849
|
: {}),
|
|
@@ -939,7 +948,17 @@ export function poolVotes(ctx, regionVotes, sat, N, td) {
|
|
|
939
948
|
},
|
|
940
949
|
pool,
|
|
941
950
|
};
|
|
942
|
-
|
|
951
|
+
// THE SEARCH WAS THE ONE LAYER WITH NO TIME. The climb's phases are timed
|
|
952
|
+
// (voteRegions, structuralResonance, crossRegion) but the pooled derivation
|
|
953
|
+
// was not, so any cost or gain inside it stayed invisible.
|
|
954
|
+
// `timeSync`, not `time`: the search is SYNCHRONOUS, and wrapping it in a
|
|
955
|
+
// promise only to time it would make the profiled path wait where the
|
|
956
|
+
// unprofiled one does not (meter.ts's own contract).
|
|
957
|
+
if (ctx.meter) {
|
|
958
|
+
ctx.meter.timeSync("climb.derivation", () => lightestDerivation(system));
|
|
959
|
+
}
|
|
960
|
+
else
|
|
961
|
+
lightestDerivation(system);
|
|
943
962
|
const votes = new Map();
|
|
944
963
|
const votesIdf = new Map();
|
|
945
964
|
const support = new Map();
|
|
@@ -973,12 +992,24 @@ export function poolVotes(ctx, regionVotes, sat, N, td) {
|
|
|
973
992
|
// The LARGEST single region's contribution to this anchor's pooled vote.
|
|
974
993
|
// The pool is a SUM (deliberately — see the pooling note above), so it says
|
|
975
994
|
// how much evidence there is in total, never whether any ONE place in the
|
|
976
|
-
// query carries evidence on its own.
|
|
977
|
-
//
|
|
978
|
-
//
|
|
979
|
-
//
|
|
980
|
-
//
|
|
981
|
-
//
|
|
995
|
+
// query carries evidence on its own. Recorded here, beside the count,
|
|
996
|
+
// because this is the only place the per-region contributions are still
|
|
997
|
+
// separable.
|
|
998
|
+
//
|
|
999
|
+
// THE BAR IS THE POOLED FLOOR, AND IT WAS ONCE CLAIMED OTHERWISE HERE.
|
|
1000
|
+
// This comment used to say that holding an anchor to consensusFloor(N)
|
|
1001
|
+
// "prices ONE region's evidence", so comparing a six-region sum against it
|
|
1002
|
+
// was "a dimensional error". THAT WAS FALSE. `thresholds.md` §2 derives
|
|
1003
|
+
// `consensusFloor` as the POOLED-vote significance floor ("each region
|
|
1004
|
+
// contributes at most ln(N/c) <= ln(N); ln(N) + 1/2 demands ..."), and the
|
|
1005
|
+
// climb weights by IDF, so the sum and the floor are in ONE dimension —
|
|
1006
|
+
// which is exactly why `recall.ts` gates `forest[0].idfVote` against it and
|
|
1007
|
+
// why `commitVotes` does too. The other two weighting modes DO leave that
|
|
1008
|
+
// dimension (by at most ln 2, two-sided: `direct` deflates a region and
|
|
1009
|
+
// `combined` inflates it), and the gates therefore read the IDF sum, which
|
|
1010
|
+
// is mode-independent; `test/55` tests 19 and 20 pin both halves — the sum
|
|
1011
|
+
// as the reading the bar is derived for, and the absence of any gate
|
|
1012
|
+
// inversion across the three modes.
|
|
982
1013
|
const regionPeak = new Map();
|
|
983
1014
|
const steps = [];
|
|
984
1015
|
let order = 0;
|
|
@@ -1136,12 +1167,23 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
|
|
|
1136
1167
|
anchor,
|
|
1137
1168
|
vote,
|
|
1138
1169
|
peak: regionPeak.get(anchor) ?? 0,
|
|
1170
|
+
idfVote: votesIdf.get(anchor) ?? 0,
|
|
1139
1171
|
start: s.start,
|
|
1140
1172
|
end: s.end,
|
|
1141
1173
|
breadth: (regionSupport.get(anchor) ?? 0) / totalRegions,
|
|
1142
1174
|
clusters: countClusters(regionSpans.get(anchor) ?? [], ctx.space.maxGroup),
|
|
1143
1175
|
};
|
|
1144
1176
|
})
|
|
1177
|
+
// THE ORDER IS NOT A PREFERENCE: with equal evidence it decides ADMISSION,
|
|
1178
|
+
// through the stable sort and the first-come overlap absorption below.
|
|
1179
|
+
// Measured on test/34's corpus, query "red": the two candidates (`red
|
|
1180
|
+
// circle` and `red square`) carry IDENTICAL `vote` and IDENTICAL `idfVote`
|
|
1181
|
+
// (1.3863 each, three seeds), so this comparator leaves them tied and the
|
|
1182
|
+
// stable sort keeps the ENUMERATION order — which is corpus-determined and
|
|
1183
|
+
// admits `red square`, 60/60 seeds. Adding an id tie-break (`|| a.anchor -
|
|
1184
|
+
// b.anchor`) picks `red circle` instead and makes a single region reach the
|
|
1185
|
+
// JOINT context, which is the premise `test/34` exists to protect. The
|
|
1186
|
+
// gates read IDF; this line only decides who gets looked at first.
|
|
1145
1187
|
.sort((a, b) => b.vote - a.vote);
|
|
1146
1188
|
const overlaps = (a, b) => a.start < b.end && b.start < a.end;
|
|
1147
1189
|
// Read the root cut from the anchors the QUERY pointed at. A vote standing
|
|
@@ -1177,6 +1219,13 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
|
|
|
1177
1219
|
rank,
|
|
1178
1220
|
pooledVote: point.vote,
|
|
1179
1221
|
idfVote: votesIdf.get(point.anchor) ?? 0,
|
|
1222
|
+
// The LARGEST single-region contribution behind this anchor — the bar
|
|
1223
|
+
// recall's own gate reads (mechanisms/recall.ts: forest[0].peak > LN2),
|
|
1224
|
+
// and until now the only decision-making quantity the climb computed and
|
|
1225
|
+
// did not publish. `regionPeak` reached `ranked` (see its build below)
|
|
1226
|
+
// and stopped there. Published, not recomputed: the value is the one the
|
|
1227
|
+
// climb already carries.
|
|
1228
|
+
peak: point.peak,
|
|
1180
1229
|
candidateBreadth: regions.length,
|
|
1181
1230
|
contributingVotes: regionAxioms.get(point.anchor) ?? 0,
|
|
1182
1231
|
contributingEvidence: regionSupport.get(point.anchor) ?? 0,
|
|
@@ -1206,6 +1255,18 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
|
|
|
1206
1255
|
let passesConsensusFloor;
|
|
1207
1256
|
let pastLeadingSaturation;
|
|
1208
1257
|
let tiedWithDominant;
|
|
1258
|
+
// ── ONE OF THREE ADMISSIONS, AND THEY ARE NOT THE SAME READING ────────
|
|
1259
|
+
// This block admits by VOTES: per-region evidence pooled, gated on the
|
|
1260
|
+
// natural break and on consensusFloor, with the dominant allowed to bypass
|
|
1261
|
+
// both. `structuralResonance` admits by a MARGIN over the estimator's own
|
|
1262
|
+
// noise, and `crossRegionVotes` admits by STRUCTURE (which regions may pair
|
|
1263
|
+
// at all, with at least one side individually discriminative). Read
|
|
1264
|
+
// together they look like one policy written three times; they are three
|
|
1265
|
+
// different measurements of the same question ("is this evidence?"), and
|
|
1266
|
+
// unifying them would average three readings into one — the mistake
|
|
1267
|
+
// `extraction.ts` records as "do not unify the two into one machine".
|
|
1268
|
+
// What they DO share, and must keep sharing, is the discipline of deriving
|
|
1269
|
+
// every bar from D/W/N rather than choosing it (thresholds.md).
|
|
1209
1270
|
const rejectionReasons = [];
|
|
1210
1271
|
if (absorbed) {
|
|
1211
1272
|
status = "overlap";
|
|
@@ -1216,9 +1277,75 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
|
|
|
1216
1277
|
pastLeadingSaturation = pastLeading;
|
|
1217
1278
|
const vote = votesIdf.get(point.anchor) ?? 0;
|
|
1218
1279
|
if (roots.length === 0) {
|
|
1219
|
-
//
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1280
|
+
// THE DOMINANCE PRIVILEGE, AND THE TENSION IT CARRIES (measured).
|
|
1281
|
+
//
|
|
1282
|
+
// The first non-overlapping candidate is DOMINANT: it bypasses both
|
|
1283
|
+
// vote gates below and grounds on its own; only the leading-saturation
|
|
1284
|
+
// gate still applies to it. The privilege is load-bearing — analogies,
|
|
1285
|
+
// substitutions and composed contexts are precisely candidates the
|
|
1286
|
+
// query does NOT contain, and the engine loses them without it.
|
|
1287
|
+
//
|
|
1288
|
+
// WHICH candidate receives it, though, is decided by this loop's ORDER.
|
|
1289
|
+
// That order comes from `ranked`, and when two candidates carry equal
|
|
1290
|
+
// evidence the stable sort preserves the ENUMERATION order, so the
|
|
1291
|
+
// privilege is allocated by an ordering rather than by a rule.
|
|
1292
|
+
//
|
|
1293
|
+
// Measured on test/34's corpus, query "red":
|
|
1294
|
+
//
|
|
1295
|
+
// 0:#77 vote=1.3863 idf=1.3863 [0,3) | 1:#49 vote=1.3863 idf=1.3863 [0,3)
|
|
1296
|
+
//
|
|
1297
|
+
// Both candidates (`red square` #77, `red circle` #49) have IDENTICAL
|
|
1298
|
+
// `vote` AND IDENTICAL `idfVote` over the SAME support span, so the
|
|
1299
|
+
// comparator leaves them tied, the second is absorbed as "overlap", and
|
|
1300
|
+
// the first grounds. With this build's enumeration order that first is
|
|
1301
|
+
// `red square`, 60/60 seeds, and `red circle` — the JOINT context — is
|
|
1302
|
+
// never reached by "red" alone. That is the premise test/34 exists to
|
|
1303
|
+
// protect: no single region reaches the joint context, which is what
|
|
1304
|
+
// makes the binding query unreachable without direct region
|
|
1305
|
+
// interaction.
|
|
1306
|
+
//
|
|
1307
|
+
// THE TENSION: the premise therefore holds BY ENUMERATION ORDER, not by
|
|
1308
|
+
// a rule, so any change to this ordering can move the privilege onto the
|
|
1309
|
+
// joint context and let one region reach it. Measured: adding
|
|
1310
|
+
// `|| a.anchor - b.anchor` — the lowest-id tie-break that AGENTS.md §2
|
|
1311
|
+
// sanctions as an equivalent corpus-determined tie-break — does exactly
|
|
1312
|
+
// that: "red" then attends to `red circle`, test/34 fails 6/1, and the
|
|
1313
|
+
// canonical suite reports 1 failure.
|
|
1314
|
+
//
|
|
1315
|
+
// TWO ATTEMPTS TO MAKE IT A RULE, BOTH REFUTED BY MEASUREMENT:
|
|
1316
|
+
//
|
|
1317
|
+
// 1. EVIDENCE SEPARATION. Grant the privilege only when the first
|
|
1318
|
+
// candidate's evidence is separated from the next distinct
|
|
1319
|
+
// candidate's by more than the co-dominant band (sqrt(k) *
|
|
1320
|
+
// estimatorNoise(D)). Refuted: that band exists to ADMIT the
|
|
1321
|
+
// anchors the estimator cannot separate from the dominant — its own
|
|
1322
|
+
// documented purpose — so withholding the privilege on ties removes
|
|
1323
|
+
// the very case it was written for. Suite: 4 failures (the two
|
|
1324
|
+
// co-dominant band laws, breadth/scale invariance, test/29 D2).
|
|
1325
|
+
//
|
|
1326
|
+
// 2. QUERY-OWNED CONTENT. Grant the privilege only to a candidate
|
|
1327
|
+
// that IS a recognised region's identity (regions.some(r => r.id ===
|
|
1328
|
+
// point.anchor)). Measured: for "circle" that identity IS the
|
|
1329
|
+
// ranked candidate, so the privilege stays and `circle` grounds; for
|
|
1330
|
+
// "red" the identity is the `red` node itself while the candidates
|
|
1331
|
+
// are the conjunctions, so neither is privileged; for "red then
|
|
1332
|
+
// circle" the composed context carries idf 3.958 and clears both
|
|
1333
|
+
// gates on its own evidence. All four control queries came out
|
|
1334
|
+
// right — and the suite: 10 failures, six of them in the
|
|
1335
|
+
// analogy/counterfactual/CAST suites ("an analogy still transfers
|
|
1336
|
+
// from a structure the query never names"; "a substitute the query
|
|
1337
|
+
// NAMES may still be voiced"). Refuted: the privilege exists to
|
|
1338
|
+
// admit what the query does NOT contain, so identity is the wrong
|
|
1339
|
+
// axis.
|
|
1340
|
+
//
|
|
1341
|
+
// WHAT A FUTURE ATTEMPT MUST RESPECT: whatever allocates this privilege
|
|
1342
|
+
// has to (a) keep it available to candidates the query does not contain
|
|
1343
|
+
// — analogies, substitutions, compositions — and (b) not depend on the
|
|
1344
|
+
// estimator's ordering among anchors of equal evidence, because that
|
|
1345
|
+
// ordering is not a fact about the corpus. No lever satisfying both has
|
|
1346
|
+
// been found. Until one is, this premise rests on the enumeration order
|
|
1347
|
+
// recorded above, and test/34 is the only test that notices if it
|
|
1348
|
+
// moves.
|
|
1222
1349
|
dominant = true;
|
|
1223
1350
|
if (pastLeading) {
|
|
1224
1351
|
status = "root";
|
|
@@ -1229,8 +1356,15 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N, td, cfg)
|
|
|
1229
1356
|
}
|
|
1230
1357
|
}
|
|
1231
1358
|
else {
|
|
1232
|
-
|
|
1233
|
-
|
|
1359
|
+
// THE FLOOR AND THE BREAK READ THE IDF WEIGHTING. `floor` is derived
|
|
1360
|
+
// for pooled IDF-weighted votes, and `rootCut` comes from the IDF
|
|
1361
|
+
// distribution (`idfDesc`), so gating the mode-dependent `vote` against
|
|
1362
|
+
// either let a weighting mode change an admission (measured: anchor 87,
|
|
1363
|
+
// inverse 2.682 admitted vs direct 1.468 refused). Reading the IDF sum
|
|
1364
|
+
// makes the verdict mode-independent, and changes nothing in the
|
|
1365
|
+
// engine's own mode, where the two readings coincide.
|
|
1366
|
+
passesNaturalBreak = point.idfVote >= rootCut;
|
|
1367
|
+
passesConsensusFloor = point.idfVote >= floor;
|
|
1234
1368
|
// CO-DOMINANT — an anchor the estimator cannot separate from the
|
|
1235
1369
|
// dominant inherits the dominant's exemption, because that exemption's
|
|
1236
1370
|
// only warrant is being TOP, and "top" is not a fact about the corpus
|
|
@@ -1685,6 +1819,13 @@ ownRootsA, ownRootsB, trace) {
|
|
|
1685
1819
|
outcome,
|
|
1686
1820
|
});
|
|
1687
1821
|
};
|
|
1822
|
+
// ── ADMISSION BY MARGIN, not by votes (see voteRegions' note) ─────────
|
|
1823
|
+
// What this site measures: how far the best ANN proposal's effective score
|
|
1824
|
+
// (score × semanticConfidence) stands above the runner-up's, against
|
|
1825
|
+
// `estimatorNoise(D)`. What it does NOT measure: how many regions voted,
|
|
1826
|
+
// or whether the query's regions agree — that is voteRegions' question, and
|
|
1827
|
+
// here a synthetic gist has already replaced them. The two bars are both
|
|
1828
|
+
// derived (thresholds.md), and neither is a tuning of the other.
|
|
1688
1829
|
let selected = null;
|
|
1689
1830
|
let selectedReach = null;
|
|
1690
1831
|
let selectedIdf = 0;
|
|
@@ -1790,6 +1931,15 @@ async function crossRegionVotes(ctx, query, regions, rvs, k, N, reachMemo, td) {
|
|
|
1790
1931
|
// successfully reconstructed while probing one pair must not be read and
|
|
1791
1932
|
// perceived again while probing another pair in the same climb.
|
|
1792
1933
|
const siblingGistMemo = new Map();
|
|
1934
|
+
// ── ADMISSION BY STRUCTURE, not by a bar (see voteRegions' note) ──────
|
|
1935
|
+
// What this site decides: WHICH regions may pair at all — a region that
|
|
1936
|
+
// already voted (individually discriminative), or a KNOWN non-voting one as
|
|
1937
|
+
// the weak side of a pair whose other side voted; never two non-voting
|
|
1938
|
+
// regions, and never a span contained in a maximal one whose reading is
|
|
1939
|
+
// exact. The bar (the container's idf) comes later, on the candidate. So
|
|
1940
|
+
// its "rejection reasons" name structural disqualifications — a different
|
|
1941
|
+
// vocabulary because it answers a different question, and the three
|
|
1942
|
+
// taxonomies stay separate for the same reason the readings do.
|
|
1793
1943
|
const votedSpans = new Set();
|
|
1794
1944
|
for (const rv of rvs.votes)
|
|
1795
1945
|
votedSpans.add(`${rv.start},${rv.end}`);
|
|
@@ -2130,7 +2280,7 @@ async function crossRegionVotes(ctx, query, regions, rvs, k, N, reachMemo, td) {
|
|
|
2130
2280
|
const ri = indexOf(bytes, right, 0);
|
|
2131
2281
|
if (li >= 0 && ri >= 0) {
|
|
2132
2282
|
const joined = bytes.subarray(Math.min(li, ri), Math.max(li + left.length, ri + right.length));
|
|
2133
|
-
if (
|
|
2283
|
+
if (restates(query, joined, 0)) {
|
|
2134
2284
|
if (structuralTrace)
|
|
2135
2285
|
structuralTrace.selfEvidenceRejected++;
|
|
2136
2286
|
continue; // query says it itself
|
|
@@ -23,6 +23,22 @@ export declare function leafIdRun(ctx: MindContext, bytes: Uint8Array, from: num
|
|
|
23
23
|
* what a partial prefix means (deposit only interns the whole-stream flat
|
|
24
24
|
* branch when the prefix covers everything). */
|
|
25
25
|
export declare function leafIdPrefix(ctx: MindContext, bytes: Uint8Array): number[];
|
|
26
|
+
/** Which prefixes of `prefix ‖ tail` are STORED NODES — as lengths in the
|
|
27
|
+
* tail's own coordinates, ascending, excluding the empty one. This is the
|
|
28
|
+
* candidate set a rule needs to join an already-stored prefix to a suffix it
|
|
29
|
+
* has not stored: a key names a relation exactly when `prefix ‖ tail[0..p]` IS
|
|
30
|
+
* a node, and a key can end strictly inside the tail without sitting on any
|
|
31
|
+
* fold boundary (a stored member's end is the end of ITS OWN stream, and the
|
|
32
|
+
* fold never emits a cut at a stream's end). Measured: "stockholm mayor"
|
|
33
|
+
* exists, leads on, and its boundary 6 is in neither the tail's cuts nor the
|
|
34
|
+
* concatenation's.
|
|
35
|
+
*
|
|
36
|
+
* ONE cheap content-addressed probe per offset — `leafIdPrefix` walks the bytes
|
|
37
|
+
* once (a point probe each), `findBranch` hashes the growing kid run — and NO
|
|
38
|
+
* `resolve`, which is what keeps this off the O(suffix) vector folds the
|
|
39
|
+
* recognition path pays. It stops at the first byte that was never interned,
|
|
40
|
+
* which costs nothing real: a stored key's bytes are interned by construction. */
|
|
41
|
+
export declare function keyEnds(ctx: MindContext, prefix: Uint8Array, tail: Uint8Array): number[];
|
|
26
42
|
/** The canonical W-window node ids of a byte stream, offset → id — the
|
|
27
43
|
* CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
|
|
28
44
|
* two deposits share IS the same node (hash-consing paid the comparison at
|
|
@@ -70,6 +70,47 @@ export function leafIdPrefix(ctx, bytes) {
|
|
|
70
70
|
}
|
|
71
71
|
return ids;
|
|
72
72
|
}
|
|
73
|
+
/** Which prefixes of `prefix ‖ tail` are STORED NODES — as lengths in the
|
|
74
|
+
* tail's own coordinates, ascending, excluding the empty one. This is the
|
|
75
|
+
* candidate set a rule needs to join an already-stored prefix to a suffix it
|
|
76
|
+
* has not stored: a key names a relation exactly when `prefix ‖ tail[0..p]` IS
|
|
77
|
+
* a node, and a key can end strictly inside the tail without sitting on any
|
|
78
|
+
* fold boundary (a stored member's end is the end of ITS OWN stream, and the
|
|
79
|
+
* fold never emits a cut at a stream's end). Measured: "stockholm mayor"
|
|
80
|
+
* exists, leads on, and its boundary 6 is in neither the tail's cuts nor the
|
|
81
|
+
* concatenation's.
|
|
82
|
+
*
|
|
83
|
+
* ONE cheap content-addressed probe per offset — `leafIdPrefix` walks the bytes
|
|
84
|
+
* once (a point probe each), `findBranch` hashes the growing kid run — and NO
|
|
85
|
+
* `resolve`, which is what keeps this off the O(suffix) vector folds the
|
|
86
|
+
* recognition path pays. It stops at the first byte that was never interned,
|
|
87
|
+
* which costs nothing real: a stored key's bytes are interned by construction. */
|
|
88
|
+
export function keyEnds(ctx, prefix, tail) {
|
|
89
|
+
if (prefix.length === 0 || tail.length === 0)
|
|
90
|
+
return [];
|
|
91
|
+
const joined = new Uint8Array(prefix.length + tail.length);
|
|
92
|
+
joined.set(prefix, 0);
|
|
93
|
+
joined.set(tail, prefix.length);
|
|
94
|
+
const ids = leafIdPrefix(ctx, joined);
|
|
95
|
+
if (ids.length < prefix.length)
|
|
96
|
+
return [];
|
|
97
|
+
const ends = [];
|
|
98
|
+
// The kid run GROWS by one id per offset; `findBranch` wants an array, so the
|
|
99
|
+
// run is built once and pushed into, never re-sliced. Re-slicing
|
|
100
|
+
// `ids.slice(0, prefix.length + p)` per offset made this O(|tail| ·
|
|
101
|
+
// (|prefix| + |tail|)) — quadratic in the tail, where the learning path this
|
|
102
|
+
// follows slices a run that SHRINKS. Same ends, linear copying.
|
|
103
|
+
const run = ids.slice(0, prefix.length);
|
|
104
|
+
// The loop ENDS at the first byte that was never interned (`ids.length`):
|
|
105
|
+
// every later prefix contains it, so none of them can be a node either — this
|
|
106
|
+
// is where the scan stops, not a silent truncation of the answer.
|
|
107
|
+
for (let p = 1; prefix.length + p <= ids.length; p++) {
|
|
108
|
+
run.push(ids[prefix.length + p - 1]);
|
|
109
|
+
if (ctx.store.findBranch(run) !== null)
|
|
110
|
+
ends.push(p);
|
|
111
|
+
}
|
|
112
|
+
return ends;
|
|
113
|
+
}
|
|
73
114
|
/** The canonical W-window node ids of a byte stream, offset → id — the
|
|
74
115
|
* CONTENT-ADDRESSED IDENTITY of every W-sized slice, under which any content
|
|
75
116
|
* two deposits share IS the same node (hash-consing paid the comparison at
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/** A half-open `[start, end)` span of the asker's own bytes. */
|
|
2
|
+
export type Span = readonly [number, number];
|
|
3
|
+
/** The BYTE COUNT of a span list — what the currency calls `unaccounted` in
|
|
4
|
+
* `weight = moves + PASS·unaccounted`. ONE definition: this was four copies of
|
|
5
|
+
* the same `reduce` before the architecture audit collapsed them. */
|
|
6
|
+
export declare function unaccountedBytes(spans: ReadonlyArray<Span>): number;
|
|
7
|
+
/** The `[start, end)` gaps of `[0, queryLen)` NOT covered by `accounted` — the
|
|
8
|
+
* union-of-spans reading the ladder prices at PASS per byte, and the raw
|
|
9
|
+
* material every closure decision measures. Clipped to the question, sorted
|
|
10
|
+
* and merged, so two overlapping spans account for their union once. */
|
|
11
|
+
export declare function unexplainedSpans(queryLen: number, accounted: ReadonlyArray<Span>): Array<[number, number]>;
|
|
12
|
+
/** THE REMAINDER: what no step has accounted for, with every span below one
|
|
13
|
+
* river-fold quantum dropped. `W` is the mind's own line between bridging
|
|
14
|
+
* punctuation and a substantive phrase — the same floor `liftedScaffolding`
|
|
15
|
+
* and the honesty-density bar use — so a remainder under it licenses nothing
|
|
16
|
+
* and blocks nothing. This is the law's measure. */
|
|
17
|
+
export declare function remainderOf(queryLen: number, explained: ReadonlyArray<Span>, W: number): Array<[number, number]>;
|
|
18
|
+
/** A WITNESS — what a transition carries, in one reading: the SPAN it accounts
|
|
19
|
+
* for, and the WINDOW of the question the product itself holds. The window is
|
|
20
|
+
* present only when the product holds question material, and it is the ONLY
|
|
21
|
+
* thing the question's remainder may be consumed by. */
|
|
22
|
+
export interface Witness {
|
|
23
|
+
readonly span: Span;
|
|
24
|
+
readonly window?: Span;
|
|
25
|
+
}
|
|
26
|
+
/** The window of `span` that `product` holds, or null: the ONE reading of
|
|
27
|
+
* coverage — used by {@link carries}, by the move branch, and by the GROUNDING
|
|
28
|
+
* when it decides what its answer has actually paid for. */
|
|
29
|
+
export declare function windowOf(span: Span, product: Uint8Array, query: Uint8Array, W: number): Span | null;
|
|
30
|
+
/** PROGRESS, by coverage: the first member of `remainder` that `product` carries
|
|
31
|
+
* a whole quantum of, or null when it carries none. The window is taken from
|
|
32
|
+
* `query`, the asker's own bytes, so the test is "this product restates a
|
|
33
|
+
* quantum of what was left unaccounted", never a similarity score.
|
|
34
|
+
*
|
|
35
|
+
* One witness per step, deterministically the FIRST in remainder order: the
|
|
36
|
+
* measure stays a single member of a finite list, which is what makes the walk
|
|
37
|
+
* reviewable — and, because the remainder is not drained, it is the walker's
|
|
38
|
+
* cycle protection (not this) that terminates a chain. */
|
|
39
|
+
export declare function carries(remainder: ReadonlyArray<Span>, product: Uint8Array, query: Uint8Array, W: number): Array<Witness> | null;
|
|
40
|
+
/** Whether `bytes` RESTATES the question — says nothing the asker did not just
|
|
41
|
+
* say — and is therefore not an answer. This is a closure condition: a
|
|
42
|
+
* derivation whose product is already the question has added nothing, and the
|
|
43
|
+
* engine asks it in five places. ONE definition, asked everywhere, with the
|
|
44
|
+
* DIFFERENCES between those places supplied as WITNESSES by the caller — never
|
|
45
|
+
* as a mechanism or a producer. If this function ever needs to know who
|
|
46
|
+
* produced the bytes to decide, the right conclusion is that a witness is
|
|
47
|
+
* missing, not that it should dispatch.
|
|
48
|
+
*
|
|
49
|
+
* `floor` is one river-fold quantum: below it, byte overlap is chance, not
|
|
50
|
+
* evidence — the same line `identityBar`, the bridge's `attestedQ` and
|
|
51
|
+
* recognition's site floor all draw. `0` disables the floor, which is the
|
|
52
|
+
* reading the callers that ask before any structure exists use.
|
|
53
|
+
*
|
|
54
|
+
* THE THREE READINGS the callers need, and why each is a witness rather than a
|
|
55
|
+
* branch here:
|
|
56
|
+
*
|
|
57
|
+
* • `proper` — a PROPER part of the question (strictly shorter). This is the
|
|
58
|
+
* reading every tier that rejects a fragment uses.
|
|
59
|
+
* • `whole` — the EQUALITY reading: only "the answer IS the question" counts,
|
|
60
|
+
* because the caller has already handled a proper fragment elsewhere (a
|
|
61
|
+
* recall tier's own subspan tests).
|
|
62
|
+
* • `equate` — the response's own notion of "the same text" (whatever
|
|
63
|
+
* `src/canon.ts` equates: case, width, whitespace). A caller that has one
|
|
64
|
+
* passes it; a caller that does not gets the byte-exact reading. It is the
|
|
65
|
+
* same fallback `resolve` already makes when an exact lookup misses.
|
|
66
|
+
*
|
|
67
|
+
* The LITERAL EXEMPTION is deliberately NOT here: whether a span is the site's
|
|
68
|
+
* own bytes at its own position is the CALLER's knowledge, and a caller states
|
|
69
|
+
* it by not asking (see `segRestatesQuery` in types.ts, which returns false for
|
|
70
|
+
* a literal span before reaching this). */
|
|
71
|
+
export declare function restates(query: Uint8Array, bytes: Uint8Array, floor?: number, witnesses?: {
|
|
72
|
+
equate?: ((b: Uint8Array) => Uint8Array) | null;
|
|
73
|
+
proper?: boolean;
|
|
74
|
+
whole?: boolean;
|
|
75
|
+
/** THE POSITIONAL WITNESS: search from this offset, because the caller has
|
|
76
|
+
* established that only material at or after it counts. A transcript pasted
|
|
77
|
+
* into a single response is the case that needs it — a caller's own prior
|
|
78
|
+
* answer lies LATER in the query, after the root that would restate it — and
|
|
79
|
+
* the reasoner's per-root `alreadyAnswered` guard asks exactly that question.
|
|
80
|
+
* Omitted, the search starts at 0 and the reading is the plain one. */
|
|
81
|
+
from?: number;
|
|
82
|
+
}): boolean;
|
|
83
|
+
/** Whether the query span `[from, to)` lies inside a COMPLETED ASSISTANT TURN —
|
|
84
|
+
* material the engine has already produced, so it is context rather than
|
|
85
|
+
* something the asker is asserting. Recognition and attention still see the
|
|
86
|
+
* full transcript; what excludes these spans is the closure reading "this was
|
|
87
|
+
* already answered", and it is a closure reading rather than a budget: a window
|
|
88
|
+
* inside a prior reply is not a fresh constraint.
|
|
89
|
+
*
|
|
90
|
+
* ONE definition of it. `cursor` is the CALLER's own progress through `turns`
|
|
91
|
+
* (they are ascending and each caller scans its candidates in ascending order),
|
|
92
|
+
* so the amortised search is preserved exactly and a caller passes the same
|
|
93
|
+
* holder for a whole scan: extracting the reading must not cost the scan. */
|
|
94
|
+
export declare function insideAnsweredTurn(turns: ReadonlyArray<Span>, cursor: {
|
|
95
|
+
at: number;
|
|
96
|
+
}, from: number, to: number): boolean;
|
|
97
|
+
/** THE derivation state — the unit that crosses one inference.
|
|
98
|
+
*
|
|
99
|
+
* Every field is read by the law or by the market's one cost ladder, and
|
|
100
|
+
* nothing else travels. A count of steps is a consequence (the cost), and
|
|
101
|
+
* cycle protection belongs to the layer that walks a graph. */
|
|
102
|
+
export interface DerivationState {
|
|
103
|
+
/** PRODUCT — the structure produced: what this derivation stands on. */
|
|
104
|
+
readonly product: Uint8Array;
|
|
105
|
+
/** ACCOUNTED — the asker's spans the producing transition priced. A COST
|
|
106
|
+
* quantity, and the producing mechanism's own judgement of what its answer
|
|
107
|
+
* explains: cover leaves its computed spans out so the PASS-bridged bytes
|
|
108
|
+
* they account for stay charged, while a corroborated substitution DOES
|
|
109
|
+
* account for its span, because the mechanism paid a move for it. */
|
|
110
|
+
readonly accounted: ReadonlyArray<Span>;
|
|
111
|
+
/** REMAINDER — the asker's material no step has accounted for, each member at
|
|
112
|
+
* or above one quantum. Empty means the derivation is CLOSED. */
|
|
113
|
+
readonly remainder: ReadonlyArray<Span>;
|
|
114
|
+
/** COST — position on the one ladder (`graph-search.ts`'s MICRO/STEP/CONCEPT/
|
|
115
|
+
* PASS); the market takes the lattice minimum over it. */
|
|
116
|
+
readonly cost: number;
|
|
117
|
+
/** FIXED — the producer SUPPLIED a fixed point: the query IS the context, so
|
|
118
|
+
* no transition may consume this state. Declared, never inferred. */
|
|
119
|
+
readonly fixed?: boolean;
|
|
120
|
+
/** USED — what the product speaks for. An EMPTY set is itself a declaration
|
|
121
|
+
* ("this answer voices nothing"); omitted means the layer must re-recognise
|
|
122
|
+
* the product to decide for itself. */
|
|
123
|
+
readonly used?: ReadonlySet<number>;
|
|
124
|
+
}
|
|
125
|
+
/** A candidate continuation, as reported by the layer that knows the structure.
|
|
126
|
+
* The layer says what it has; the law decides. */
|
|
127
|
+
export interface Continuation {
|
|
128
|
+
/** The structure the transition would make the derivation's product. */
|
|
129
|
+
readonly product: Uint8Array;
|
|
130
|
+
/** CONTAINS — the transition's structure holds the product: a node in its
|
|
131
|
+
* tree, or one contiguous byte run of it. Resolved by the reporter.
|
|
132
|
+
*
|
|
133
|
+
* SUFFICIENT FOR EVERY DECISION THIS CORE MAKES, and a boolean is the minimum:
|
|
134
|
+
* the law reads it ONCE, as the admission gate, and that decision is binary —
|
|
135
|
+
* may this state be consumed by this transition at all? Every other decision
|
|
136
|
+
* is fed by other witnesses, never by this one: the product's identity is
|
|
137
|
+
* `resolve(product)`, progress is the window a move carries or the
|
|
138
|
+
* `reaches` declaration, accounting is the span. Carrying the reporter's
|
|
139
|
+
* structure here would therefore be a dump of mechanism internals bought for
|
|
140
|
+
* nothing. The producers make it true by construction — a continuation is
|
|
141
|
+
* built from the CURRENT product's own structure, never from a different one
|
|
142
|
+
* — and test/133 pins the refusal when a reporter declares false. */
|
|
143
|
+
readonly contains: boolean;
|
|
144
|
+
/** REACHES — the transition MOVES: it reaches structure this derivation has
|
|
145
|
+
* not consumed
|
|
146
|
+
* (a node outside the walker's own set). The second species of progress: a
|
|
147
|
+
* step need not excuse itself with question material when it moves to new
|
|
148
|
+
* structure. Resolved by the reporter, declared by the transition — never
|
|
149
|
+
* inferred from its producer. */
|
|
150
|
+
readonly reaches?: boolean;
|
|
151
|
+
/** What the transition accounts for, when it declares it. A transition
|
|
152
|
+
* taken from a CLOSED state has nothing to progress on, so it is the one
|
|
153
|
+
* case that must say what it accounts for; a transition that carries the
|
|
154
|
+
* remainder declares nothing and the law's own witness is used. */
|
|
155
|
+
readonly explains?: ReadonlyArray<Span>;
|
|
156
|
+
/** The transition's own moves, in ladder units. */
|
|
157
|
+
readonly cost: number;
|
|
158
|
+
}
|
|
159
|
+
/** CLOSED — nothing of the asker's material is left unaccounted. */
|
|
160
|
+
export declare function closed(d: DerivationState): boolean;
|
|
161
|
+
/** THE LAW, evaluated once.
|
|
162
|
+
*
|
|
163
|
+
* Returns the spans the transition accounts for — the witness that lets it be
|
|
164
|
+
* taken — or `null` when it is inadmissible. Evaluating it once and advancing
|
|
165
|
+
* the state with {@link advance} is the whole of a transition; asking twice for
|
|
166
|
+
* the same pair would repeat the scan, which this module must not make anyone
|
|
167
|
+
* do.
|
|
168
|
+
*
|
|
169
|
+
* ¬FIXED ∧ CONTAINS ∧ ( CLOSED ∨ CARRIES ∨ MOVES )
|
|
170
|
+
*
|
|
171
|
+
* Cost is not a term: it is the lattice order the market minimises over, and
|
|
172
|
+
* neither is any budget — a cap decides with a number of work, this decides
|
|
173
|
+
* with the remainder. */
|
|
174
|
+
export declare function admissible(d: DerivationState, t: Continuation, query: Uint8Array, W: number): ReadonlyArray<Witness> | null;
|
|
175
|
+
export declare function advance(d: DerivationState, t: Continuation, explains: ReadonlyArray<Witness>): DerivationState;
|
|
176
|
+
/** What a layer offers the law: the next continuation of a state, or null when
|
|
177
|
+
* it has none. A layer OFFERS; the law disposes.
|
|
178
|
+
*
|
|
179
|
+
* ONE OFFER, AND IT IS THE LAYER'S LAST: {@link closure} stops when the law
|
|
180
|
+
* refuses what was offered, so a refusal is read as "no continuation exists".
|
|
181
|
+
* A layer must not offer candidates one at a time and expect the walk to
|
|
182
|
+
* continue after a refusal — its own fallbacks belong inside this function.
|
|
183
|
+
* The producers do exactly that: they choose between the forward absorb and the
|
|
184
|
+
* pivot before offering, and return null only when neither exists, which is why
|
|
185
|
+
* the one offer the law can still refuse (a pivot without ownership, whose
|
|
186
|
+
* material the answer does not carry) really is the last one. */
|
|
187
|
+
export type Offer = (d: DerivationState) => Promise<Continuation | null>;
|
|
188
|
+
/** THE CLOSURE — the walk of {@link advance} over the continuations `offer`
|
|
189
|
+
* proposes, run until the layer has nothing further to offer or the law refuses
|
|
190
|
+
* the one it offered.
|
|
191
|
+
*
|
|
192
|
+
* ADMISSION is entirely the law's; TERMINATION is the layer's, and deliberately
|
|
193
|
+
* so. The remainder does not descend (draining it was implemented and refuted
|
|
194
|
+
* — see the module note), so a walk cannot run forever only because the layer
|
|
195
|
+
* offering continuations keeps its own cycle protection over a finite graph.
|
|
196
|
+
* Nothing here counts steps, and nothing here decides admissibility. */
|
|
197
|
+
export declare function closure(d: DerivationState, query: Uint8Array, W: number, offer: Offer,
|
|
198
|
+
/** Called for each step the law admits, with the state before and after. */
|
|
199
|
+
onTaken?: (before: DerivationState, after: DerivationState) => void,
|
|
200
|
+
/** Called when the law refused the continuation the layer offered. */
|
|
201
|
+
onRefused?: (at: DerivationState) => void): Promise<DerivationState>;
|