@hviana/sema 0.9.1 → 0.9.2
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/src/meter.d.ts +13 -0
- package/dist/src/meter.js +13 -0
- package/dist/src/mind/evidence.d.ts +24 -0
- package/dist/src/mind/evidence.js +90 -0
- package/dist/src/mind/mechanisms/cover.js +41 -3
- package/dist/src/mind/mechanisms/recall.js +10 -2
- package/dist/src/mind/mind.d.ts +6 -0
- package/dist/src/mind/mind.js +3 -0
- package/dist/src/mind/pipeline.js +46 -4
- package/dist/src/mind/reasoning.js +43 -8
- package/dist/src/mind/traverse.d.ts +42 -2
- package/dist/src/mind/traverse.js +282 -5
- package/dist/src/mind/types.d.ts +9 -0
- package/docs/INDEX.md +18 -17
- package/docs/INVARIANTS.md +1 -0
- package/docs/architecture/evidence.md +113 -0
- package/docs/architecture/exact-vs-approximate.md +10 -9
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/meter.ts +13 -0
- package/src/mind/evidence.ts +107 -0
- package/src/mind/mechanisms/cover.ts +58 -3
- package/src/mind/mechanisms/recall.ts +16 -2
- package/src/mind/mind.ts +4 -0
- package/src/mind/pipeline.ts +59 -4
- package/src/mind/reasoning.ts +54 -7
- package/src/mind/traverse.ts +318 -5
- package/src/mind/types.ts +6 -0
- package/test/154-the-question-names-the-step.test.mjs +281 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// evidence.ts — what of a stored form the material at hand WITNESSES.
|
|
2
|
+
//
|
|
3
|
+
// A stored form is WITNESSED by some material when every one of its bytes lies
|
|
4
|
+
// inside a W-window that occurs literally in that material — in any order, at
|
|
5
|
+
// any place. It is the order-free reading of correspondence (`alignRuns` is
|
|
6
|
+
// its run-producing sibling, `junctionContainersFrom(…, unordered)` its
|
|
7
|
+
// container-finding one): a learnt form is evidenced by bytes that hold all of
|
|
8
|
+
// its windows, whichever way the asker happened to arrange them.
|
|
9
|
+
//
|
|
10
|
+
// The material is a LIST of sources because what a derivation has at hand is
|
|
11
|
+
// more than the question: the node it stands on is material too. A trained
|
|
12
|
+
// question `Edmond T. Gréville place of death` is in neither the asker's
|
|
13
|
+
// `Where was the place of death of the director of film Beat Girl?` nor the
|
|
14
|
+
// fact `The director of Beat Girl is Edmond T. Gréville.` the first hop reached
|
|
15
|
+
// — and both together hold every byte of it. The witness records which source
|
|
16
|
+
// held each window, so a consumer can tell the question's share (what the asker
|
|
17
|
+
// SAID about the form) from what the derivation itself brought.
|
|
18
|
+
//
|
|
19
|
+
// Exact, deterministic and linear: one window index per source, one probe per
|
|
20
|
+
// window of the form. Below one window a byte agreement is chance (the floor
|
|
21
|
+
// identityBar, attestedQ and the site test all draw), so a form shorter than W
|
|
22
|
+
// is never witnessed.
|
|
23
|
+
//
|
|
24
|
+
// Layering: bytes only — importable from traverse.ts and everything above it.
|
|
25
|
+
|
|
26
|
+
import { latin1 } from "../bytes.js";
|
|
27
|
+
|
|
28
|
+
/** Where each W-window of one source first occurs. Built once per source and
|
|
29
|
+
* reused across every form asked against it. */
|
|
30
|
+
export type WindowIndex = Map<string, number>;
|
|
31
|
+
|
|
32
|
+
export function windowIndex(bytes: Uint8Array, W: number): WindowIndex {
|
|
33
|
+
const index: WindowIndex = new Map();
|
|
34
|
+
for (let o = 0; o + W <= bytes.length; o++) {
|
|
35
|
+
const key = latin1(bytes.subarray(o, o + W));
|
|
36
|
+
if (!index.has(key)) index.set(key, o);
|
|
37
|
+
}
|
|
38
|
+
return index;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The windows of `index` (over `bytes`) that `spoken` does not hold — what
|
|
42
|
+
* of the question no product of the derivation has restated yet. The keys
|
|
43
|
+
* keep their positions in `bytes`, so a witnessing span still names the
|
|
44
|
+
* asker's bytes. */
|
|
45
|
+
export function unspoken(
|
|
46
|
+
index: WindowIndex,
|
|
47
|
+
spoken: ReadonlyArray<WindowIndex>,
|
|
48
|
+
): WindowIndex {
|
|
49
|
+
const out: WindowIndex = new Map();
|
|
50
|
+
for (const [key, at] of index) {
|
|
51
|
+
if (!spoken.some((s) => s.has(key))) out.set(key, at);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** How a form is witnessed by a list of sources. */
|
|
57
|
+
export interface Witnessing {
|
|
58
|
+
/** Every byte of the form lies in a window some source holds. */
|
|
59
|
+
complete: boolean;
|
|
60
|
+
/** The windows SOURCE 0 alone supplied — windows no later source holds — as
|
|
61
|
+
* merged spans of source 0. With the question as source 0, this is what the
|
|
62
|
+
* asker said about the form that the derivation did not already have. */
|
|
63
|
+
spans: Array<[number, number]>;
|
|
64
|
+
/** Bytes of source 0 inside `spans`. */
|
|
65
|
+
bytes: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Witness `form` against `sources` (their window indexes, same order). A
|
|
69
|
+
* window is credited to the LAST source holding it — so source 0 is credited
|
|
70
|
+
* only with what nothing else at hand supplies. */
|
|
71
|
+
export function witness(
|
|
72
|
+
form: Uint8Array,
|
|
73
|
+
indexes: ReadonlyArray<WindowIndex>,
|
|
74
|
+
W: number,
|
|
75
|
+
): Witnessing {
|
|
76
|
+
const none: Witnessing = { complete: false, spans: [], bytes: 0 };
|
|
77
|
+
if (form.length < W || indexes.length === 0) return none;
|
|
78
|
+
const covered = new Uint8Array(form.length);
|
|
79
|
+
const own: Array<[number, number]> = [];
|
|
80
|
+
for (let o = 0; o + W <= form.length; o++) {
|
|
81
|
+
const key = latin1(form.subarray(o, o + W));
|
|
82
|
+
let at = -1;
|
|
83
|
+
let from = -1;
|
|
84
|
+
for (let s = indexes.length - 1; s >= 0; s--) {
|
|
85
|
+
const p = indexes[s].get(key);
|
|
86
|
+
if (p !== undefined) {
|
|
87
|
+
at = p;
|
|
88
|
+
from = s;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (from < 0) continue;
|
|
93
|
+
covered.fill(1, o, o + W);
|
|
94
|
+
if (from === 0) own.push([at, at + W]);
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < form.length; i++) if (!covered[i]) return none;
|
|
97
|
+
own.sort((a, b) => a[0] - b[0]);
|
|
98
|
+
const spans: Array<[number, number]> = [];
|
|
99
|
+
for (const [s, e] of own) {
|
|
100
|
+
const last = spans[spans.length - 1];
|
|
101
|
+
if (last !== undefined && s <= last[1]) last[1] = Math.max(last[1], e);
|
|
102
|
+
else spans.push([s, e]);
|
|
103
|
+
}
|
|
104
|
+
let bytes = 0;
|
|
105
|
+
for (const [s, e] of spans) bytes += e - s;
|
|
106
|
+
return { complete: true, spans, bytes };
|
|
107
|
+
}
|
|
@@ -18,7 +18,12 @@ import type {
|
|
|
18
18
|
Site,
|
|
19
19
|
} from "../graph-search.js";
|
|
20
20
|
import { read, resolve } from "../primitives.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
guidedFirst,
|
|
23
|
+
hubBound,
|
|
24
|
+
namedContinuations,
|
|
25
|
+
scaffoldSpans,
|
|
26
|
+
} from "../traverse.js";
|
|
22
27
|
import { conceptHop } from "../match.js";
|
|
23
28
|
import { bridge } from "../resonance.js";
|
|
24
29
|
import { liftAnswer, liftedScaffolding, segRestatesQuery } from "../types.js";
|
|
@@ -229,7 +234,7 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
229
234
|
|
|
230
235
|
// Masking: computed spans are authoritative. Remove recognised sites
|
|
231
236
|
// that overlap any computed span before building the cover search.
|
|
232
|
-
|
|
237
|
+
let sites = computed.length === 0
|
|
233
238
|
? rec.sites
|
|
234
239
|
: rec.sites.filter((s) =>
|
|
235
240
|
!computed.some((u) => s.start < u.j && u.i < s.end)
|
|
@@ -254,6 +259,45 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
254
259
|
);
|
|
255
260
|
}
|
|
256
261
|
|
|
262
|
+
// A FRAGMENT ANSWERS OTHER QUESTIONS. A recognised form that sits inside
|
|
263
|
+
// other forms (it has structural parents or containers) holds continuations
|
|
264
|
+
// because the forms it is a piece of were asked — suffix inheritance gives
|
|
265
|
+
// `f death` the answer of every `… place of death` question. Choosing one of
|
|
266
|
+
// several such continuations by popularity voices some other question's
|
|
267
|
+
// answer, and the cover used to account the fragment's bytes as explained by
|
|
268
|
+
// it. A fragment with several continuations leads somewhere FOR THIS
|
|
269
|
+
// QUESTION only when the question names one (traverse.ts, the exact tier).
|
|
270
|
+
// A form that is (nearly) the whole question is not a piece of it: the
|
|
271
|
+
// question says nothing beyond it, so its continuations answer THIS question.
|
|
272
|
+
const asked = ctx._edgeAsked;
|
|
273
|
+
const W0 = ctx.space.maxGroup;
|
|
274
|
+
const leading = asked === null
|
|
275
|
+
? sites
|
|
276
|
+
: sites.filter((s) =>
|
|
277
|
+
query.length - (s.end - s.start) < W0 ||
|
|
278
|
+
!(ctx.store.hasParents(s.payload) ||
|
|
279
|
+
ctx.store.hasContainers(s.payload)) ||
|
|
280
|
+
ctx.store.nextFirst(s.payload, 2).length < 2 ||
|
|
281
|
+
namedContinuations(ctx, s.payload, asked) !== null
|
|
282
|
+
);
|
|
283
|
+
if (leading.length < sites.length) {
|
|
284
|
+
if (ctx.meter) {
|
|
285
|
+
ctx.meter.unaskedFragments += sites.length - leading.length;
|
|
286
|
+
}
|
|
287
|
+
ctx.trace?.step(
|
|
288
|
+
"unaskedFragments",
|
|
289
|
+
sites.filter((s) => !leading.includes(s)).map((s) =>
|
|
290
|
+
rItem(query.subarray(s.start, s.end), "fragment", s.payload, [
|
|
291
|
+
s.start,
|
|
292
|
+
s.end,
|
|
293
|
+
])
|
|
294
|
+
),
|
|
295
|
+
[],
|
|
296
|
+
"a piece of other forms whose continuations the question names none of — it answers other questions",
|
|
297
|
+
);
|
|
298
|
+
sites = leading;
|
|
299
|
+
}
|
|
300
|
+
|
|
257
301
|
if (sites.length === 0 && computed.length === 0) return [];
|
|
258
302
|
|
|
259
303
|
const connectors = ctx.meter
|
|
@@ -382,8 +426,19 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
382
426
|
// restated span contributes nothing to the composed answer, so it must
|
|
383
427
|
// not be priced as if it did (PASS-carried bytes are priced already;
|
|
384
428
|
// the diagnostic label reflects the same distinction).
|
|
429
|
+
//
|
|
430
|
+
// …and a span made of nothing but corpus-global scaffolding names nothing:
|
|
431
|
+
// every window of it is a piece of more than √N stored forms, so the form
|
|
432
|
+
// recognised there — the song `What`, in the trained store — is one of
|
|
433
|
+
// thousands it could be. Voicing that form's fact explains no byte the
|
|
434
|
+
// asker owes: measured, `What country is Jerry Bock a citizen of?` was won
|
|
435
|
+
// by `The performer of What is Melinda Marx.` glued onto the right fact.
|
|
436
|
+
const scaffold = scaffoldSpans(ctx, query);
|
|
385
437
|
const accounted: Array<[number, number]> = segs
|
|
386
|
-
.filter((s) =>
|
|
438
|
+
.filter((s) =>
|
|
439
|
+
s.rec && !segRestatesQuery(s, query, query.length, W) &&
|
|
440
|
+
!scaffold.some(([a, b]) => a <= s.i && s.j <= b)
|
|
441
|
+
)
|
|
387
442
|
.map((s) => [s.i, s.j]);
|
|
388
443
|
|
|
389
444
|
return [{
|
|
@@ -15,7 +15,13 @@ import {
|
|
|
15
15
|
import type { MindContext } from "../types.js";
|
|
16
16
|
import { gistOf, read, resolve } from "../primitives.js";
|
|
17
17
|
import { indexOf } from "../../bytes.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
allWindowsAreScaffolding,
|
|
20
|
+
askedEvidence,
|
|
21
|
+
corpusN,
|
|
22
|
+
guidedFirst,
|
|
23
|
+
hubBound,
|
|
24
|
+
} from "../traverse.js";
|
|
19
25
|
import {
|
|
20
26
|
follow,
|
|
21
27
|
project,
|
|
@@ -154,10 +160,18 @@ export async function recallByResonance(
|
|
|
154
160
|
g !== null && g.length > 0 &&
|
|
155
161
|
!lawRestates(query, g, 0, { proper: true })
|
|
156
162
|
) {
|
|
163
|
+
// EVIDENCE TRAVELS: when the question NAMED the continuation followed
|
|
164
|
+
// (traverse.ts, the exact tier — `Who is the father of Frederick II?`
|
|
165
|
+
// witnesses the father fact's own question `Frederick II father`), the
|
|
166
|
+
// spans that named it are explained too, not only the argument.
|
|
167
|
+
const hop = guidedFirst(ctx, arg.payload);
|
|
168
|
+
const named = hop === undefined
|
|
169
|
+
? []
|
|
170
|
+
: askedEvidence(ctx, arg.payload, hop);
|
|
157
171
|
return ground(
|
|
158
172
|
g,
|
|
159
173
|
"argument binding — the query's sole edge-source constituent, continuation followed",
|
|
160
|
-
[[arg.start, arg.end]],
|
|
174
|
+
[[arg.start, arg.end], ...named],
|
|
161
175
|
STEP,
|
|
162
176
|
);
|
|
163
177
|
}
|
package/src/mind/mind.ts
CHANGED
|
@@ -141,6 +141,7 @@ import {
|
|
|
141
141
|
leadsSomewhere,
|
|
142
142
|
} from "./traverse.js";
|
|
143
143
|
import { invalidateJunctionCache } from "./junction.js";
|
|
144
|
+
import type { WindowIndex } from "./evidence.js";
|
|
144
145
|
import { follow } from "./match.js";
|
|
145
146
|
import { recognise, segment } from "./recognition.js";
|
|
146
147
|
import { meaningOf } from "./resonance.js";
|
|
@@ -333,6 +334,8 @@ export class Mind implements MindContext {
|
|
|
333
334
|
* first-edge behaviour) and the reverse projection uses it for
|
|
334
335
|
* reverse-recall disambiguation via `chooseAmong`. */
|
|
335
336
|
_edgeGuide: Vec | null = null;
|
|
337
|
+
/** See {@link MindContext._edgeAsked}. */
|
|
338
|
+
_edgeAsked: { bytes: Uint8Array; index: WindowIndex } | null = null;
|
|
336
339
|
/** Per-response memo of {@link chooseNext} picks — ensures every mechanism
|
|
337
340
|
* of a single response follows the SAME continuation for each ambiguous
|
|
338
341
|
* context node. */
|
|
@@ -647,6 +650,7 @@ export class Mind implements MindContext {
|
|
|
647
650
|
this.canon = null;
|
|
648
651
|
this.canonMemo = null;
|
|
649
652
|
this._edgeGuide = null;
|
|
653
|
+
this._edgeAsked = null;
|
|
650
654
|
this._edgeChoice.clear();
|
|
651
655
|
}
|
|
652
656
|
|
package/src/mind/pipeline.ts
CHANGED
|
@@ -27,7 +27,9 @@ import {
|
|
|
27
27
|
} from "./derivation.js";
|
|
28
28
|
import { rItem } from "./trace.js";
|
|
29
29
|
import { unexplainedLabel } from "./rationale.js";
|
|
30
|
-
import { hubBound } from "./traverse.js";
|
|
30
|
+
import { hubBound, offsetCanon, scaffoldExtents } from "./traverse.js";
|
|
31
|
+
import { windowIndex, witness } from "./evidence.js";
|
|
32
|
+
import { indexOf } from "../bytes.js";
|
|
31
33
|
import {
|
|
32
34
|
type MechanismResult,
|
|
33
35
|
type PipelineMechanism,
|
|
@@ -189,6 +191,13 @@ export async function think(
|
|
|
189
191
|
if (query.length === 0) return null;
|
|
190
192
|
|
|
191
193
|
ctx._edgeGuide = gistOf(ctx, query);
|
|
194
|
+
{
|
|
195
|
+
const asked = offsetCanon(ctx, query);
|
|
196
|
+
ctx._edgeAsked = {
|
|
197
|
+
bytes: asked,
|
|
198
|
+
index: windowIndex(asked, ctx.space.maxGroup),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
192
201
|
ctx._edgeChoice.clear();
|
|
193
202
|
|
|
194
203
|
const t = ctx.trace?.enter("think", [rItem(query, "query")]);
|
|
@@ -690,7 +699,21 @@ export async function think(
|
|
|
690
699
|
const explained = priced.filter(([a, b]) =>
|
|
691
700
|
windowOf([a, b], answer, query, ctx.space.maxGroup) !== null
|
|
692
701
|
);
|
|
693
|
-
|
|
702
|
+
// THE DERIVATION IS BORN OWING ITS DISCRIMINATIVE MATERIAL. The bytes a
|
|
703
|
+
// corpus-global scaffolding window reaches (traverse.ts, `scaffoldExtents`)
|
|
704
|
+
// are nobody's debt — otherwise a later step could claim to pay them by
|
|
705
|
+
// restating ` is `, which every fact holds, and the law (which measures
|
|
706
|
+
// carrying by any window of what is owed) would admit it; the substitution
|
|
707
|
+
// bridge reads gaps the same way (`explainedSpan`). PRICING is untouched:
|
|
708
|
+
// the ladder still charges every unexplained byte, because for a question
|
|
709
|
+
// made of nothing but scaffolding (`How are you today?`) covering those bytes
|
|
710
|
+
// IS the evidence.
|
|
711
|
+
const scaffold = scaffoldExtents(ctx, query);
|
|
712
|
+
const paid = remainderOf(
|
|
713
|
+
query.length,
|
|
714
|
+
[...explained, ...scaffold],
|
|
715
|
+
ctx.space.maxGroup,
|
|
716
|
+
);
|
|
694
717
|
// WHAT THE CONSTRUCTION WITHHOLDS, at or above one quantum: the difference between
|
|
695
718
|
// the remainder paid in full and the remainder paid by carrying. Both readings
|
|
696
719
|
// are the law's, so the floor is applied once and in one place — and the
|
|
@@ -711,8 +734,31 @@ export async function think(
|
|
|
711
734
|
const uncovered = state.remainder;
|
|
712
735
|
|
|
713
736
|
// ── Post-grounding, gated by the declaration and the remainder ────────
|
|
737
|
+
// WHAT THE GROUNDING SPOKE FOR, when it did not declare it: the forms inside
|
|
738
|
+
// the answer that the ASKER already holds. An answer is the asker's material
|
|
739
|
+
// plus what the corpus added — the fact's own value — and only the former was
|
|
740
|
+
// spoken for: re-entering it walks back to the question. The latter is what a
|
|
741
|
+
// chain continues THROUGH. Consuming every form recognised in the answer
|
|
742
|
+
// consumed that too, so a chain could start only where recognition happened
|
|
743
|
+
// to miss the next entity — measured on 133 held-out 2Wiki compositional
|
|
744
|
+
// questions, 3 pivot steps in all once interior recognition named the entity
|
|
745
|
+
// inside every fact. Witnessed exactly, as `chooseNext`'s exact tier reads the
|
|
746
|
+
// question (evidence.ts); a form below one window cannot be witnessed, and is
|
|
747
|
+
// consumed as before.
|
|
748
|
+
const asked = ctx._edgeAsked;
|
|
714
749
|
const preConsumed = declaredUsed ??
|
|
715
|
-
new Set(
|
|
750
|
+
new Set(
|
|
751
|
+
recognise(ctx, answer).sites
|
|
752
|
+
.filter((s) =>
|
|
753
|
+
asked === null || s.end - s.start < ctx.space.maxGroup ||
|
|
754
|
+
witness(
|
|
755
|
+
offsetCanon(ctx, answer.subarray(s.start, s.end)),
|
|
756
|
+
[asked.index],
|
|
757
|
+
ctx.space.maxGroup,
|
|
758
|
+
).complete
|
|
759
|
+
)
|
|
760
|
+
.map((s) => s.payload),
|
|
761
|
+
);
|
|
716
762
|
// A grounding that DECLARED itself complete is not extended: the answer is
|
|
717
763
|
// already a trained form's own continuation, reached through an identity
|
|
718
764
|
// claim about the query, so a multi-hop pivot could only chain past the
|
|
@@ -741,8 +787,17 @@ export async function think(
|
|
|
741
787
|
// `preConsumed` is derived by re-recognising the answer — "everything in
|
|
742
788
|
// it", not "what it voiced" — and a containment rule over that would
|
|
743
789
|
// suppress every pivot the answer legitimately contains.
|
|
790
|
+
//
|
|
791
|
+
// …and a continuation the ANSWER ITSELF holds was voiced, not withheld. A
|
|
792
|
+
// substitution that answers with its anchor's own continuation (`The director
|
|
793
|
+
// of Beat Girl is Edmond T. Gréville.`) declared that anchor used, so reading
|
|
794
|
+
// its continuations as withheld refused every pivot inside the answer — the
|
|
795
|
+
// chain could never step past the entity the first hop introduced.
|
|
744
796
|
const voiced = declaredUsed === undefined ? [] : [...declaredUsed].flatMap(
|
|
745
|
-
(id) =>
|
|
797
|
+
(id) =>
|
|
798
|
+
ctx.store.nextFirst(id, hubBound(ctx))
|
|
799
|
+
.map((n) => read(ctx, n))
|
|
800
|
+
.filter((v) => indexOf(answer, v, 0) < 0),
|
|
746
801
|
);
|
|
747
802
|
// WHAT THIS BRANCH READ, published where it was read. Post-grounding decides
|
|
748
803
|
// by the DECLARATION (`decided.used`, which becomes `voiced`), by what the
|
package/src/mind/reasoning.ts
CHANGED
|
@@ -8,10 +8,17 @@ import { bytesEqual, indexOf, latin1 } from "../bytes.js";
|
|
|
8
8
|
import type { Attention, MindContext } from "./types.js";
|
|
9
9
|
import { read, resolve } from "./primitives.js";
|
|
10
10
|
import { countClusters } from "./attention.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
guidedFirst,
|
|
13
|
+
hubBound,
|
|
14
|
+
namedContinuations,
|
|
15
|
+
offsetCanon,
|
|
16
|
+
} from "./traverse.js";
|
|
17
|
+
import { unspoken, type WindowIndex, windowIndex } from "./evidence.js";
|
|
12
18
|
import { containsSpan, follow, haloSiblings, project } from "./match.js";
|
|
13
19
|
import { joinWithBridge, pivotInto } from "./resonance.js";
|
|
14
20
|
import {
|
|
21
|
+
closed,
|
|
15
22
|
closeOver,
|
|
16
23
|
type ClosureLayer,
|
|
17
24
|
type Continuation,
|
|
@@ -102,6 +109,12 @@ export function walkLayer(
|
|
|
102
109
|
// fire — the same visibility trade chooseNext documents.
|
|
103
110
|
const bound = hubBound(ctx);
|
|
104
111
|
const consumed = new Set<number>();
|
|
112
|
+
/** The windows each product of this derivation holds — what of the question
|
|
113
|
+
* the derivation has already restated, so a later step is NAMED only by
|
|
114
|
+
* question material none of them said. */
|
|
115
|
+
const spoken: WindowIndex[] = [];
|
|
116
|
+
const speak = (product: Uint8Array) =>
|
|
117
|
+
spoken.push(windowIndex(offsetCanon(ctx, product), ctx.space.maxGroup));
|
|
105
118
|
/** `prev` lets a caller hand in an already-read reverse-edge list — hop 0
|
|
106
119
|
* reuses the guard's, below, instead of re-reading it. */
|
|
107
120
|
const consumeNode = (
|
|
@@ -122,6 +135,7 @@ export function walkLayer(
|
|
|
122
135
|
const begin = async (d0: DerivationState): Promise<boolean> => {
|
|
123
136
|
const answer = d0.product;
|
|
124
137
|
startedFrom = answer;
|
|
138
|
+
speak(answer);
|
|
125
139
|
// Echo guard: a query that is ITSELF a learnt continuation (some context's
|
|
126
140
|
// answer) is being asked back at the system — hopping forward from it would
|
|
127
141
|
// chain through the very fact that produced it and echo the conversation
|
|
@@ -402,16 +416,48 @@ export function walkLayer(
|
|
|
402
416
|
);
|
|
403
417
|
return null;
|
|
404
418
|
}
|
|
419
|
+
// THE STEP THE QUESTION ASKS FOR. The hop `follow` took is NAMED when one
|
|
420
|
+
// of its establishing contexts is witnessed by what of the question no
|
|
421
|
+
// product has restated yet, plus the pivot itself (traverse.ts,
|
|
422
|
+
// `namedContinuations`): `Where was the place of death of the director of
|
|
423
|
+
// film Beat Girl?` names `Edmond T. Gréville place of death` once the first
|
|
424
|
+
// hop has restated `director` and `Beat Girl` — and `Who is the father of
|
|
425
|
+
// Frederick II?` names nothing past `Peter III of Aragon`, because `father`
|
|
426
|
+
// was restated by the hop that reached him. A named step MOVES to structure
|
|
427
|
+
// the question asked for, so it is admitted on that ground; an unnamed one
|
|
428
|
+
// must carry the question's remainder as before, and from a CLOSED
|
|
429
|
+
// derivation — nothing left owed — it is not offered at all: the question
|
|
430
|
+
// asks for no further step, and the law would otherwise admit any.
|
|
431
|
+
const asked = ctx._edgeAsked;
|
|
432
|
+
let named = false;
|
|
433
|
+
if (asked !== null) {
|
|
434
|
+
const unsaid = {
|
|
435
|
+
bytes: asked.bytes,
|
|
436
|
+
index: unspoken(asked.index, spoken),
|
|
437
|
+
};
|
|
438
|
+
const names = namedContinuations(ctx, pivot, unsaid);
|
|
439
|
+
const hop = guidedFirst(ctx, pivot);
|
|
440
|
+
named = names !== null && hop !== undefined && names.includes(hop);
|
|
441
|
+
}
|
|
442
|
+
if (!named && !producerOwnsShape && closed(d)) {
|
|
443
|
+
ctx.trace?.step(
|
|
444
|
+
"pivotUnasked",
|
|
445
|
+
[rItem(cur, "answer"), rNode(ctx, pivot, "pivot")],
|
|
446
|
+
[rItem(fc, "withheld")],
|
|
447
|
+
"the derivation owes nothing and the question names no further step — not offered",
|
|
448
|
+
);
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
405
451
|
pending = { kind: "pivot", cur, pivot, fc };
|
|
406
|
-
// Offered with the identity species
|
|
407
|
-
// speaks for
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
// test.
|
|
452
|
+
// Offered with the identity species when the grounding declared what it
|
|
453
|
+
// speaks for, or when the question names the step; otherwise the law
|
|
454
|
+
// requires this step to carry question material the grounding left
|
|
455
|
+
// unaccounted, which is the drift the extension tests pin — refused by the
|
|
456
|
+
// law's own measure, not by a private window test.
|
|
411
457
|
return {
|
|
412
458
|
product: fc,
|
|
413
459
|
contains: true,
|
|
414
|
-
reaches: producerOwnsShape,
|
|
460
|
+
reaches: producerOwnsShape || named,
|
|
415
461
|
cost: STEP,
|
|
416
462
|
};
|
|
417
463
|
};
|
|
@@ -422,6 +468,7 @@ export function walkLayer(
|
|
|
422
468
|
witnesses: ReadonlyArray<Witness>,
|
|
423
469
|
): void => {
|
|
424
470
|
meterTransition(ctx, before, after, witnesses);
|
|
471
|
+
speak(after.product);
|
|
425
472
|
const p = pending;
|
|
426
473
|
pending = null;
|
|
427
474
|
if (p === null) return;
|