@hviana/sema 0.8.3 → 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 +11 -10
- package/README.md +17 -38
- package/dist/example/demo.js +85 -34
- package/dist/src/geometry.d.ts +0 -10
- package/dist/src/geometry.js +0 -12
- package/dist/src/meter.d.ts +11 -0
- package/dist/src/meter.js +11 -0
- package/dist/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.js +2 -1
- 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 +37 -15
- package/dist/src/mind/match.d.ts +2 -0
- package/dist/src/mind/match.js +2 -0
- 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 +15 -18
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
- package/dist/src/mind/mechanisms/confluence.js +3 -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 +8 -9
- package/dist/src/mind/mechanisms/reference.js +3 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +1 -4
- package/dist/src/mind/pipeline.js +106 -41
- package/dist/src/mind/rationale.d.ts +0 -11
- package/dist/src/mind/rationale.js +6 -32
- package/dist/src/mind/reasoning.d.ts +4 -30
- package/dist/src/mind/reasoning.js +183 -151
- 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 +4 -4
- package/docs/architecture/closure.md +65 -0
- package/docs/architecture/commonality.md +27 -18
- package/docs/architecture/cost-model.md +5 -5
- package/docs/architecture/exact-vs-approximate.md +4 -4
- package/docs/architecture/factored-machinery.md +14 -14
- package/docs/architecture/mechanism-market.md +9 -9
- package/docs/architecture/meter.md +4 -5
- package/docs/architecture/store.md +2 -2
- package/docs/architecture/thresholds.md +1 -1
- package/docs/failures/tempting-but-wrong.md +11 -1
- package/docs/harness/gates.md +6 -6
- package/docs/mechanisms/cover.md +2 -2
- package/example/demo.ts +90 -37
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/geometry.ts +0 -13
- package/src/meter.ts +11 -0
- package/src/mind/articulation.ts +0 -1
- package/src/mind/attention.ts +2 -1
- package/src/mind/derivation.ts +473 -0
- package/src/mind/graph-search.ts +37 -20
- package/src/mind/match.ts +2 -0
- package/src/mind/mechanisms/alu.ts +0 -2
- package/src/mind/mechanisms/cast.ts +17 -21
- package/src/mind/mechanisms/confluence.ts +3 -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 +7 -9
- package/src/mind/mechanisms/reference.ts +2 -3
- package/src/mind/pipeline-mechanism.ts +1 -4
- package/src/mind/pipeline.ts +121 -46
- package/src/mind/rationale.ts +6 -36
- package/src/mind/reasoning.ts +208 -178
- package/src/mind/types.ts +5 -2
- 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/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 +22 -2
- package/test/55-cost-meter.test.mjs +4 -1
package/src/mind/reasoning.ts
CHANGED
|
@@ -10,22 +10,18 @@ import { resolve } from "./primitives.js";
|
|
|
10
10
|
import { corpusN, hubBound } from "./traverse.js";
|
|
11
11
|
import { containsSpan, follow, haloSiblings, project } from "./match.js";
|
|
12
12
|
import { joinWithBridge, pivotInto } from "./resonance.js";
|
|
13
|
+
import { admissible, advance, type Continuation } from "./derivation.js";
|
|
13
14
|
import type { Precomputed } from "./pipeline-mechanism.js";
|
|
14
|
-
import { type Rationale
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* walk turns up, since `follow()`/`chooseNext`/`pivotInto` know nothing of
|
|
25
|
-
* the query at all — only of what structurally continues what. */
|
|
26
|
-
export function restatesQuery(query: Uint8Array, bytes: Uint8Array): boolean {
|
|
27
|
-
return bytes.length < query.length && indexOf(query, bytes, 0) >= 0;
|
|
28
|
-
}
|
|
15
|
+
import { type Rationale } from "./rationale.js";
|
|
16
|
+
import {
|
|
17
|
+
closure,
|
|
18
|
+
type DerivationState,
|
|
19
|
+
type Offer,
|
|
20
|
+
restates,
|
|
21
|
+
type Span,
|
|
22
|
+
unaccountedBytes,
|
|
23
|
+
} from "./derivation.js";
|
|
24
|
+
import { STEP } from "./graph-search.js";
|
|
29
25
|
|
|
30
26
|
/** Extend a grounded answer forward across facts (multi-hop reasoning).
|
|
31
27
|
* Pivots on the longest unconsumed learnt context each answer contains,
|
|
@@ -42,31 +38,19 @@ export function restatesQuery(query: Uint8Array, bytes: Uint8Array): boolean {
|
|
|
42
38
|
* when it declared one — see the pivot's own containment rule. `pre` is the
|
|
43
39
|
* response's shared pre-computation — the post-grounding stages read the
|
|
44
40
|
* same container the mechanisms did. */
|
|
45
|
-
/** What the multi-hop extension produced, and what it cost: the bytes (the
|
|
46
|
-
* answer), the spans of the grounding's UNCOVERED material that each step was
|
|
47
|
-
* justified by, and how many steps were taken. The last two exist so the
|
|
48
|
-
* caller can price the extension in the ladder's own currency — `steps · STEP`
|
|
49
|
-
* against `PASS · unaccounted` — instead of taking it unconditionally. Both
|
|
50
|
-
* are FACTS, not verdicts: nothing here says whether the extension was worth
|
|
51
|
-
* it; that is the comparison's job, one layer up. */
|
|
52
|
-
export interface ReasonedAnswer {
|
|
53
|
-
bytes: Uint8Array;
|
|
54
|
-
carried: Array<[number, number]>;
|
|
55
|
-
steps: number;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
41
|
export async function reason(
|
|
59
42
|
ctx: MindContext,
|
|
60
43
|
query: Uint8Array,
|
|
61
|
-
|
|
44
|
+
d0: DerivationState,
|
|
62
45
|
preConsumed: ReadonlySet<number>,
|
|
63
46
|
pre: Precomputed,
|
|
64
47
|
voiced: readonly Uint8Array[] = [],
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
48
|
+
): Promise<DerivationState> {
|
|
49
|
+
const answer = d0.product;
|
|
50
|
+
/** The query material the GROUNDING left uncovered — the state's own
|
|
51
|
+
* remainder. Only the reasoner's OWN extensions are judged against it; a
|
|
52
|
+
* mechanism carrying its own `used` set owns its shape. */
|
|
53
|
+
const uncovered = d0.remainder;
|
|
70
54
|
// Echo guard: a query that is ITSELF a learnt continuation (some context's
|
|
71
55
|
// answer) is being asked back at the system — hopping forward from it would
|
|
72
56
|
// chain through the very fact that produced it and echo the conversation
|
|
@@ -74,7 +58,7 @@ export async function reason(
|
|
|
74
58
|
// broad structural gate; pinned by test/31-audit.
|
|
75
59
|
const qId = pre.queryResolved;
|
|
76
60
|
if (qId !== null && ctx.store.prevCount(qId) > 0) {
|
|
77
|
-
return
|
|
61
|
+
return d0;
|
|
78
62
|
}
|
|
79
63
|
|
|
80
64
|
// Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
|
|
@@ -94,7 +78,7 @@ export async function reason(
|
|
|
94
78
|
// is nothing left to chain for.
|
|
95
79
|
//
|
|
96
80
|
// Every stopping condition in the loop below judges the ANSWER (`consumed` /
|
|
97
|
-
// `
|
|
81
|
+
// the law's `restates` / `bytesEqual`); none asks whether the QUESTION was
|
|
98
82
|
// satisfied. So a single-hop question whose answer happens to name another
|
|
99
83
|
// learnt context extends past a correct answer and REPLACES it:
|
|
100
84
|
//
|
|
@@ -134,7 +118,7 @@ export async function reason(
|
|
|
134
118
|
? null
|
|
135
119
|
: ctx.store.prevFirst(groundedId, bound);
|
|
136
120
|
if (qId !== null && groundedPrev !== null && groundedPrev.includes(qId)) {
|
|
137
|
-
return
|
|
121
|
+
return d0;
|
|
138
122
|
}
|
|
139
123
|
|
|
140
124
|
const consumed = new Set<number>();
|
|
@@ -176,41 +160,66 @@ export async function reason(
|
|
|
176
160
|
await preconsume();
|
|
177
161
|
}
|
|
178
162
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
// the extension's cost against what it explains, in the ladder's own currency.
|
|
189
|
-
const carried: Array<[number, number]> = [];
|
|
190
|
-
let steps = 0;
|
|
163
|
+
// ── THE WALK, AS THE LAW'S ───────────────────────────────────────────
|
|
164
|
+
//
|
|
165
|
+
// This function no longer decides whether a step may be taken: it OFFERS a
|
|
166
|
+
// continuation — a forward step from the answer's own learnt edge, or a pivot
|
|
167
|
+
// on a learnt context the answer contains — and `derivation.ts`'s law admits
|
|
168
|
+
// or refuses it. The state it offers against is the pipeline's own: the
|
|
169
|
+
// grounding's remainder (what the question still owes), its accounting, its
|
|
170
|
+
// cost.
|
|
171
|
+
//
|
|
191
172
|
// NO ALLOWANCE: THE CHAIN ENDS WHEN IT STOPS. Every exit below is the law —
|
|
192
173
|
// no pivot, no forward step, no question material carried — and the walk is
|
|
193
|
-
// bounded by the material and the graph rather than by a count:
|
|
194
|
-
// step
|
|
195
|
-
// `consumed` refuses to revisit a node. `recallQueryK` no longer bounds
|
|
196
|
-
// reasoner here; it keeps its other roles (the bridge's candidate reads,
|
|
197
|
-
// pivot's probe budget, the resonance limits).
|
|
174
|
+
// bounded by the material and the graph rather than by a count: the law
|
|
175
|
+
// requires each step to engage what is left (or to move to new structure),
|
|
176
|
+
// and `consumed` refuses to revisit a node. `recallQueryK` no longer bounds
|
|
177
|
+
// the reasoner here; it keeps its other roles (the bridge's candidate reads,
|
|
178
|
+
// the pivot's probe budget, the resonance limits).
|
|
198
179
|
//
|
|
199
|
-
// Measured before removing
|
|
200
|
-
// case in the suite — is green and no slower without
|
|
201
|
-
//
|
|
180
|
+
// Measured before removing the allowance: test/89 — the corpus-cost guard, the
|
|
181
|
+
// heaviest case in the suite — is green and no slower without it (27 s against
|
|
182
|
+
// 30 s); and raising it from 12 to 200 changed neither the answer nor
|
|
202
183
|
// `pivotSteps` on the chain fixtures.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
184
|
+
const qv = pre.guide; // the response-wide guide IS the query's gist
|
|
185
|
+
const W = ctx.space.maxGroup;
|
|
186
|
+
// WHOSE EXTENSION IS THIS? `voiced` is what the mechanism WITHHELD (the
|
|
187
|
+
// pipeline sends the used anchors' CONTINUATIONS, not their bytes), so a
|
|
188
|
+
// non-empty `voiced` means exactly what that note says: the grounding came
|
|
189
|
+
// from a mechanism that carries its own short `used` set (cast/join) and
|
|
190
|
+
// therefore OWNS the shape of its answer. The further terms inside such a
|
|
191
|
+
// seat are legitimately followable (test/29 C3's `Mona Lisa` lives inside the
|
|
192
|
+
// voiced seat and leads on to a fact about neither analog), which the law
|
|
193
|
+
// expresses as the identity species of progress — `moves` — declared by the
|
|
194
|
+
// reporter and never inferred from the mechanism's name.
|
|
195
|
+
const producerOwnsShape = voiced.length > 0;
|
|
196
|
+
const startedFrom = answer;
|
|
197
|
+
let hop = 0;
|
|
198
|
+
let t: ReturnType<Rationale["enter"]> | undefined;
|
|
199
|
+
// WHAT THE OFFERED STEP WOULD BE. The law decides, so the accepted step is
|
|
200
|
+
// traced where the decision went (`onTaken`) and a refused one is reported as
|
|
201
|
+
// the law's refusal (`onRefused`) — a step is never reported before it is
|
|
202
|
+
// admitted.
|
|
203
|
+
let pending:
|
|
204
|
+
| { kind: "absorb"; cur: Uint8Array; curId: number | null; fwd: Uint8Array }
|
|
205
|
+
| { kind: "pivot"; cur: Uint8Array; pivot: number; fc: Uint8Array }
|
|
206
|
+
| null = null;
|
|
207
|
+
|
|
208
|
+
const offer: Offer = async (d) => {
|
|
209
|
+
const cur = d.product;
|
|
210
|
+
// The first step's `cur` IS the grounding's product, so the guard above
|
|
211
|
+
// already resolved it and read its reverse edges — reuse both.
|
|
206
212
|
const curId = hop === 0 ? groundedId : resolve(ctx, cur);
|
|
207
213
|
consumeNode(curId, hop === 0 ? groundedPrev ?? undefined : undefined);
|
|
214
|
+
hop++;
|
|
208
215
|
|
|
209
216
|
// Forward-absorb: follow only UNCONSUMED continuations. The gate below
|
|
210
|
-
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
217
|
+
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows nothing
|
|
218
|
+
// of `consumed` and may still walk to a consumed fixpoint — absorbing it
|
|
219
|
+
// would repeat content the grounding stage already spoke for, so a consumed
|
|
220
|
+
// fixpoint falls through to the pivot step instead. Offered with `moves`:
|
|
221
|
+
// completing the answer's OWN learnt form is an identity step, not a claim
|
|
222
|
+
// about the asker's material.
|
|
214
223
|
if (
|
|
215
224
|
curId !== null &&
|
|
216
225
|
ctx.store.nextFirst(curId, bound).some((n) => !consumed.has(n))
|
|
@@ -220,133 +229,122 @@ export async function reason(
|
|
|
220
229
|
if (
|
|
221
230
|
fwd !== null && !bytesEqual(fwd, cur) &&
|
|
222
231
|
(fwdId === null || !consumed.has(fwdId)) &&
|
|
223
|
-
!
|
|
232
|
+
!restates(query, fwd, 0, { proper: true })
|
|
224
233
|
) {
|
|
225
234
|
consumeAll(curId);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
]);
|
|
229
|
-
ctx.trace?.step(
|
|
230
|
-
"absorbForward",
|
|
231
|
-
[rItem(cur, "answer", curId)],
|
|
232
|
-
[rItem(fwd, "answer", resolve(ctx, fwd) ?? undefined)],
|
|
233
|
-
"the answer is itself a learnt fact — follow its continuation to the fixpoint",
|
|
234
|
-
);
|
|
235
|
-
cur = fwd;
|
|
236
|
-
steps++;
|
|
237
|
-
continue;
|
|
235
|
+
pending = { kind: "absorb", cur, curId, fwd };
|
|
236
|
+
return { product: fwd, contains: true, reaches: true, cost: STEP };
|
|
238
237
|
}
|
|
239
238
|
}
|
|
240
239
|
|
|
241
|
-
// Pivot:
|
|
240
|
+
// Pivot: the longest unconsumed learnt context the answer contains.
|
|
242
241
|
consumeAll(curId);
|
|
243
242
|
const pivot = await pivotInto(ctx, cur, consumed, voiced);
|
|
244
|
-
if (pivot === null)
|
|
245
|
-
|
|
243
|
+
if (pivot === null) return null;
|
|
246
244
|
const fc = await follow(ctx, pivot, qv);
|
|
247
245
|
consumeAll(pivot);
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
// the
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
justified = [a, b];
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if (progress) break;
|
|
246
|
+
if (
|
|
247
|
+
fc === null || bytesEqual(fc, cur) ||
|
|
248
|
+
restates(query, fc, 0, { proper: true })
|
|
249
|
+
) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
pending = { kind: "pivot", cur, pivot, fc };
|
|
253
|
+
// Offered with the identity species ONLY when the grounding declared what it
|
|
254
|
+
// speaks for; otherwise the law requires this step to carry question
|
|
255
|
+
// material the grounding left unaccounted, which is the drift the extension
|
|
256
|
+
// tests pin — refused by the law's own measure, not by a private window
|
|
257
|
+
// test.
|
|
258
|
+
return {
|
|
259
|
+
product: fc,
|
|
260
|
+
contains: true,
|
|
261
|
+
reaches: producerOwnsShape,
|
|
262
|
+
cost: STEP,
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const closed_ = await closure(
|
|
267
|
+
d0,
|
|
268
|
+
query,
|
|
269
|
+
W,
|
|
270
|
+
offer,
|
|
271
|
+
(before, after) => {
|
|
272
|
+
if (ctx.meter) {
|
|
273
|
+
ctx.meter.closureDrainedBytes += unaccountedBytes(before.remainder) -
|
|
274
|
+
unaccountedBytes(after.remainder);
|
|
281
275
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// silent cut AGENTS §6 forbids: the rationale is where a reader learns
|
|
288
|
-
// that an extension was declined for want of question material, and
|
|
289
|
-
// where the next person sees why the chain stopped here. Measured with
|
|
290
|
-
// the check disabled, test/110 and test/116 fail — so this brake is the
|
|
291
|
-
// only thing keeping the extension honest until the pivot reports its
|
|
292
|
-
// own accounted spans and the ladder can judge it instead.
|
|
293
|
-
//
|
|
294
|
-
// THE PROMISE IS NOW KEPT, AND THE BRAKE TURNS OUT TO BE THE LADDER'S
|
|
295
|
-
// OWN CONSEQUENCE. The extension reports what it carried (`carried`,
|
|
296
|
-
// the span each step was justified by) and what it cost (`steps`), both
|
|
297
|
-
// counted in the meter (`reasonCarriedBytes`, `reasonSteps`), so the
|
|
298
|
-
// ladder CAN judge it: it accepts while
|
|
299
|
-
//
|
|
300
|
-
// steps · STEP < PASS · carried
|
|
301
|
-
//
|
|
302
|
-
// and this brake accepts whenever the step carries a `W`-window, i.e.
|
|
303
|
-
// whenever `carried ≥ W ≥ 1`. With `PASS/STEP = 1000` the two therefore
|
|
304
|
-
// agree on every extension with `steps ≤ 1000 · carried` — and every
|
|
305
|
-
// extension this repository produces takes 0 or 1 steps (measured on
|
|
306
|
-
// chains of 3, 8, 20 and 40 links). Above that bound the ladder would
|
|
307
|
-
// refuse what this brake accepts, which is the corner named in the
|
|
308
|
-
// closure report's limits: a chain of thousands of links explaining a
|
|
309
|
-
// handful of bytes. No guard is added for it — a limit without a
|
|
310
|
-
// derivation is exactly what the brake must not become.
|
|
311
|
-
const left = unaccountedBytes(uncovered);
|
|
276
|
+
const p = pending;
|
|
277
|
+
pending = null;
|
|
278
|
+
if (p === null) return;
|
|
279
|
+
t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
|
|
280
|
+
if (p.kind === "absorb") {
|
|
312
281
|
ctx.trace?.step(
|
|
313
|
-
"
|
|
314
|
-
[rItem(cur, "answer"
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
282
|
+
"absorbForward",
|
|
283
|
+
[rItem(p.cur, "answer", p.curId ?? undefined)],
|
|
284
|
+
[rItem(p.fwd, "answer", resolve(ctx, p.fwd) ?? undefined)],
|
|
285
|
+
"the answer is itself a learnt fact — follow its continuation to the fixpoint",
|
|
286
|
+
);
|
|
287
|
+
} else {
|
|
288
|
+
if (ctx.meter) ctx.meter.pivotSteps++;
|
|
289
|
+
ctx.trace?.step(
|
|
290
|
+
"pivotStep",
|
|
291
|
+
[rItem(p.cur, "answer"), rNode(ctx, p.pivot, "pivot")],
|
|
292
|
+
[rItem(p.fc, "answer", resolve(ctx, p.fc) ?? undefined)],
|
|
293
|
+
"pivot on the shared span this answer contains, then step forward across that fact",
|
|
318
294
|
);
|
|
319
|
-
break;
|
|
320
295
|
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
"
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
296
|
+
},
|
|
297
|
+
(at) => {
|
|
298
|
+
const p = pending;
|
|
299
|
+
pending = null;
|
|
300
|
+
if (p === null || p.kind !== "pivot") return;
|
|
301
|
+
// THE BRAKE, MADE VISIBLE — and it is now the LAW's refusal, reported
|
|
302
|
+
// where it happened. The reasoner declines a step that carries none of
|
|
303
|
+
// the material the grounding left unaccounted. A refusal that leaves no
|
|
304
|
+
// trace is the kind of silent cut AGENTS §6 forbids: the rationale is
|
|
305
|
+
// where a reader learns that an extension was declined for want of
|
|
306
|
+
// question material, and where the next person sees why the chain stopped
|
|
307
|
+
// here. Measured with the check disabled, test/110 and test/116 fail.
|
|
308
|
+
const left = unaccountedBytes(at.remainder);
|
|
309
|
+
ctx.trace?.step(
|
|
310
|
+
"pivotRefused",
|
|
311
|
+
[rItem(p.cur, "answer"), rItem(query, "query")],
|
|
312
|
+
at.remainder.map(([a, b]) => rItem(query.subarray(a, b), "uncovered")),
|
|
313
|
+
`the step carries none of the question material the grounding left ` +
|
|
314
|
+
`unaccounted (${left} byte(s) in ${at.remainder.length} span(s)) — refused`,
|
|
315
|
+
);
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
333
319
|
// INSTRUMENTATION ONLY — the extension's two facts, untraced (meter.ts
|
|
334
320
|
// contract 1: a counter never reaches a decision). They are what a caller
|
|
335
|
-
// needs to PRICE the extension instead of taking it unconditionally
|
|
336
|
-
//
|
|
321
|
+
// needs to PRICE the extension instead of taking it unconditionally, and both
|
|
322
|
+
// are now read off the state the law advanced rather than accumulated beside
|
|
323
|
+
// it: the work it did is the cost it accumulated, and the material it carried
|
|
324
|
+
// is the accounting the law's witnesses added.
|
|
325
|
+
const steps = closed_.cost - d0.cost;
|
|
337
326
|
if (ctx.meter) {
|
|
338
327
|
ctx.meter.reasonSteps += steps;
|
|
339
|
-
|
|
328
|
+
// The accounting the law's witnesses added, summed by the law's own function
|
|
329
|
+
// over the tail of the state's list — and computed ONLY when a meter is
|
|
330
|
+
// attached: this used to slice and MAP a fresh array on every response, for a
|
|
331
|
+
// counter that usually does not exist. One allocation, under the meter.
|
|
332
|
+
ctx.meter.reasonCarriedBytes += unaccountedBytes(
|
|
333
|
+
closed_.accounted.slice(d0.accounted.length),
|
|
334
|
+
);
|
|
340
335
|
}
|
|
341
336
|
t?.done(
|
|
342
|
-
[rItem(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
337
|
+
[rItem(
|
|
338
|
+
closed_.product,
|
|
339
|
+
"answer",
|
|
340
|
+
resolve(ctx, closed_.product) ?? undefined,
|
|
341
|
+
)],
|
|
342
|
+
// A FIXPOINT: no further step was offered, or the law refused the one that
|
|
343
|
+
// was. There is no allowance to exhaust, so the note is true by
|
|
344
|
+
// construction.
|
|
347
345
|
"the multi-hop chain's fixpoint",
|
|
348
346
|
);
|
|
349
|
-
return
|
|
347
|
+
return closed_;
|
|
350
348
|
}
|
|
351
349
|
|
|
352
350
|
/** Fuse independent points of attention into one answer (multi-topic).
|
|
@@ -356,7 +354,7 @@ export async function reason(
|
|
|
356
354
|
export async function fuseAttention(
|
|
357
355
|
ctx: MindContext,
|
|
358
356
|
query: Uint8Array,
|
|
359
|
-
|
|
357
|
+
state: DerivationState,
|
|
360
358
|
pre: Precomputed,
|
|
361
359
|
/** True when `primary` never touched the consensus climb at all — e.g. a
|
|
362
360
|
* pure ALU computation, which has no anchor of its own. commitVotes
|
|
@@ -371,8 +369,13 @@ export async function fuseAttention(
|
|
|
371
369
|
* which is the layer that knows how a given grounding records its evidence;
|
|
372
370
|
* fuseAttention just reads a position from it. Empty or absent preserves
|
|
373
371
|
* the original behaviour exactly. */
|
|
374
|
-
primarySpans: ReadonlyArray<
|
|
375
|
-
): Promise<
|
|
372
|
+
primarySpans: ReadonlyArray<Span> = [],
|
|
373
|
+
): Promise<DerivationState> {
|
|
374
|
+
// THE STATE, NOT BARE BYTES: fusion is one more transition of the derivation
|
|
375
|
+
// the walk returned, so it reads that state's product and hands back a state.
|
|
376
|
+
// Its own structural gates stay here — this is the layer that can resolve
|
|
377
|
+
// those witnesses, and the law never re-derives one.
|
|
378
|
+
const primary = state.product;
|
|
376
379
|
// When the answer is structurally drawn from the query itself
|
|
377
380
|
// (extraction), it already spans all the query's pieces — fusion
|
|
378
381
|
// would only add noise from unrelated stored contexts. The gate is
|
|
@@ -380,7 +383,7 @@ export async function fuseAttention(
|
|
|
380
383
|
// byte run): the old sparse-subsequence test was trivially satisfied by
|
|
381
384
|
// short answers over long queries, silently starving multi-topic queries
|
|
382
385
|
// of fusion.
|
|
383
|
-
if (containsSpan(ctx, query, primary)) return
|
|
386
|
+
if (containsSpan(ctx, query, primary)) return state;
|
|
384
387
|
|
|
385
388
|
// The committed points of attention ARE the shared climb's roots (same
|
|
386
389
|
// query, same k, same DF mode) — read them from Precomputed instead of
|
|
@@ -423,7 +426,7 @@ export async function fuseAttention(
|
|
|
423
426
|
const lonePromotes = unclimbed && forest.length === 1 &&
|
|
424
427
|
forest[0].breadth > 0.5 && independentOfPrimary(forest[0]);
|
|
425
428
|
if (forest.length === 0 || (forest.length <= 1 && !lonePromotes)) {
|
|
426
|
-
return
|
|
429
|
+
return state;
|
|
427
430
|
}
|
|
428
431
|
|
|
429
432
|
// WHERE THE QUERY ASKED FOR IT. The sort below orders the fused pieces by
|
|
@@ -506,7 +509,13 @@ export async function fuseAttention(
|
|
|
506
509
|
// the root resonates.
|
|
507
510
|
const cont = await follow(ctx, root.anchor, qv);
|
|
508
511
|
if (
|
|
509
|
-
cont !== null && cont.length > 0 &&
|
|
512
|
+
cont !== null && cont.length > 0 &&
|
|
513
|
+
// The law's positional reading: the caller knows the material it is
|
|
514
|
+
// looking for lies AT OR AFTER this root, which is the witness; the law
|
|
515
|
+
// owns the containment test itself. The `cont.length > 0` guard stays
|
|
516
|
+
// here because an EMPTY continuation indexes at every offset — the law
|
|
517
|
+
// has no opinion about whether "nothing" counts as said.
|
|
518
|
+
restates(query, cont, 0, { from: root.end })
|
|
510
519
|
) {
|
|
511
520
|
ctx.trace?.step(
|
|
512
521
|
"alreadyAnswered",
|
|
@@ -526,7 +535,7 @@ export async function fuseAttention(
|
|
|
526
535
|
[rItem(primary, "answer")],
|
|
527
536
|
"no further independent point grounded",
|
|
528
537
|
);
|
|
529
|
-
return
|
|
538
|
+
return state;
|
|
530
539
|
}
|
|
531
540
|
|
|
532
541
|
pieces.sort((a, b) => a.start - b.start);
|
|
@@ -543,5 +552,26 @@ export async function fuseAttention(
|
|
|
543
552
|
// THE FACT IS THE FUSED ANSWER, not the call: every early return above hands
|
|
544
553
|
// back `primary` untouched. Untraced on purpose (meter.ts contract 1).
|
|
545
554
|
if (ctx.meter) ctx.meter.fuseRuns++;
|
|
546
|
-
|
|
555
|
+
// A FUSION IS A TRANSITION, and the only one in the engine that can splice the
|
|
556
|
+
// QUESTION'S OWN material into the product. So it is OFFERED to the law rather
|
|
557
|
+
// than built by hand: the law reads the window the fused product holds — the
|
|
558
|
+
// same reading the carries admission uses, one definition — accounts for the
|
|
559
|
+
// span it carried, and lets the question's remainder drain on EVIDENCE. A
|
|
560
|
+
// fusion that carries nothing consumes nothing, because the reading finds no
|
|
561
|
+
// window; and `reaches` is declared only when the fusion composed another root,
|
|
562
|
+
// which is structure this derivation had not stood on.
|
|
563
|
+
const fusedT: Continuation = {
|
|
564
|
+
product: out,
|
|
565
|
+
contains: true,
|
|
566
|
+
reaches: rest.length > 0,
|
|
567
|
+
cost: STEP,
|
|
568
|
+
};
|
|
569
|
+
const fusedWitness = admissible(state, fusedT, query, ctx.space.maxGroup);
|
|
570
|
+
if (fusedWitness === null) return { ...state, product: out };
|
|
571
|
+
const fused = advance(state, fusedT, fusedWitness);
|
|
572
|
+
if (ctx.meter) {
|
|
573
|
+
ctx.meter.closureDrainedBytes += unaccountedBytes(state.remainder) -
|
|
574
|
+
unaccountedBytes(fused.remainder);
|
|
575
|
+
}
|
|
576
|
+
return fused;
|
|
547
577
|
}
|
package/src/mind/types.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface DepositCacheEntry {
|
|
|
34
34
|
content: ContentFold;
|
|
35
35
|
}
|
|
36
36
|
import { bytesEqual, concatBytes, indexOf } from "../bytes.js";
|
|
37
|
+
import { restates } from "./derivation.js";
|
|
37
38
|
import { dominates } from "../geometry.js";
|
|
38
39
|
|
|
39
40
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -486,11 +487,13 @@ export function segRestatesQuery(
|
|
|
486
487
|
W: number,
|
|
487
488
|
): boolean {
|
|
488
489
|
if (!s.rec) return false;
|
|
490
|
+
// THE LITERAL EXEMPTION IS THE CALLER'S. A span that IS the site's own bytes
|
|
491
|
+
// at its own position is naming what is already there, not substituting for
|
|
492
|
+
// it — and only this caller knows that, so it says so by not asking.
|
|
489
493
|
const literal = s.j - s.i === s.bytes.length &&
|
|
490
494
|
bytesEqual(s.bytes, query.subarray(s.i, s.j));
|
|
491
495
|
if (literal) return false;
|
|
492
|
-
return s.bytes
|
|
493
|
-
indexOf(query, s.bytes, 0) >= 0;
|
|
496
|
+
return restates(query, s.bytes, W, { proper: true });
|
|
494
497
|
}
|
|
495
498
|
|
|
496
499
|
/** Lift the answer out of the cover for think: the recognised region, free of
|