@hviana/sema 0.1.3 → 0.1.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/dist/example/demo.js +25 -25
- package/dist/example/train_base.d.ts +21 -19
- package/dist/example/train_base.js +1952 -1684
- package/dist/src/alphabet.d.ts +3 -3
- package/dist/src/alphabet.js +27 -24
- package/dist/src/alu/src/alu.d.ts +194 -169
- package/dist/src/alu/src/alu.js +414 -374
- package/dist/src/alu/src/expr.d.ts +43 -36
- package/dist/src/alu/src/expr.js +278 -252
- package/dist/src/alu/src/index.d.ts +98 -9
- package/dist/src/alu/src/index.js +63 -9
- package/dist/src/alu/src/kernel-arith.d.ts +5 -1
- package/dist/src/alu/src/kernel-arith.js +285 -195
- package/dist/src/alu/src/kernel-bits.d.ts +5 -1
- package/dist/src/alu/src/kernel-bits.js +115 -81
- package/dist/src/alu/src/kernel-logic.js +63 -33
- package/dist/src/alu/src/kernel-nd.js +195 -155
- package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
- package/dist/src/alu/src/kernel-numeric.js +331 -259
- package/dist/src/alu/src/operation.d.ts +132 -112
- package/dist/src/alu/src/operation.js +163 -149
- package/dist/src/alu/src/parser.d.ts +175 -168
- package/dist/src/alu/src/parser.js +421 -382
- package/dist/src/alu/src/resonance.d.ts +25 -16
- package/dist/src/alu/src/resonance.js +56 -49
- package/dist/src/alu/src/text.d.ts +7 -3
- package/dist/src/alu/src/text.js +37 -31
- package/dist/src/alu/src/value.d.ts +14 -14
- package/dist/src/alu/src/value.js +160 -150
- package/dist/src/alu/test/alu.test.js +656 -543
- package/dist/src/bytes.d.ts +5 -1
- package/dist/src/bytes.js +40 -33
- package/dist/src/config.d.ts +102 -102
- package/dist/src/config.js +83 -83
- package/dist/src/derive/src/deduction.d.ts +62 -60
- package/dist/src/derive/src/deduction.js +109 -103
- package/dist/src/derive/src/index.d.ts +7 -1
- package/dist/src/derive/src/priority-queue.d.ts +8 -8
- package/dist/src/derive/src/priority-queue.js +61 -57
- package/dist/src/derive/src/rewrite.d.ts +18 -15
- package/dist/src/derive/src/rewrite.js +67 -59
- package/dist/src/derive/src/trie.d.ts +56 -56
- package/dist/src/derive/src/trie.js +179 -175
- package/dist/src/derive/test/derive.test.js +100 -96
- package/dist/src/extension.d.ts +16 -13
- package/dist/src/geometry.d.ts +42 -17
- package/dist/src/geometry.js +266 -235
- package/dist/src/index.d.ts +17 -2
- package/dist/src/index.js +7 -1
- package/dist/src/ingest-cache.d.ts +30 -25
- package/dist/src/ingest-cache.js +127 -108
- package/dist/src/mind/articulation.d.ts +5 -1
- package/dist/src/mind/articulation.js +112 -77
- package/dist/src/mind/attention.d.ts +108 -40
- package/dist/src/mind/attention.js +871 -749
- package/dist/src/mind/canonical.d.ts +19 -4
- package/dist/src/mind/canonical.js +31 -27
- package/dist/src/mind/graph-search.d.ts +225 -201
- package/dist/src/mind/graph-search.js +821 -742
- package/dist/src/mind/index.d.ts +10 -2
- package/dist/src/mind/junction.d.ts +58 -31
- package/dist/src/mind/junction.js +237 -172
- package/dist/src/mind/learning.d.ts +52 -16
- package/dist/src/mind/learning.js +165 -143
- package/dist/src/mind/match.d.ts +69 -20
- package/dist/src/mind/match.js +318 -259
- package/dist/src/mind/mechanisms/alu.js +16 -16
- package/dist/src/mind/mechanisms/cast.d.ts +13 -9
- package/dist/src/mind/mechanisms/cast.js +456 -363
- package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
- package/dist/src/mind/mechanisms/confluence.js +183 -152
- package/dist/src/mind/mechanisms/cover.d.ts +8 -2
- package/dist/src/mind/mechanisms/cover.js +210 -148
- package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
- package/dist/src/mind/mechanisms/extraction.js +288 -234
- package/dist/src/mind/mechanisms/recall.d.ts +10 -6
- package/dist/src/mind/mechanisms/recall.js +185 -126
- package/dist/src/mind/mind.d.ts +154 -129
- package/dist/src/mind/mind.js +288 -258
- package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
- package/dist/src/mind/pipeline-mechanism.js +172 -161
- package/dist/src/mind/pipeline.d.ts +14 -4
- package/dist/src/mind/pipeline.js +189 -125
- package/dist/src/mind/primitives.d.ts +32 -8
- package/dist/src/mind/primitives.js +117 -99
- package/dist/src/mind/rationale.d.ts +98 -86
- package/dist/src/mind/rationale.js +121 -113
- package/dist/src/mind/reasoning.d.ts +13 -2
- package/dist/src/mind/reasoning.js +166 -129
- package/dist/src/mind/recognition.d.ts +4 -1
- package/dist/src/mind/recognition.js +208 -183
- package/dist/src/mind/resonance.d.ts +22 -5
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +25 -6
- package/dist/src/mind/trace.js +58 -50
- package/dist/src/mind/traverse.d.ts +58 -16
- package/dist/src/mind/traverse.js +357 -304
- package/dist/src/mind/types.d.ts +127 -120
- package/dist/src/mind/types.js +69 -60
- package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
- package/dist/src/rabitq-hnsw/src/database.js +359 -342
- package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
- package/dist/src/rabitq-hnsw/src/heap.js +80 -75
- package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
- package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
- package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
- package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
- package/dist/src/rabitq-hnsw/src/prng.js +27 -25
- package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
- package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
- package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
- package/dist/src/rabitq-hnsw/src/store.js +843 -752
- package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
- package/dist/src/sema.d.ts +35 -0
- package/dist/src/sema.js +77 -0
- package/dist/src/store-sqlite.d.ts +166 -144
- package/dist/src/store-sqlite.js +660 -578
- package/dist/src/store.d.ts +630 -593
- package/dist/src/store.js +1581 -1440
- package/dist/src/vec.d.ts +9 -5
- package/dist/src/vec.js +73 -61
- package/package.json +2 -2
- package/src/sema.ts +102 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// juxtapose.
|
|
13
13
|
import { read } from "../primitives.js";
|
|
14
14
|
import { argmaxBy, hubBound } from "../traverse.js";
|
|
15
|
-
import { analogyStrength, follow, project, reverseContext
|
|
15
|
+
import { analogyStrength, follow, project, reverseContext } from "../match.js";
|
|
16
16
|
import { joinWithBridge } from "../resonance.js";
|
|
17
17
|
import { CONCEPT, STEP } from "../graph-search.js";
|
|
18
18
|
import { concat2, indexOf } from "../../bytes.js";
|
|
@@ -70,378 +70,471 @@ const MIN_WEAVE = 2;
|
|
|
70
70
|
*
|
|
71
71
|
* Returns the array of {@link CastResult}s that fired (possibly empty). */
|
|
72
72
|
export async function counterfactualTransfer(ctx, query, pre) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const quantum = ctx.space.maxGroup;
|
|
74
|
+
if (query.length < 2 * quantum || ctx.store.edgeSourceCount() === 0) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const { roots, ranked } = await pre.attention();
|
|
78
|
+
if (ranked.length < 2) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const weave = await pre.weave();
|
|
82
|
+
const points = weave.points;
|
|
83
|
+
const depth = weave.depth;
|
|
84
|
+
const aligned = points.length;
|
|
85
|
+
if (aligned < 2) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
// ── Frame gate (half-dominance, weave-local) ─────────────────────────
|
|
89
|
+
// A byte is FRAME when more than MIN_WEAVE aligned structures cover it
|
|
90
|
+
// AND those structures are a majority of all aligned structures.
|
|
91
|
+
// Per-byte: frame(i) ⇔ depth[i] > MIN_WEAVE ∧ dominates(depth[i], aligned)
|
|
92
|
+
// Per-run: usable(r) ⇔ ¬dominates(framedCount, runLen)
|
|
93
|
+
const isFrame = (i) => depth[i] > MIN_WEAVE && dominates(depth[i], aligned);
|
|
94
|
+
const framedCount = (qs, qe) => {
|
|
95
|
+
let n = 0;
|
|
96
|
+
for (let i = qs; i < qe; i++) {
|
|
97
|
+
if (isFrame(i)) {
|
|
98
|
+
n++;
|
|
99
|
+
}
|
|
76
100
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (isFrame(i))
|
|
96
|
-
n++;
|
|
97
|
-
return n;
|
|
98
|
-
};
|
|
99
|
-
const usable = (qs, qe) => !dominates(framedCount(qs, qe), qe - qs);
|
|
100
|
-
// The weave's DOMINANT is its principal STRUCTURE — the aligned point
|
|
101
|
-
// explaining the most query bytes — not the climb's top-ranked TOPIC.
|
|
102
|
-
// The two used to coincide (approximate votes from a query's novel spans
|
|
103
|
-
// boosted whichever exemplar shared its frame), but the contrastive
|
|
104
|
-
// margin ranks the query's own exact site first, and CAST's schemas all
|
|
105
|
-
// orient around the frame-bearing structure: the substitution/redirection
|
|
106
|
-
// seat is displaced IN the dominant, and comparison seats the analogs by
|
|
107
|
-
// the contexts that establish their roles. Coverage is weave-local and
|
|
108
|
-
// derived (sum of aligned run lengths); ties keep the ranked order.
|
|
109
|
-
let dominant = points[0];
|
|
110
|
-
let domCover = -1;
|
|
111
|
-
for (const p of points) {
|
|
112
|
-
let cover = 0;
|
|
113
|
-
for (const r of p.runs)
|
|
114
|
-
cover += r.qe - r.qs;
|
|
115
|
-
if (cover > domCover) {
|
|
116
|
-
domCover = cover;
|
|
117
|
-
dominant = p;
|
|
118
|
-
}
|
|
101
|
+
return n;
|
|
102
|
+
};
|
|
103
|
+
const usable = (qs, qe) => !dominates(framedCount(qs, qe), qe - qs);
|
|
104
|
+
// The weave's DOMINANT is its principal STRUCTURE — the aligned point
|
|
105
|
+
// explaining the most query bytes — not the climb's top-ranked TOPIC.
|
|
106
|
+
// The two used to coincide (approximate votes from a query's novel spans
|
|
107
|
+
// boosted whichever exemplar shared its frame), but the contrastive
|
|
108
|
+
// margin ranks the query's own exact site first, and CAST's schemas all
|
|
109
|
+
// orient around the frame-bearing structure: the substitution/redirection
|
|
110
|
+
// seat is displaced IN the dominant, and comparison seats the analogs by
|
|
111
|
+
// the contexts that establish their roles. Coverage is weave-local and
|
|
112
|
+
// derived (sum of aligned run lengths); ties keep the ranked order.
|
|
113
|
+
let dominant = points[0];
|
|
114
|
+
let domCover = -1;
|
|
115
|
+
for (const p of points) {
|
|
116
|
+
let cover = 0;
|
|
117
|
+
for (const r of p.runs) {
|
|
118
|
+
cover += r.qe - r.qs;
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
120
|
+
if (cover > domCover) {
|
|
121
|
+
domCover = cover;
|
|
122
|
+
dominant = p;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const isRoot = (id) => roots.some((r) => r.anchor === id);
|
|
126
|
+
// The weave must touch a COMMITTED point of attention: the dominant
|
|
127
|
+
// structure itself, or another aligned point the climb committed to.
|
|
128
|
+
if (!points.some((p) => isRoot(p.anchor))) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const woven = points.some((p) =>
|
|
132
|
+
p.runs.some((r) =>
|
|
133
|
+
!pre.rec.sites.some((s) => r.qs >= s.start && r.qe <= s.end)
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
if (!woven) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
const t = ctx.trace?.enter("counterfactual", [
|
|
140
|
+
rItem(query, "query"),
|
|
141
|
+
]);
|
|
142
|
+
// Each schema tried below RECORDS its candidate (when it fires) rather than
|
|
143
|
+
// returning immediately — every schema that succeeds contributes its own
|
|
144
|
+
// candidate, and the grounding decider's own weight comparison (not CAST's
|
|
145
|
+
// former internal priority) picks among them.
|
|
146
|
+
//
|
|
147
|
+
// `accounted` is SCHEMA-SPECIFIC, not the whole weave's alignment: a
|
|
148
|
+
// schema only actually TRANSFERS BETWEEN the two points its own logic
|
|
149
|
+
// names (substitution: the filled subject + the displaced seat;
|
|
150
|
+
// redirection: the displaced seat + the named substitute; comparison:
|
|
151
|
+
// the dominant + its analog) — a THIRD point the weave happened to align
|
|
152
|
+
// but this schema never touched contributes nothing to what THIS answer
|
|
153
|
+
// explains. Pricing every schema against the SAME "every kept point's
|
|
154
|
+
// every run" span would let the cheapest schema win on move-cost alone
|
|
155
|
+
// regardless of which one actually used more of the query; pricing it
|
|
156
|
+
// against only a fragment of even its OWN two points (e.g. one run
|
|
157
|
+
// instead of the point's full aligned evidence) is just as wrong the
|
|
158
|
+
// other way — it starves an otherwise-correct schema of credit for
|
|
159
|
+
// evidence it legitimately relied on. Each call site below passes the
|
|
160
|
+
// full run set of exactly the points ITS OWN transfer used — no more,
|
|
161
|
+
// no less.
|
|
162
|
+
const runSpans = (p) => p.runs.map((r) => [r.qs, r.qe]);
|
|
163
|
+
const results = [];
|
|
164
|
+
const record = (answer, note, used, moves, accounted) => {
|
|
165
|
+
if (answer === null) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
ctx.trace?.step("castSchema", [rItem(query, "query")], [
|
|
169
|
+
rItem(answer, "answer"),
|
|
170
|
+
], note);
|
|
171
|
+
results.push({
|
|
172
|
+
bytes: answer,
|
|
173
|
+
used: used ?? new Set(),
|
|
174
|
+
accounted,
|
|
175
|
+
moves,
|
|
176
|
+
unexplained: unexplainedLabel(query, accounted),
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
ctx.trace?.step(
|
|
180
|
+
"alignStructures",
|
|
181
|
+
[rItem(query, "query")],
|
|
182
|
+
points.map((p) => rNode(ctx, p.anchor, "structure", p.vote)),
|
|
183
|
+
"the independent learnt structures the query weaves, by graded alignment",
|
|
184
|
+
);
|
|
185
|
+
const lastRun = (p) => p.runs[p.runs.length - 1];
|
|
186
|
+
const qv = pre.guide;
|
|
187
|
+
// ── SUBSTITUTION ──────────────────────────────────────────────────
|
|
188
|
+
const fillerOf = (s) => {
|
|
189
|
+
const r = s.runs[0];
|
|
190
|
+
return r.cs < quantum
|
|
191
|
+
? s.ctx.subarray(0, r.cs + (r.qe - r.qs))
|
|
192
|
+
: query.subarray(r.qs, r.qe);
|
|
193
|
+
};
|
|
194
|
+
const beforeOf = (p, r) =>
|
|
195
|
+
argmaxBy(
|
|
196
|
+
points.filter((s) =>
|
|
197
|
+
s !== p && lastRun(s).qe <= r.qs &&
|
|
176
198
|
s.runs[0].cs < quantum &&
|
|
177
|
-
usable(s.runs[0].qs, s.runs[0].qe)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
usable(s.runs[0].qs, s.runs[0].qe)
|
|
200
|
+
),
|
|
201
|
+
(s) => lastRun(s).qs,
|
|
202
|
+
-Infinity,
|
|
203
|
+
true,
|
|
204
|
+
)?.item;
|
|
205
|
+
const displacement = points
|
|
206
|
+
.map((p) => {
|
|
207
|
+
const r = p.runs[0];
|
|
208
|
+
if (r.cs < quantum || !usable(r.qs, r.qe)) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const before = beforeOf(p, r);
|
|
212
|
+
if (before === undefined) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
if (r.cs > fillerOf(before).length + quantum) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return { p, before, depth: p.ctx.length - r.cs };
|
|
190
219
|
})
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
.filter((c) => c !== null);
|
|
221
|
+
const picked = argmaxBy(displacement, (c) => c.depth, -Infinity, true);
|
|
222
|
+
const proj = picked?.item.p ?? null;
|
|
223
|
+
const subj = picked?.item.before ?? null;
|
|
224
|
+
if (proj !== null && subj !== null) {
|
|
225
|
+
const seat = proj.runs[0];
|
|
226
|
+
const filler = fillerOf(subj);
|
|
227
|
+
const tail = proj.ctx.subarray(seat.cs);
|
|
228
|
+
let answer = await joinWithBridge(ctx, filler, tail);
|
|
229
|
+
const fwd = await follow(ctx, proj.anchor, qv);
|
|
230
|
+
if (fwd !== null && indexOf(answer, fwd, 0) < 0) {
|
|
231
|
+
answer = concat2(answer, fwd);
|
|
232
|
+
}
|
|
233
|
+
ctx.trace?.step(
|
|
234
|
+
"projectCounterfactual",
|
|
235
|
+
[
|
|
236
|
+
rItem(filler, "filler", subj.anchor),
|
|
237
|
+
rNode(ctx, proj.anchor, "displaced-structure"),
|
|
238
|
+
],
|
|
239
|
+
[rItem(answer, "projection")],
|
|
240
|
+
"transfer the displaced structure onto the subject filler (seat substitution)",
|
|
241
|
+
);
|
|
242
|
+
record(
|
|
243
|
+
answer,
|
|
244
|
+
"counterfactual substitution — the subject fills the analog's seat",
|
|
245
|
+
new Set([subj.anchor, proj.anchor]),
|
|
246
|
+
// The acts performed: one seat INSERT projection + one edge FOLLOW.
|
|
247
|
+
STEP + STEP,
|
|
248
|
+
// What substitution actually READ: the two points it transfers
|
|
249
|
+
// between — the subject filling the seat, and the displaced
|
|
250
|
+
// structure whose seat it fills — not every OTHER point the weave
|
|
251
|
+
// happened to align (a third, unrelated point in the same weave
|
|
252
|
+
// contributes nothing to what substitution itself explains).
|
|
253
|
+
[...runSpans(subj), ...runSpans(proj)],
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
// ── REDIRECTION ────────────────────────────────────────────────────
|
|
257
|
+
const last = points.reduce((a, b) => lastRun(b).qs > lastRun(a).qs ? b : a);
|
|
258
|
+
// Displacement test, capped at the hub bound: a hub anchor can carry a
|
|
259
|
+
// corpus-sized fan-out, and each continuation costs a full byte
|
|
260
|
+
// reconstruction plus an O(|query|·|bytes|) scan. The first √N edges (the
|
|
261
|
+
// same insertion-order convention chooseNext caps by) decide; past a hub's
|
|
262
|
+
// cap the test reads "none of the established continuations appears".
|
|
263
|
+
const domNext = ctx.store.nextFirst(dominant.anchor, hubBound(ctx));
|
|
264
|
+
const displaced = domNext
|
|
265
|
+
.every((n) => indexOf(query, read(ctx, n), 0) < 0);
|
|
266
|
+
if (
|
|
267
|
+
last !== dominant &&
|
|
268
|
+
last.runs[0].cs === 0 && displaced &&
|
|
269
|
+
usable(last.runs[0].qs, last.runs[0].qe)
|
|
270
|
+
) {
|
|
271
|
+
const g = await project(ctx, last.anchor, qv);
|
|
272
|
+
if (g !== null) {
|
|
273
|
+
ctx.trace?.step(
|
|
274
|
+
"projectCounterfactual",
|
|
275
|
+
[
|
|
276
|
+
rNode(ctx, dominant.anchor, "displaced-structure"),
|
|
277
|
+
rNode(ctx, last.anchor, "substitute"),
|
|
278
|
+
],
|
|
279
|
+
[rItem(g, "projection")],
|
|
280
|
+
"the substitute's own fact replaces the displaced structure's answer",
|
|
281
|
+
);
|
|
282
|
+
record(
|
|
283
|
+
g,
|
|
284
|
+
"counterfactual redirection — the named substitute's fact is followed",
|
|
285
|
+
new Set([dominant.anchor, last.anchor]),
|
|
286
|
+
// One forward projection across the substitute's own fact.
|
|
287
|
+
STEP,
|
|
288
|
+
// What redirection READ: the displaced structure's own recognized
|
|
289
|
+
// seat (still explained — this schema RECOGNIZES it as the slot
|
|
290
|
+
// being overridden, it just doesn't answer from it) plus the named
|
|
291
|
+
// substitute's own aligned run — not every OTHER point the weave
|
|
292
|
+
// happened to align.
|
|
293
|
+
[...runSpans(dominant), ...runSpans(last)],
|
|
294
|
+
);
|
|
217
295
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// being overridden, it just doesn't answer from it) plus the named
|
|
243
|
-
// substitute's own aligned run — not every OTHER point the weave
|
|
244
|
-
// happened to align.
|
|
245
|
-
[...runSpans(dominant), ...runSpans(last)]);
|
|
246
|
-
}
|
|
296
|
+
}
|
|
297
|
+
// ── COMPARISON ─────────────────────────────────────────────────────
|
|
298
|
+
// Collect every qualifying non-dominant point as a candidate analog.
|
|
299
|
+
// When a point's own anchor is structurally at the wrong level
|
|
300
|
+
// (e.g. a long exemplar sentence whose halo does not resemble the
|
|
301
|
+
// dominant's), its nextOf targets often point to the right level — the
|
|
302
|
+
// person / concept the exemplar is about. Trying both prevents a
|
|
303
|
+
// seed-dependent failure where the climb ranks an exemplar above a
|
|
304
|
+
// person node and the person node is excluded from points by run-
|
|
305
|
+
// overlap trimming.
|
|
306
|
+
// The seat that establishes a candidate's role: the REVERSE projection
|
|
307
|
+
// (the context it follows), voiced by the query gist — falling back to the
|
|
308
|
+
// candidate's own bytes when it follows nothing. DELIBERATE STRENGTHENING
|
|
309
|
+
// over the pre-refactor code: reverseContext also returns null when the
|
|
310
|
+
// picked context READS EMPTY (a dangling id degrades to zero bytes), so a
|
|
311
|
+
// corrupted-store read now falls back here instead of voicing a hollow
|
|
312
|
+
// seat into the comparison — the same "empty bytes are no grounding"
|
|
313
|
+
// invariant project() has always enforced.
|
|
314
|
+
const seatOfNode = (id, fallback) => reverseContext(ctx, id, qv) ?? fallback;
|
|
315
|
+
const seatOf = (p) => seatOfNode(p.anchor, p.ctx);
|
|
316
|
+
const analogs = [];
|
|
317
|
+
for (const p of points) {
|
|
318
|
+
if (p === dominant) {
|
|
319
|
+
continue;
|
|
247
320
|
}
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// The seat that establishes a candidate's role: the REVERSE projection
|
|
258
|
-
// (the context it follows), voiced by the query gist — falling back to the
|
|
259
|
-
// candidate's own bytes when it follows nothing. DELIBERATE STRENGTHENING
|
|
260
|
-
// over the pre-refactor code: reverseContext also returns null when the
|
|
261
|
-
// picked context READS EMPTY (a dangling id degrades to zero bytes), so a
|
|
262
|
-
// corrupted-store read now falls back here instead of voicing a hollow
|
|
263
|
-
// seat into the comparison — the same "empty bytes are no grounding"
|
|
264
|
-
// invariant project() has always enforced.
|
|
265
|
-
const seatOfNode = (id, fallback) => reverseContext(ctx, id, qv) ?? fallback;
|
|
266
|
-
const seatOf = (p) => seatOfNode(p.anchor, p.ctx);
|
|
267
|
-
const analogs = [];
|
|
268
|
-
for (const p of points) {
|
|
269
|
-
if (p === dominant)
|
|
270
|
-
continue;
|
|
271
|
-
// Push the point's own anchor only when its context fits within
|
|
272
|
-
// the query (the seat sentence must not dominate the comparison).
|
|
273
|
-
if (p.ctx.length <= query.length &&
|
|
274
|
-
indexOf(dominant.ctx, p.ctx, 0) < 0 &&
|
|
275
|
-
indexOf(p.ctx, dominant.ctx, 0) < 0 &&
|
|
276
|
-
indexOf(query, p.ctx, 0) < 0) {
|
|
277
|
-
analogs.push({ anchor: p.anchor, point: p });
|
|
278
|
-
}
|
|
279
|
-
// Reach through to the point's continuation targets regardless
|
|
280
|
-
// of the point's own context length: when the point is a leaf
|
|
281
|
-
// (exemplar sentence), its nextOf is the hub (person / concept)
|
|
282
|
-
// that makes a genuine cross-domain analog, and the hub's own
|
|
283
|
-
// (shorter) context will be the seat.
|
|
284
|
-
// Capped like every fan-out: a hub anchor's full continuation list is
|
|
285
|
-
// corpus-sized, and each candidate costs a read plus O(|query|·|bytes|)
|
|
286
|
-
// scans — only the first √N (insertion order, the same convention
|
|
287
|
-
// chooseNext caps by) are reachable as analogs.
|
|
288
|
-
for (const nid of ctx.store.nextFirst(p.anchor, hubBound(ctx))) {
|
|
289
|
-
const nctx = read(ctx, nid);
|
|
290
|
-
if (nctx.length > query.length ||
|
|
291
|
-
indexOf(dominant.ctx, nctx, 0) >= 0 ||
|
|
292
|
-
indexOf(nctx, dominant.ctx, 0) >= 0 ||
|
|
293
|
-
indexOf(query, nctx, 0) >= 0)
|
|
294
|
-
continue;
|
|
295
|
-
analogs.push({ anchor: nid, point: null });
|
|
296
|
-
}
|
|
321
|
+
// Push the point's own anchor only when its context fits within
|
|
322
|
+
// the query (the seat sentence must not dominate the comparison).
|
|
323
|
+
if (
|
|
324
|
+
p.ctx.length <= query.length &&
|
|
325
|
+
indexOf(dominant.ctx, p.ctx, 0) < 0 &&
|
|
326
|
+
indexOf(p.ctx, dominant.ctx, 0) < 0 &&
|
|
327
|
+
indexOf(query, p.ctx, 0) < 0
|
|
328
|
+
) {
|
|
329
|
+
analogs.push({ anchor: p.anchor, point: p });
|
|
297
330
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
331
|
+
// Reach through to the point's continuation targets regardless
|
|
332
|
+
// of the point's own context length: when the point is a leaf
|
|
333
|
+
// (exemplar sentence), its nextOf is the hub (person / concept)
|
|
334
|
+
// that makes a genuine cross-domain analog, and the hub's own
|
|
335
|
+
// (shorter) context will be the seat.
|
|
336
|
+
// Capped like every fan-out: a hub anchor's full continuation list is
|
|
337
|
+
// corpus-sized, and each candidate costs a read plus O(|query|·|bytes|)
|
|
338
|
+
// scans — only the first √N (insertion order, the same convention
|
|
339
|
+
// chooseNext caps by) are reachable as analogs.
|
|
340
|
+
for (const nid of ctx.store.nextFirst(p.anchor, hubBound(ctx))) {
|
|
341
|
+
const nctx = read(ctx, nid);
|
|
342
|
+
if (
|
|
343
|
+
nctx.length > query.length ||
|
|
344
|
+
indexOf(dominant.ctx, nctx, 0) >= 0 ||
|
|
345
|
+
indexOf(nctx, dominant.ctx, 0) >= 0 ||
|
|
346
|
+
indexOf(query, nctx, 0) >= 0
|
|
347
|
+
) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
analogs.push({ anchor: nid, point: null });
|
|
310
351
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// is a property of the corpus, not of the seed — but note ids are SIGNED:
|
|
329
|
-
// byte leaves occupy −256…−1, so "lowest id" is creation order only among
|
|
330
|
-
// multi-byte nodes and byte-value order among leaves. Either way it is
|
|
331
|
-
// deterministic, which is all the final tie-break must be.
|
|
332
|
-
if (bestAnalog === null && analogs.length > 0) {
|
|
333
|
-
let hubSupport = -1;
|
|
334
|
-
let hubMass = -1;
|
|
335
|
-
const fanClamp = hubBound(ctx) + 1;
|
|
336
|
-
for (const c of analogs) {
|
|
337
|
-
// Evidence clamped at the hub bound: beyond √N + 1 the exact fan-out
|
|
338
|
-
// no longer discriminates (every mega-hub ties at the clamp), and
|
|
339
|
-
// counting it exactly would require the corpus-sized read.
|
|
340
|
-
const fanOut = ctx.store.nextFirst(c.anchor, fanClamp).length;
|
|
341
|
-
if (fanOut === 0)
|
|
342
|
-
continue;
|
|
343
|
-
const support = ctx.store.prevCount(c.anchor);
|
|
344
|
-
if (support === 0)
|
|
345
|
-
continue;
|
|
346
|
-
const total = support + fanOut;
|
|
347
|
-
if (total < hubSupport)
|
|
348
|
-
continue;
|
|
349
|
-
const mass = ctx.store.haloMass(c.anchor);
|
|
350
|
-
if (total > hubSupport ||
|
|
351
|
-
mass > hubMass ||
|
|
352
|
-
(mass === hubMass && bestAnalog !== null &&
|
|
353
|
-
c.anchor < bestAnalog.anchor)) {
|
|
354
|
-
hubSupport = total;
|
|
355
|
-
hubMass = mass;
|
|
356
|
-
bestAnalog = c;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (bestAnalog !== null) {
|
|
360
|
-
ctx.trace?.step("tryAnalog", [], [rNode(ctx, bestAnalog.anchor, "fallback", hubSupport)], "no candidate passed the similarity gates — using the best-supported structural hub");
|
|
361
|
-
}
|
|
352
|
+
}
|
|
353
|
+
let bestAnalog = null;
|
|
354
|
+
let bestSim = 0;
|
|
355
|
+
for (const c of analogs) {
|
|
356
|
+
const sim = await analogyStrength(ctx, dominant.anchor, c.anchor);
|
|
357
|
+
ctx.trace?.step(
|
|
358
|
+
"tryAnalog",
|
|
359
|
+
[
|
|
360
|
+
rNode(ctx, dominant.anchor, "dominant"),
|
|
361
|
+
rNode(ctx, c.anchor, "candidate", sim),
|
|
362
|
+
],
|
|
363
|
+
[],
|
|
364
|
+
`analogy strength ${sim.toFixed(4)}`,
|
|
365
|
+
);
|
|
366
|
+
if (sim > bestSim) {
|
|
367
|
+
bestSim = sim;
|
|
368
|
+
bestAnalog = c;
|
|
362
369
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
370
|
+
}
|
|
371
|
+
// When every candidate fails the similarity gates (halo company — now
|
|
372
|
+
// deterministic signatures, see sema.ts — and the shared-frame tier),
|
|
373
|
+
// fall back to a candidate that is a genuine structural hub (edges in
|
|
374
|
+
// BOTH directions). A hub node — a person, concept, or category — is
|
|
375
|
+
// the kind of thing that makes sense to compare across domains. A leaf
|
|
376
|
+
// value (extracted span, terminal answer) has edges in at most one
|
|
377
|
+
// direction and comparing it would preempt the extraction pipeline,
|
|
378
|
+
// which is the right mechanism for those. A fallback comparison carries
|
|
379
|
+
// NO similarity evidence — it stays honest only because the grounding
|
|
380
|
+
// decider weighs it against mechanisms that explain more of the query
|
|
381
|
+
// (extraction accounts its whole located envelope; see extraction.ts).
|
|
382
|
+
//
|
|
383
|
+
// WHICH hub: not the first in `analogs` order — that order flows from the
|
|
384
|
+
// vote ranking, which flows from approximate resonance, which is seed-
|
|
385
|
+
// dependent. Pick by evidence instead: combined edge support (prevCount +
|
|
386
|
+
// fan-out), tie-broken by poured halo MASS (episode corroboration — the
|
|
387
|
+
// direct distributional evidence), then by LOWEST node id. The id order
|
|
388
|
+
// is a property of the corpus, not of the seed — but note ids are SIGNED:
|
|
389
|
+
// byte leaves occupy −256…−1, so "lowest id" is creation order only among
|
|
390
|
+
// multi-byte nodes and byte-value order among leaves. Either way it is
|
|
391
|
+
// deterministic, which is all the final tie-break must be.
|
|
392
|
+
if (bestAnalog === null && analogs.length > 0) {
|
|
393
|
+
let hubSupport = -1;
|
|
394
|
+
let hubMass = -1;
|
|
395
|
+
const fanClamp = hubBound(ctx) + 1;
|
|
396
|
+
for (const c of analogs) {
|
|
397
|
+
// Evidence clamped at the hub bound: beyond √N + 1 the exact fan-out
|
|
398
|
+
// no longer discriminates (every mega-hub ties at the clamp), and
|
|
399
|
+
// counting it exactly would require the corpus-sized read.
|
|
400
|
+
const fanOut = ctx.store.nextFirst(c.anchor, fanClamp).length;
|
|
401
|
+
if (fanOut === 0) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const support = ctx.store.prevCount(c.anchor);
|
|
405
|
+
if (support === 0) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const total = support + fanOut;
|
|
409
|
+
if (total < hubSupport) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const mass = ctx.store.haloMass(c.anchor);
|
|
413
|
+
if (
|
|
414
|
+
total > hubSupport ||
|
|
415
|
+
mass > hubMass ||
|
|
416
|
+
(mass === hubMass && bestAnalog !== null &&
|
|
417
|
+
c.anchor < bestAnalog.anchor)
|
|
418
|
+
) {
|
|
419
|
+
hubSupport = total;
|
|
420
|
+
hubMass = mass;
|
|
421
|
+
bestAnalog = c;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (bestAnalog !== null) {
|
|
425
|
+
ctx.trace?.step(
|
|
426
|
+
"tryAnalog",
|
|
427
|
+
[],
|
|
428
|
+
[rNode(ctx, bestAnalog.anchor, "fallback", hubSupport)],
|
|
429
|
+
"no candidate passed the similarity gates — using the best-supported structural hub",
|
|
430
|
+
);
|
|
401
431
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
432
|
+
}
|
|
433
|
+
ctx.trace?.step(
|
|
434
|
+
"tryAnalog",
|
|
435
|
+
[],
|
|
436
|
+
bestAnalog !== null ? [rNode(ctx, bestAnalog.anchor, "best", bestSim)] : [],
|
|
437
|
+
bestAnalog !== null
|
|
438
|
+
? `best analog with strength ${bestSim.toFixed(4)}`
|
|
439
|
+
: `no analog candidate passed (${analogs.length} checked)`,
|
|
440
|
+
);
|
|
441
|
+
// COMPARISON gate — analogical comparison seats the dominant against ONE
|
|
442
|
+
// analog, so it presupposes the query is ABOUT a single thing. When the
|
|
443
|
+
// consensus climb instead committed to MULTIPLE independent points of
|
|
444
|
+
// attention (`roots.length > 1`), the query names independent topics to
|
|
445
|
+
// FUSE — the reasoner's fuseAttention already combines them — not analogs
|
|
446
|
+
// to compare. Firing here would juxtapose two co-scaffolded but unrelated
|
|
447
|
+
// records (each sharing only the corpus preamble), out-accounting the
|
|
448
|
+
// honest thin multi-root grounding with a frame echo. Derived from the
|
|
449
|
+
// climb's own forest, never tuned; substitution/redirection stay
|
|
450
|
+
// unaffected — they orient around a displaced seat, not a whole-topic
|
|
451
|
+
// analogy.
|
|
452
|
+
if (
|
|
453
|
+
bestAnalog !== null &&
|
|
454
|
+
dominant.ctx.length <= query.length &&
|
|
455
|
+
roots.length <= 1
|
|
456
|
+
) {
|
|
457
|
+
ctx.trace?.step(
|
|
458
|
+
"validateAnalogy",
|
|
459
|
+
[
|
|
460
|
+
rNode(ctx, dominant.anchor, "analog", bestSim),
|
|
461
|
+
rNode(ctx, bestAnalog.anchor, "analog", bestSim),
|
|
462
|
+
],
|
|
463
|
+
[],
|
|
464
|
+
"the two structures keep distributional company beyond chance — genuine analogs",
|
|
465
|
+
);
|
|
466
|
+
const a = seatOf(dominant);
|
|
467
|
+
const b = bestAnalog.point !== null
|
|
468
|
+
? seatOf(bestAnalog.point)
|
|
469
|
+
: seatOfNode(bestAnalog.anchor, read(ctx, bestAnalog.anchor));
|
|
470
|
+
const answer = await joinWithBridge(ctx, a, b);
|
|
471
|
+
record(
|
|
472
|
+
answer,
|
|
473
|
+
"analogical comparison — each analog voiced by the context that establishes its role",
|
|
474
|
+
new Set([dominant.anchor, bestAnalog.anchor]),
|
|
475
|
+
// A halo-mediated act (the analogy gate) plus two seat projections.
|
|
476
|
+
CONCEPT + STEP + STEP,
|
|
477
|
+
// What comparison READ: the dominant's own aligned runs, plus the
|
|
478
|
+
// analog's aligned runs when it was itself an aligned point (a nextOf
|
|
479
|
+
// descendant was never aligned to the query directly, so it
|
|
480
|
+
// contributes no accounted span — its seat is graph-reached, not
|
|
481
|
+
// query-matched).
|
|
482
|
+
[
|
|
483
|
+
...runSpans(dominant),
|
|
484
|
+
...(bestAnalog.point !== null ? runSpans(bestAnalog.point) : []),
|
|
485
|
+
],
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
t?.done(
|
|
489
|
+
results.map((r) => rItem(r.bytes, "answer")),
|
|
490
|
+
results.length > 0
|
|
491
|
+
? `${results.length} counterfactual schema(s) fired — the grounding decider weighs them`
|
|
492
|
+
: "no counterfactual weave — the ordinary pipeline decides",
|
|
493
|
+
);
|
|
494
|
+
return results;
|
|
406
495
|
}
|
|
407
496
|
export const castMechanism = {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
497
|
+
name: "cast",
|
|
498
|
+
provenance: "cast",
|
|
499
|
+
async floor(_ctx, query, pre, worthRunning) {
|
|
500
|
+
const W = _ctx.space.maxGroup;
|
|
501
|
+
// Cheap checks first — no pre-computation needed.
|
|
502
|
+
if (query.length < 2 * W || _ctx.store.edgeSourceCount() === 0) {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
// CAST's floor, when it exists, is ALWAYS exactly 2*STEP — the climb and
|
|
506
|
+
// the weave only decide whether it exists (2*STEP) or not (null), they
|
|
507
|
+
// never tighten the number itself. So if 2*STEP already can't beat
|
|
508
|
+
// whatever incumbent has already won this response (cover runs first —
|
|
509
|
+
// see defaultMechanisms), no analysis can change the outcome: RETURN THE
|
|
510
|
+
// BOUND uninvested (still admissible) and let the pipeline's own check
|
|
511
|
+
// prune run() with the truthful "cannot beat incumbent" note. This is
|
|
512
|
+
// the SAME admissible-floor economy worthRunning applies to run(),
|
|
513
|
+
// applied to floor()'s own investment — uniformly, whatever mechanism
|
|
514
|
+
// supplied the incumbent (an extension's computed result is not
|
|
515
|
+
// special-cased; any sufficiently cheap incumbent prunes the same way).
|
|
516
|
+
if (!worthRunning(2 * STEP)) {
|
|
517
|
+
return 2 * STEP;
|
|
518
|
+
}
|
|
519
|
+
// Now first-touch the shared analyses (climb, then the weave built on
|
|
520
|
+
// it). If another mechanism already triggered either, this awaits the
|
|
521
|
+
// cached result; otherwise it's computed once here and reused in run().
|
|
522
|
+
if ((await pre.attention()).ranked.length < 2) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
if ((await pre.weave()).points.length < 2) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
return 2 * STEP;
|
|
529
|
+
},
|
|
530
|
+
async run(ctx, query, pre) {
|
|
531
|
+
const casts = await counterfactualTransfer(ctx, query, pre);
|
|
532
|
+
return casts.map((c) => ({
|
|
533
|
+
bytes: c.bytes,
|
|
534
|
+
accounted: c.accounted,
|
|
535
|
+
moves: c.moves,
|
|
536
|
+
used: c.used,
|
|
537
|
+
unexplained: c.unexplained,
|
|
538
|
+
}));
|
|
539
|
+
},
|
|
447
540
|
};
|