@hviana/sema 0.8.2 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +38 -37
- package/README.md +17 -38
- package/TRADEMARKS.md +0 -1
- package/dist/example/demo.js +85 -34
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.js +2 -0
- package/dist/src/geometry.d.ts +21 -10
- package/dist/src/geometry.js +21 -12
- package/dist/src/meter.d.ts +62 -0
- package/dist/src/meter.js +62 -0
- package/dist/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.d.ts +4 -0
- package/dist/src/mind/attention.js +167 -17
- package/dist/src/mind/canonical.d.ts +16 -0
- package/dist/src/mind/canonical.js +41 -0
- package/dist/src/mind/derivation.d.ts +201 -0
- package/dist/src/mind/derivation.js +327 -0
- package/dist/src/mind/graph-search.d.ts +2 -1
- package/dist/src/mind/graph-search.js +70 -29
- package/dist/src/mind/match.d.ts +3 -1
- package/dist/src/mind/match.js +7 -3
- package/dist/src/mind/mechanisms/alu.js +0 -2
- package/dist/src/mind/mechanisms/cast.d.ts +1 -5
- package/dist/src/mind/mechanisms/cast.js +16 -19
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
- package/dist/src/mind/mechanisms/confluence.js +27 -9
- package/dist/src/mind/mechanisms/cover.js +17 -20
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
- package/dist/src/mind/mechanisms/extraction.js +13 -8
- package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
- package/dist/src/mind/mechanisms/recall.d.ts +0 -1
- package/dist/src/mind/mechanisms/recall.js +40 -13
- package/dist/src/mind/mechanisms/reference.js +3 -4
- package/dist/src/mind/mind.d.ts +4 -2
- package/dist/src/mind/mind.js +5 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +7 -3
- package/dist/src/mind/pipeline.js +136 -44
- package/dist/src/mind/primitives.js +9 -1
- package/dist/src/mind/rationale.d.ts +21 -5
- package/dist/src/mind/rationale.js +16 -21
- package/dist/src/mind/reasoning.d.ts +12 -20
- package/dist/src/mind/reasoning.js +190 -106
- package/dist/src/mind/recognition.js +4 -8
- package/dist/src/mind/resonance.js +20 -1
- package/dist/src/mind/trace.js +1 -0
- package/dist/src/mind/traverse.js +6 -2
- package/dist/src/mind/types.d.ts +36 -13
- package/dist/src/mind/types.js +6 -3
- package/docs/INDEX.md +23 -24
- package/docs/INVARIANTS.md +16 -17
- package/docs/architecture/bounded-reads.md +5 -5
- package/docs/architecture/closure.md +65 -0
- package/docs/architecture/commonality.md +29 -20
- package/docs/architecture/cost-model.md +7 -7
- package/docs/architecture/determinism.md +7 -7
- package/docs/architecture/exact-vs-approximate.md +4 -4
- package/docs/architecture/factored-machinery.md +14 -14
- package/docs/architecture/match-project.md +2 -3
- package/docs/architecture/mechanism-market.md +16 -16
- package/docs/architecture/meter.md +10 -11
- package/docs/architecture/store.md +4 -4
- package/docs/architecture/thresholds.md +1 -1
- package/docs/failures/tempting-but-wrong.md +14 -5
- package/docs/harness/gates.md +7 -7
- package/docs/mechanisms/cast.md +2 -2
- package/docs/mechanisms/cover.md +4 -5
- package/docs/mechanisms/extraction.md +7 -7
- package/docs/mechanisms/recall.md +8 -9
- package/example/demo.ts +90 -37
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +11 -12
- package/src/config.ts +13 -0
- package/src/geometry.ts +21 -13
- package/src/meter.ts +62 -0
- package/src/mind/articulation.ts +0 -1
- package/src/mind/attention.ts +169 -17
- package/src/mind/canonical.ts +43 -0
- package/src/mind/derivation.ts +473 -0
- package/src/mind/graph-search.ts +76 -34
- package/src/mind/match.ts +7 -3
- package/src/mind/mechanisms/alu.ts +0 -2
- package/src/mind/mechanisms/cast.ts +20 -22
- package/src/mind/mechanisms/confluence.ts +27 -13
- package/src/mind/mechanisms/cover.ts +17 -20
- package/src/mind/mechanisms/extraction.ts +13 -9
- package/src/mind/mechanisms/prefix-completion.ts +0 -1
- package/src/mind/mechanisms/recall.ts +39 -13
- package/src/mind/mechanisms/reference.ts +2 -3
- package/src/mind/mind.ts +6 -4
- package/src/mind/pipeline-mechanism.ts +7 -3
- package/src/mind/pipeline.ts +160 -52
- package/src/mind/primitives.ts +9 -1
- package/src/mind/rationale.ts +27 -23
- package/src/mind/reasoning.ts +227 -120
- package/src/mind/recognition.ts +4 -8
- package/src/mind/resonance.ts +19 -1
- package/src/mind/trace.ts +1 -0
- package/src/mind/traverse.ts +7 -5
- package/src/mind/types.ts +41 -15
- package/test/105-derive-through-reports-its-refusal.test.mjs +24 -0
- package/test/118-the-join-reaches-a-key-off-the-cut.test.mjs +74 -0
- package/test/119-the-work-does-not-grow-with-the-corpus.test.mjs +122 -0
- package/test/120-composition-is-consequence.test.mjs +132 -0
- package/test/121-the-extension-does-not-grow-with-the-corpus.test.mjs +128 -0
- package/test/122-the-climb-search-does-not-grow-with-the-corpus.test.mjs +117 -0
- package/test/123-the-paired-formulas-agree.test.mjs +90 -0
- package/test/125-the-post-grounding-branch-publishes-its-operand.test.mjs +51 -0
- package/test/126-the-pipeline-does-not-name-mechanisms.test.mjs +42 -0
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +83 -0
- package/test/129-the-trace-payload-shape.test.mjs +164 -0
- package/test/133-the-decision-point-renders-the-state.test.mjs +204 -0
- package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
- package/test/135-one-law-any-producer.test.mjs +289 -0
- package/test/136-the-two-named-limits.test.mjs +205 -0
- package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
- package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
- package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
- package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
- package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
- package/test/32-confluence.test.mjs +68 -0
- package/test/36-already-answered-fusion.test.mjs +20 -2
- package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
- package/test/38-reason-restate-guard.test.mjs +28 -2
- package/test/43-cast-analog-seat.test.mjs +10 -0
- package/test/55-cost-meter.test.mjs +862 -0
package/src/mind/reasoning.ts
CHANGED
|
@@ -8,29 +8,31 @@ import { bytesEqual, indexOf } from "../bytes.js";
|
|
|
8
8
|
import type { Attention, MindContext } from "./types.js";
|
|
9
9
|
import { resolve } from "./primitives.js";
|
|
10
10
|
import { corpusN, hubBound } from "./traverse.js";
|
|
11
|
-
import { follow, haloSiblings, project } from "./match.js";
|
|
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
|
|
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,
|
|
32
|
-
* then follows the pivot's continuation to the next fact.
|
|
33
|
-
*
|
|
28
|
+
* then follows the pivot's continuation to the next fact. **The chain ends
|
|
29
|
+
* when it STOPS, never when a count runs out**: every exit is a refusal (no
|
|
30
|
+
* pivot, no forward step, no question material carried) and the walk is bounded
|
|
31
|
+
* by the material and the graph — `consumed` refuses to revisit a node. There
|
|
32
|
+
* is no hop allowance, so this doc deliberately names no `cfg` capacity: the
|
|
33
|
+
* cover prices every hop at `STEP` and lets the search decide the depth, and a
|
|
34
|
+
* second count here would be a second decision about the same thing.
|
|
35
|
+
* `preConsumed` carries node ids already
|
|
34
36
|
* spoken for by the grounding stage (cover/extract/CAST). `voiced` carries
|
|
35
37
|
* the BYTES of the anchors a mechanism declared it voiced (its `used` set),
|
|
36
38
|
* when it declared one — see the pivot's own containment rule. `pre` is the
|
|
@@ -39,22 +41,25 @@ export function restatesQuery(query: Uint8Array, bytes: Uint8Array): boolean {
|
|
|
39
41
|
export async function reason(
|
|
40
42
|
ctx: MindContext,
|
|
41
43
|
query: Uint8Array,
|
|
42
|
-
|
|
44
|
+
d0: DerivationState,
|
|
43
45
|
preConsumed: ReadonlySet<number>,
|
|
44
46
|
pre: Precomputed,
|
|
45
47
|
voiced: readonly Uint8Array[] = [],
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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;
|
|
51
54
|
// Echo guard: a query that is ITSELF a learnt continuation (some context's
|
|
52
55
|
// answer) is being asked back at the system — hopping forward from it would
|
|
53
56
|
// chain through the very fact that produced it and echo the conversation
|
|
54
57
|
// back. The grounded answer alone is the honest read-out. Deliberately a
|
|
55
58
|
// broad structural gate; pinned by test/31-audit.
|
|
56
59
|
const qId = pre.queryResolved;
|
|
57
|
-
if (qId !== null && ctx.store.prevCount(qId) > 0)
|
|
60
|
+
if (qId !== null && ctx.store.prevCount(qId) > 0) {
|
|
61
|
+
return d0;
|
|
62
|
+
}
|
|
58
63
|
|
|
59
64
|
// Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
|
|
60
65
|
// the hub bound, via the store's LIMITed edge reads: a common continuation's
|
|
@@ -73,7 +78,7 @@ export async function reason(
|
|
|
73
78
|
// is nothing left to chain for.
|
|
74
79
|
//
|
|
75
80
|
// Every stopping condition in the loop below judges the ANSWER (`consumed` /
|
|
76
|
-
// `
|
|
81
|
+
// the law's `restates` / `bytesEqual`); none asks whether the QUESTION was
|
|
77
82
|
// satisfied. So a single-hop question whose answer happens to name another
|
|
78
83
|
// learnt context extends past a correct answer and REPLACES it:
|
|
79
84
|
//
|
|
@@ -113,7 +118,7 @@ export async function reason(
|
|
|
113
118
|
? null
|
|
114
119
|
: ctx.store.prevFirst(groundedId, bound);
|
|
115
120
|
if (qId !== null && groundedPrev !== null && groundedPrev.includes(qId)) {
|
|
116
|
-
return
|
|
121
|
+
return d0;
|
|
117
122
|
}
|
|
118
123
|
|
|
119
124
|
const consumed = new Set<number>();
|
|
@@ -155,21 +160,66 @@ export async function reason(
|
|
|
155
160
|
await preconsume();
|
|
156
161
|
}
|
|
157
162
|
|
|
158
|
-
|
|
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
|
+
//
|
|
172
|
+
// NO ALLOWANCE: THE CHAIN ENDS WHEN IT STOPS. Every exit below is the law —
|
|
173
|
+
// no pivot, no forward step, no question material carried — and the walk is
|
|
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).
|
|
179
|
+
//
|
|
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
|
|
183
|
+
// `pivotSteps` on the chain fixtures.
|
|
159
184
|
const qv = pre.guide; // the response-wide guide IS the query's gist
|
|
160
|
-
|
|
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;
|
|
161
196
|
const startedFrom = answer;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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.
|
|
165
212
|
const curId = hop === 0 ? groundedId : resolve(ctx, cur);
|
|
166
213
|
consumeNode(curId, hop === 0 ? groundedPrev ?? undefined : undefined);
|
|
214
|
+
hop++;
|
|
167
215
|
|
|
168
216
|
// Forward-absorb: follow only UNCONSUMED continuations. The gate below
|
|
169
|
-
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
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.
|
|
173
223
|
if (
|
|
174
224
|
curId !== null &&
|
|
175
225
|
ctx.store.nextFirst(curId, bound).some((n) => !consumed.has(n))
|
|
@@ -179,96 +229,122 @@ export async function reason(
|
|
|
179
229
|
if (
|
|
180
230
|
fwd !== null && !bytesEqual(fwd, cur) &&
|
|
181
231
|
(fwdId === null || !consumed.has(fwdId)) &&
|
|
182
|
-
!
|
|
232
|
+
!restates(query, fwd, 0, { proper: true })
|
|
183
233
|
) {
|
|
184
234
|
consumeAll(curId);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
]);
|
|
188
|
-
ctx.trace?.step(
|
|
189
|
-
"absorbForward",
|
|
190
|
-
[rItem(cur, "answer", curId)],
|
|
191
|
-
[rItem(fwd, "answer", resolve(ctx, fwd) ?? undefined)],
|
|
192
|
-
"the answer is itself a learnt fact — follow its continuation to the fixpoint",
|
|
193
|
-
);
|
|
194
|
-
cur = fwd;
|
|
195
|
-
continue;
|
|
235
|
+
pending = { kind: "absorb", cur, curId, fwd };
|
|
236
|
+
return { product: fwd, contains: true, reaches: true, cost: STEP };
|
|
196
237
|
}
|
|
197
238
|
}
|
|
198
239
|
|
|
199
|
-
// Pivot:
|
|
240
|
+
// Pivot: the longest unconsumed learnt context the answer contains.
|
|
200
241
|
consumeAll(curId);
|
|
201
242
|
const pivot = await pivotInto(ctx, cur, consumed, voiced);
|
|
202
|
-
if (pivot === null)
|
|
203
|
-
|
|
243
|
+
if (pivot === null) return null;
|
|
204
244
|
const fc = await follow(ctx, pivot, qv);
|
|
205
245
|
consumeAll(pivot);
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
// the
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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);
|
|
235
275
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// that an extension was declined for want of question material, and
|
|
242
|
-
// where the next person sees why the chain stopped here. Measured with
|
|
243
|
-
// the check disabled, test/110 and test/116 fail — so this brake is the
|
|
244
|
-
// only thing keeping the extension honest until the pivot reports its
|
|
245
|
-
// own accounted spans and the ladder can judge it instead.
|
|
246
|
-
const left = uncovered.reduce((n, [a, b]) => n + (b - a), 0);
|
|
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") {
|
|
247
281
|
ctx.trace?.step(
|
|
248
|
-
"
|
|
249
|
-
[rItem(cur, "answer"
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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",
|
|
253
294
|
);
|
|
254
|
-
break;
|
|
255
295
|
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
"
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
|
|
319
|
+
// INSTRUMENTATION ONLY — the extension's two facts, untraced (meter.ts
|
|
320
|
+
// contract 1: a counter never reaches a decision). They are what a caller
|
|
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;
|
|
326
|
+
if (ctx.meter) {
|
|
327
|
+
ctx.meter.reasonSteps += steps;
|
|
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),
|
|
264
334
|
);
|
|
265
|
-
cur = fc;
|
|
266
335
|
}
|
|
267
336
|
t?.done(
|
|
268
|
-
[rItem(
|
|
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.
|
|
269
345
|
"the multi-hop chain's fixpoint",
|
|
270
346
|
);
|
|
271
|
-
return
|
|
347
|
+
return closed_;
|
|
272
348
|
}
|
|
273
349
|
|
|
274
350
|
/** Fuse independent points of attention into one answer (multi-topic).
|
|
@@ -278,7 +354,7 @@ export async function reason(
|
|
|
278
354
|
export async function fuseAttention(
|
|
279
355
|
ctx: MindContext,
|
|
280
356
|
query: Uint8Array,
|
|
281
|
-
|
|
357
|
+
state: DerivationState,
|
|
282
358
|
pre: Precomputed,
|
|
283
359
|
/** True when `primary` never touched the consensus climb at all — e.g. a
|
|
284
360
|
* pure ALU computation, which has no anchor of its own. commitVotes
|
|
@@ -293,8 +369,13 @@ export async function fuseAttention(
|
|
|
293
369
|
* which is the layer that knows how a given grounding records its evidence;
|
|
294
370
|
* fuseAttention just reads a position from it. Empty or absent preserves
|
|
295
371
|
* the original behaviour exactly. */
|
|
296
|
-
primarySpans: ReadonlyArray<
|
|
297
|
-
): 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;
|
|
298
379
|
// When the answer is structurally drawn from the query itself
|
|
299
380
|
// (extraction), it already spans all the query's pieces — fusion
|
|
300
381
|
// would only add noise from unrelated stored contexts. The gate is
|
|
@@ -302,7 +383,7 @@ export async function fuseAttention(
|
|
|
302
383
|
// byte run): the old sparse-subsequence test was trivially satisfied by
|
|
303
384
|
// short answers over long queries, silently starving multi-topic queries
|
|
304
385
|
// of fusion.
|
|
305
|
-
if (containsSpan(ctx, query, primary)) return
|
|
386
|
+
if (containsSpan(ctx, query, primary)) return state;
|
|
306
387
|
|
|
307
388
|
// The committed points of attention ARE the shared climb's roots (same
|
|
308
389
|
// query, same k, same DF mode) — read them from Precomputed instead of
|
|
@@ -345,7 +426,7 @@ export async function fuseAttention(
|
|
|
345
426
|
const lonePromotes = unclimbed && forest.length === 1 &&
|
|
346
427
|
forest[0].breadth > 0.5 && independentOfPrimary(forest[0]);
|
|
347
428
|
if (forest.length === 0 || (forest.length <= 1 && !lonePromotes)) {
|
|
348
|
-
return
|
|
429
|
+
return state;
|
|
349
430
|
}
|
|
350
431
|
|
|
351
432
|
// WHERE THE QUERY ASKED FOR IT. The sort below orders the fused pieces by
|
|
@@ -428,7 +509,13 @@ export async function fuseAttention(
|
|
|
428
509
|
// the root resonates.
|
|
429
510
|
const cont = await follow(ctx, root.anchor, qv);
|
|
430
511
|
if (
|
|
431
|
-
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 })
|
|
432
519
|
) {
|
|
433
520
|
ctx.trace?.step(
|
|
434
521
|
"alreadyAnswered",
|
|
@@ -448,7 +535,7 @@ export async function fuseAttention(
|
|
|
448
535
|
[rItem(primary, "answer")],
|
|
449
536
|
"no further independent point grounded",
|
|
450
537
|
);
|
|
451
|
-
return
|
|
538
|
+
return state;
|
|
452
539
|
}
|
|
453
540
|
|
|
454
541
|
pieces.sort((a, b) => a.start - b.start);
|
|
@@ -462,9 +549,29 @@ export async function fuseAttention(
|
|
|
462
549
|
[rItem(out, "answer", resolve(ctx, out) ?? undefined)],
|
|
463
550
|
`fused ${pieces.length} independent points of attention into one answer`,
|
|
464
551
|
);
|
|
465
|
-
return
|
|
552
|
+
// THE FACT IS THE FUSED ANSWER, not the call: every early return above hands
|
|
553
|
+
// back `primary` untouched. Untraced on purpose (meter.ts contract 1).
|
|
554
|
+
if (ctx.meter) ctx.meter.fuseRuns++;
|
|
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;
|
|
466
577
|
}
|
|
467
|
-
|
|
468
|
-
// (resonance.js is already a static dependency above — `bridge` — so the old
|
|
469
|
-
// dynamic import of pivotInto guarded against a cycle that does not exist.)
|
|
470
|
-
import { containsSpan } from "./match.js";
|
package/src/mind/recognition.ts
CHANGED
|
@@ -535,7 +535,10 @@ function recogniseImpl(ctx: MindContext, bytes: Uint8Array): Recognition {
|
|
|
535
535
|
// emitted, so a caller can retry a trimmed edge on the miss path only.
|
|
536
536
|
if (end - start < W) return false;
|
|
537
537
|
if (flatProbe(start, end) === null) {
|
|
538
|
-
if (!canonBudget)
|
|
538
|
+
if (!canonBudget) {
|
|
539
|
+
if (ctx.meter) ctx.meter.canonProbesDenied++;
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
539
542
|
if (!canonAdmits(start, end)) return false;
|
|
540
543
|
}
|
|
541
544
|
const id = resolveSpan(start, end);
|
|
@@ -556,13 +559,6 @@ function recogniseImpl(ctx: MindContext, bytes: Uint8Array): Recognition {
|
|
|
556
559
|
// endpoints in order and running out partway along the query. A form
|
|
557
560
|
// longer than that is out of this tier's reach — but so is a form the
|
|
558
561
|
// chain cannot span, and that is exactly the trade the budget prices.
|
|
559
|
-
// The factor is chainReach(W), the same W² scale the chain already
|
|
560
|
-
// trusts; no new constant.
|
|
561
|
-
// The factor is chainReach(W) — the same W² scale the chain itself
|
|
562
|
-
// trusts — so the cap is derived from the fold's geometry, never tuned.
|
|
563
|
-
// (It was briefly an environment variable while the cost was being
|
|
564
|
-
// measured; an env-read here would make inference non-reproducible,
|
|
565
|
-
// which the determinism contract forbids outright.)
|
|
566
562
|
// The factor is chainReach(W) — the same W² scale the chain itself
|
|
567
563
|
// trusts — so the cap is derived from the fold's geometry, never tuned.
|
|
568
564
|
// (It was briefly an environment variable while the cost was being
|
package/src/mind/resonance.ts
CHANGED
|
@@ -329,7 +329,14 @@ export async function pivotInto(
|
|
|
329
329
|
consumed: ReadonlySet<number>,
|
|
330
330
|
voiced: readonly Uint8Array[] = [],
|
|
331
331
|
): Promise<number | null> {
|
|
332
|
-
|
|
332
|
+
// The pivot's OWN shortlist capacity — not `recallQueryK`: they are different
|
|
333
|
+
// quantities (a mechanical sweep's probe budget vs the bridge's candidate-read
|
|
334
|
+
// allowance), and one number serving both means tightening either silently
|
|
335
|
+
// starves the other. The reason is the duplication, NOT a measured strangle:
|
|
336
|
+
// an earlier version of this comment claimed "at recallQueryK 1 the pivot finds
|
|
337
|
+
// no pivot", and that was probed and is FALSE on test/23's fixture — the
|
|
338
|
+
// strangle is fixture-specific, so it is not the evidence for this split.
|
|
339
|
+
const k = ctx.cfg.pivotProbeK;
|
|
333
340
|
// ONE perception of the answer, shared by the probe budget and the walk —
|
|
334
341
|
// this used to fold the same bytes twice, back to back, on every hop.
|
|
335
342
|
const tree = perceive(ctx, answer);
|
|
@@ -365,6 +372,17 @@ export async function pivotInto(
|
|
|
365
372
|
}
|
|
366
373
|
for (const c of n.kids) queue.push(c); // breadth-first: larger regions first
|
|
367
374
|
}
|
|
375
|
+
// The sweep's two FACTS, untraced (meter.ts contract 1: a counter never
|
|
376
|
+
// reaches a decision). `probes` is the work done; the shortfall is the
|
|
377
|
+
// capacity the cap withheld — the branches the breadth-first order never got
|
|
378
|
+
// to. Whether that withheld anything that mattered is NOT said here: the
|
|
379
|
+
// order spends the largest regions first, and recognition below still
|
|
380
|
+
// contributes every exact containment candidate regardless of the budget.
|
|
381
|
+
if (ctx.meter) {
|
|
382
|
+
ctx.meter.pivotProbes += probes;
|
|
383
|
+
const unprobed = branchCount - probeCap;
|
|
384
|
+
if (unprobed > 0) ctx.meter.pivotBranchesUnprobed += unprobed;
|
|
385
|
+
}
|
|
368
386
|
// THE FULL recognition, memo-shared with every other reader of these bytes.
|
|
369
387
|
// A "skip the edge trims here" variant was refuted (see recognise's own
|
|
370
388
|
// note): those trims are what find a WHOLE trained form embedded at an
|
package/src/mind/trace.ts
CHANGED
package/src/mind/traverse.ts
CHANGED
|
@@ -10,6 +10,13 @@ import { cosine, Vec } from "../vec.js";
|
|
|
10
10
|
import type { AncestorReach, MindContext, SaturationStop } from "./types.js";
|
|
11
11
|
import { gistOf, read } from "./primitives.js";
|
|
12
12
|
import { canonicalWindows, leafIdPrefix, leafIdRun } from "./canonical.js";
|
|
13
|
+
// Imported at the TOP, where every other import is. They used to sit 800 lines
|
|
14
|
+
// down under a note claiming the position mattered ("before trace module is
|
|
15
|
+
// loaded") — it does not: an ES module's static imports are HOISTED, so the
|
|
16
|
+
// file's line order never decides load order. The note described an intention
|
|
17
|
+
// the runtime does not honour; the imports move and the claim goes.
|
|
18
|
+
import { decodeText } from "./rationale.js";
|
|
19
|
+
import type { RationaleItem } from "./rationale.js";
|
|
13
20
|
|
|
14
21
|
// ── Session structural memo ─────────────────────────────────────────────
|
|
15
22
|
//
|
|
@@ -824,11 +831,6 @@ export function chooseAmong(
|
|
|
824
831
|
: { id: candidates[0], score: -Infinity };
|
|
825
832
|
}
|
|
826
833
|
|
|
827
|
-
// ── Trace shim (used by chooseNext before trace module is loaded) ────────
|
|
828
|
-
|
|
829
|
-
import { decodeText } from "./rationale.js";
|
|
830
|
-
import type { RationaleItem } from "./rationale.js";
|
|
831
|
-
|
|
832
834
|
function rItemShort(
|
|
833
835
|
ctx: MindContext,
|
|
834
836
|
id: number,
|