@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
|
@@ -6,43 +6,41 @@ import { rItem, rNode } from "./trace.js";
|
|
|
6
6
|
import { bytesEqual, indexOf } from "../bytes.js";
|
|
7
7
|
import { resolve } from "./primitives.js";
|
|
8
8
|
import { hubBound } from "./traverse.js";
|
|
9
|
-
import { follow, haloSiblings, project } from "./match.js";
|
|
9
|
+
import { containsSpan, follow, haloSiblings, project } from "./match.js";
|
|
10
10
|
import { joinWithBridge, pivotInto } from "./resonance.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* its OWN grounding candidates (tier 1's `restates`, tier 2's subspan
|
|
15
|
-
* check, tier 0b's argument-binding subspan check) — every mechanism that
|
|
16
|
-
* walks a LEARNT CONTINUATION EDGE past an already-vetted grounding
|
|
17
|
-
* (reason()'s own hops below, and CAST's `projectCounterfactual` seat
|
|
18
|
-
* substitution — see cast.ts) needs the same guard applied to what the
|
|
19
|
-
* walk turns up, since `follow()`/`chooseNext`/`pivotInto` know nothing of
|
|
20
|
-
* the query at all — only of what structurally continues what. */
|
|
21
|
-
export function restatesQuery(query, bytes) {
|
|
22
|
-
return bytes.length < query.length && indexOf(query, bytes, 0) >= 0;
|
|
23
|
-
}
|
|
11
|
+
import { admissible, advance } from "./derivation.js";
|
|
12
|
+
import { closure, restates, unaccountedBytes, } from "./derivation.js";
|
|
13
|
+
import { STEP } from "./graph-search.js";
|
|
24
14
|
/** Extend a grounded answer forward across facts (multi-hop reasoning).
|
|
25
15
|
* Pivots on the longest unconsumed learnt context each answer contains,
|
|
26
|
-
* then follows the pivot's continuation to the next fact.
|
|
27
|
-
*
|
|
16
|
+
* then follows the pivot's continuation to the next fact. **The chain ends
|
|
17
|
+
* when it STOPS, never when a count runs out**: every exit is a refusal (no
|
|
18
|
+
* pivot, no forward step, no question material carried) and the walk is bounded
|
|
19
|
+
* by the material and the graph — `consumed` refuses to revisit a node. There
|
|
20
|
+
* is no hop allowance, so this doc deliberately names no `cfg` capacity: the
|
|
21
|
+
* cover prices every hop at `STEP` and lets the search decide the depth, and a
|
|
22
|
+
* second count here would be a second decision about the same thing.
|
|
23
|
+
* `preConsumed` carries node ids already
|
|
28
24
|
* spoken for by the grounding stage (cover/extract/CAST). `voiced` carries
|
|
29
25
|
* the BYTES of the anchors a mechanism declared it voiced (its `used` set),
|
|
30
26
|
* when it declared one — see the pivot's own containment rule. `pre` is the
|
|
31
27
|
* response's shared pre-computation — the post-grounding stages read the
|
|
32
28
|
* same container the mechanisms did. */
|
|
33
|
-
export async function reason(ctx, query,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
export async function reason(ctx, query, d0, preConsumed, pre, voiced = []) {
|
|
30
|
+
const answer = d0.product;
|
|
31
|
+
/** The query material the GROUNDING left uncovered — the state's own
|
|
32
|
+
* remainder. Only the reasoner's OWN extensions are judged against it; a
|
|
33
|
+
* mechanism carrying its own `used` set owns its shape. */
|
|
34
|
+
const uncovered = d0.remainder;
|
|
38
35
|
// Echo guard: a query that is ITSELF a learnt continuation (some context's
|
|
39
36
|
// answer) is being asked back at the system — hopping forward from it would
|
|
40
37
|
// chain through the very fact that produced it and echo the conversation
|
|
41
38
|
// back. The grounded answer alone is the honest read-out. Deliberately a
|
|
42
39
|
// broad structural gate; pinned by test/31-audit.
|
|
43
40
|
const qId = pre.queryResolved;
|
|
44
|
-
if (qId !== null && ctx.store.prevCount(qId) > 0)
|
|
45
|
-
return
|
|
41
|
+
if (qId !== null && ctx.store.prevCount(qId) > 0) {
|
|
42
|
+
return d0;
|
|
43
|
+
}
|
|
46
44
|
// Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
|
|
47
45
|
// the hub bound, via the store's LIMITed edge reads: a common continuation's
|
|
48
46
|
// reverse fan-in (and a hub context's forward fan-out) is corpus-sized, and
|
|
@@ -59,7 +57,7 @@ uncovered = []) {
|
|
|
59
57
|
// is nothing left to chain for.
|
|
60
58
|
//
|
|
61
59
|
// Every stopping condition in the loop below judges the ANSWER (`consumed` /
|
|
62
|
-
// `
|
|
60
|
+
// the law's `restates` / `bytesEqual`); none asks whether the QUESTION was
|
|
63
61
|
// satisfied. So a single-hop question whose answer happens to name another
|
|
64
62
|
// learnt context extends past a correct answer and REPLACES it:
|
|
65
63
|
//
|
|
@@ -99,7 +97,7 @@ uncovered = []) {
|
|
|
99
97
|
? null
|
|
100
98
|
: ctx.store.prevFirst(groundedId, bound);
|
|
101
99
|
if (qId !== null && groundedPrev !== null && groundedPrev.includes(qId)) {
|
|
102
|
-
return
|
|
100
|
+
return d0;
|
|
103
101
|
}
|
|
104
102
|
const consumed = new Set();
|
|
105
103
|
/** `prev` lets a caller hand in an already-read reverse-edge list — hop 0
|
|
@@ -143,106 +141,158 @@ uncovered = []) {
|
|
|
143
141
|
else {
|
|
144
142
|
await preconsume();
|
|
145
143
|
}
|
|
146
|
-
|
|
144
|
+
// ── THE WALK, AS THE LAW'S ───────────────────────────────────────────
|
|
145
|
+
//
|
|
146
|
+
// This function no longer decides whether a step may be taken: it OFFERS a
|
|
147
|
+
// continuation — a forward step from the answer's own learnt edge, or a pivot
|
|
148
|
+
// on a learnt context the answer contains — and `derivation.ts`'s law admits
|
|
149
|
+
// or refuses it. The state it offers against is the pipeline's own: the
|
|
150
|
+
// grounding's remainder (what the question still owes), its accounting, its
|
|
151
|
+
// cost.
|
|
152
|
+
//
|
|
153
|
+
// NO ALLOWANCE: THE CHAIN ENDS WHEN IT STOPS. Every exit below is the law —
|
|
154
|
+
// no pivot, no forward step, no question material carried — and the walk is
|
|
155
|
+
// bounded by the material and the graph rather than by a count: the law
|
|
156
|
+
// requires each step to engage what is left (or to move to new structure),
|
|
157
|
+
// and `consumed` refuses to revisit a node. `recallQueryK` no longer bounds
|
|
158
|
+
// the reasoner here; it keeps its other roles (the bridge's candidate reads,
|
|
159
|
+
// the pivot's probe budget, the resonance limits).
|
|
160
|
+
//
|
|
161
|
+
// Measured before removing the allowance: test/89 — the corpus-cost guard, the
|
|
162
|
+
// heaviest case in the suite — is green and no slower without it (27 s against
|
|
163
|
+
// 30 s); and raising it from 12 to 200 changed neither the answer nor
|
|
164
|
+
// `pivotSteps` on the chain fixtures.
|
|
147
165
|
const qv = pre.guide; // the response-wide guide IS the query's gist
|
|
148
|
-
|
|
166
|
+
const W = ctx.space.maxGroup;
|
|
167
|
+
// WHOSE EXTENSION IS THIS? `voiced` is what the mechanism WITHHELD (the
|
|
168
|
+
// pipeline sends the used anchors' CONTINUATIONS, not their bytes), so a
|
|
169
|
+
// non-empty `voiced` means exactly what that note says: the grounding came
|
|
170
|
+
// from a mechanism that carries its own short `used` set (cast/join) and
|
|
171
|
+
// therefore OWNS the shape of its answer. The further terms inside such a
|
|
172
|
+
// seat are legitimately followable (test/29 C3's `Mona Lisa` lives inside the
|
|
173
|
+
// voiced seat and leads on to a fact about neither analog), which the law
|
|
174
|
+
// expresses as the identity species of progress — `moves` — declared by the
|
|
175
|
+
// reporter and never inferred from the mechanism's name.
|
|
176
|
+
const producerOwnsShape = voiced.length > 0;
|
|
149
177
|
const startedFrom = answer;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
178
|
+
let hop = 0;
|
|
179
|
+
let t;
|
|
180
|
+
// WHAT THE OFFERED STEP WOULD BE. The law decides, so the accepted step is
|
|
181
|
+
// traced where the decision went (`onTaken`) and a refused one is reported as
|
|
182
|
+
// the law's refusal (`onRefused`) — a step is never reported before it is
|
|
183
|
+
// admitted.
|
|
184
|
+
let pending = null;
|
|
185
|
+
const offer = async (d) => {
|
|
186
|
+
const cur = d.product;
|
|
187
|
+
// The first step's `cur` IS the grounding's product, so the guard above
|
|
188
|
+
// already resolved it and read its reverse edges — reuse both.
|
|
153
189
|
const curId = hop === 0 ? groundedId : resolve(ctx, cur);
|
|
154
190
|
consumeNode(curId, hop === 0 ? groundedPrev ?? undefined : undefined);
|
|
191
|
+
hop++;
|
|
155
192
|
// Forward-absorb: follow only UNCONSUMED continuations. The gate below
|
|
156
|
-
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
193
|
+
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows nothing
|
|
194
|
+
// of `consumed` and may still walk to a consumed fixpoint — absorbing it
|
|
195
|
+
// would repeat content the grounding stage already spoke for, so a consumed
|
|
196
|
+
// fixpoint falls through to the pivot step instead. Offered with `moves`:
|
|
197
|
+
// completing the answer's OWN learnt form is an identity step, not a claim
|
|
198
|
+
// about the asker's material.
|
|
160
199
|
if (curId !== null &&
|
|
161
200
|
ctx.store.nextFirst(curId, bound).some((n) => !consumed.has(n))) {
|
|
162
201
|
const fwd = await follow(ctx, curId, qv);
|
|
163
202
|
const fwdId = fwd !== null ? resolve(ctx, fwd) : null;
|
|
164
203
|
if (fwd !== null && !bytesEqual(fwd, cur) &&
|
|
165
204
|
(fwdId === null || !consumed.has(fwdId)) &&
|
|
166
|
-
!
|
|
205
|
+
!restates(query, fwd, 0, { proper: true })) {
|
|
167
206
|
consumeAll(curId);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
]);
|
|
171
|
-
ctx.trace?.step("absorbForward", [rItem(cur, "answer", curId)], [rItem(fwd, "answer", resolve(ctx, fwd) ?? undefined)], "the answer is itself a learnt fact — follow its continuation to the fixpoint");
|
|
172
|
-
cur = fwd;
|
|
173
|
-
continue;
|
|
207
|
+
pending = { kind: "absorb", cur, curId, fwd };
|
|
208
|
+
return { product: fwd, contains: true, reaches: true, cost: STEP };
|
|
174
209
|
}
|
|
175
210
|
}
|
|
176
|
-
// Pivot:
|
|
211
|
+
// Pivot: the longest unconsumed learnt context the answer contains.
|
|
177
212
|
consumeAll(curId);
|
|
178
213
|
const pivot = await pivotInto(ctx, cur, consumed, voiced);
|
|
179
214
|
if (pivot === null)
|
|
180
|
-
|
|
215
|
+
return null;
|
|
181
216
|
const fc = await follow(ctx, pivot, qv);
|
|
182
217
|
consumeAll(pivot);
|
|
183
|
-
if (fc === null || bytesEqual(fc, cur) ||
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
// `voiced` is what the mechanism WITHHELD (the pipeline sends the used
|
|
188
|
-
// anchors' CONTINUATIONS, not their bytes — see pipeline's own note), so a
|
|
189
|
-
// non-empty `voiced` means exactly what that note says: the grounding came
|
|
190
|
-
// from a mechanism that carries its own short `used` set (cast/join) and
|
|
191
|
-
// therefore owns the shape of its answer. The further terms inside such a
|
|
192
|
-
// seat are legitimately followable — test/29 C3's `Mona Lisa` lives inside
|
|
193
|
-
// the voiced seat and leads on to a fact about neither analog.
|
|
194
|
-
//
|
|
195
|
-
// Every other grounding is ordinary, and an extension of it is the
|
|
196
|
-
// reasoner's own inference: it is taken only while question material the
|
|
197
|
-
// grounding left uncovered remains AND the step carries some of it, judged
|
|
198
|
-
// by the mind's own line between chance and evidence — one W-byte window,
|
|
199
|
-
// no word notion, no character class, no threshold. Measured: the drift's
|
|
200
|
-
// second step (`the Eiffel Tower is in Paris` after `Paris is famous for
|
|
201
|
-
// the Eiffel Tower`) carries no window of `" famous for"` and is refused,
|
|
202
|
-
// while the first carries it. Terminates by a real argument: the uncovered
|
|
203
|
-
// material is finite and each taken extension must carry some of it.
|
|
204
|
-
const producerOwnsShape = voiced.length > 0;
|
|
205
|
-
if (!producerOwnsShape && uncovered.length > 0) {
|
|
206
|
-
const W = ctx.space.maxGroup;
|
|
207
|
-
let progress = false;
|
|
208
|
-
for (const [a, b] of uncovered) {
|
|
209
|
-
for (let i = a; i + W <= b && !progress; i++) {
|
|
210
|
-
if (indexOf(fc, query.subarray(i, i + W), 0) >= 0)
|
|
211
|
-
progress = true;
|
|
212
|
-
}
|
|
213
|
-
if (progress)
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
if (!progress) {
|
|
217
|
-
// THE BRAKE, MADE VISIBLE. The reasoner declines a step that carries
|
|
218
|
-
// none of the material the grounding left uncovered — the drift the
|
|
219
|
-
// extension tests pin. A refusal that leaves no trace is the kind of
|
|
220
|
-
// silent cut AGENTS §6 forbids: the rationale is where a reader learns
|
|
221
|
-
// that an extension was declined for want of question material, and
|
|
222
|
-
// where the next person sees why the chain stopped here. Measured with
|
|
223
|
-
// the check disabled, test/110 and test/116 fail — so this brake is the
|
|
224
|
-
// only thing keeping the extension honest until the pivot reports its
|
|
225
|
-
// own accounted spans and the ladder can judge it instead.
|
|
226
|
-
const left = uncovered.reduce((n, [a, b]) => n + (b - a), 0);
|
|
227
|
-
ctx.trace?.step("pivotRefused", [rItem(cur, "answer"), rItem(query, "query")], uncovered.map(([a, b]) => rItem(query.subarray(a, b), "uncovered")), `the step carries none of the question material the grounding left ` +
|
|
228
|
-
`uncovered (${left} byte(s) in ${uncovered.length} span(s)) — refused`);
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
218
|
+
if (fc === null || bytesEqual(fc, cur) ||
|
|
219
|
+
restates(query, fc, 0, { proper: true })) {
|
|
220
|
+
return null;
|
|
231
221
|
}
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
pending = { kind: "pivot", cur, pivot, fc };
|
|
223
|
+
// Offered with the identity species ONLY when the grounding declared what it
|
|
224
|
+
// speaks for; otherwise the law requires this step to carry question
|
|
225
|
+
// material the grounding left unaccounted, which is the drift the extension
|
|
226
|
+
// tests pin — refused by the law's own measure, not by a private window
|
|
227
|
+
// test.
|
|
228
|
+
return {
|
|
229
|
+
product: fc,
|
|
230
|
+
contains: true,
|
|
231
|
+
reaches: producerOwnsShape,
|
|
232
|
+
cost: STEP,
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
const closed_ = await closure(d0, query, W, offer, (before, after) => {
|
|
236
|
+
if (ctx.meter) {
|
|
237
|
+
ctx.meter.closureDrainedBytes += unaccountedBytes(before.remainder) -
|
|
238
|
+
unaccountedBytes(after.remainder);
|
|
239
|
+
}
|
|
240
|
+
const p = pending;
|
|
241
|
+
pending = null;
|
|
242
|
+
if (p === null)
|
|
243
|
+
return;
|
|
234
244
|
t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
|
|
235
|
-
|
|
236
|
-
|
|
245
|
+
if (p.kind === "absorb") {
|
|
246
|
+
ctx.trace?.step("absorbForward", [rItem(p.cur, "answer", p.curId ?? undefined)], [rItem(p.fwd, "answer", resolve(ctx, p.fwd) ?? undefined)], "the answer is itself a learnt fact — follow its continuation to the fixpoint");
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
if (ctx.meter)
|
|
250
|
+
ctx.meter.pivotSteps++;
|
|
251
|
+
ctx.trace?.step("pivotStep", [rItem(p.cur, "answer"), rNode(ctx, p.pivot, "pivot")], [rItem(p.fc, "answer", resolve(ctx, p.fc) ?? undefined)], "pivot on the shared span this answer contains, then step forward across that fact");
|
|
252
|
+
}
|
|
253
|
+
}, (at) => {
|
|
254
|
+
const p = pending;
|
|
255
|
+
pending = null;
|
|
256
|
+
if (p === null || p.kind !== "pivot")
|
|
257
|
+
return;
|
|
258
|
+
// THE BRAKE, MADE VISIBLE — and it is now the LAW's refusal, reported
|
|
259
|
+
// where it happened. The reasoner declines a step that carries none of
|
|
260
|
+
// the material the grounding left unaccounted. A refusal that leaves no
|
|
261
|
+
// trace is the kind of silent cut AGENTS §6 forbids: the rationale is
|
|
262
|
+
// where a reader learns that an extension was declined for want of
|
|
263
|
+
// question material, and where the next person sees why the chain stopped
|
|
264
|
+
// here. Measured with the check disabled, test/110 and test/116 fail.
|
|
265
|
+
const left = unaccountedBytes(at.remainder);
|
|
266
|
+
ctx.trace?.step("pivotRefused", [rItem(p.cur, "answer"), rItem(query, "query")], at.remainder.map(([a, b]) => rItem(query.subarray(a, b), "uncovered")), `the step carries none of the question material the grounding left ` +
|
|
267
|
+
`unaccounted (${left} byte(s) in ${at.remainder.length} span(s)) — refused`);
|
|
268
|
+
});
|
|
269
|
+
// INSTRUMENTATION ONLY — the extension's two facts, untraced (meter.ts
|
|
270
|
+
// contract 1: a counter never reaches a decision). They are what a caller
|
|
271
|
+
// needs to PRICE the extension instead of taking it unconditionally, and both
|
|
272
|
+
// are now read off the state the law advanced rather than accumulated beside
|
|
273
|
+
// it: the work it did is the cost it accumulated, and the material it carried
|
|
274
|
+
// is the accounting the law's witnesses added.
|
|
275
|
+
const steps = closed_.cost - d0.cost;
|
|
276
|
+
if (ctx.meter) {
|
|
277
|
+
ctx.meter.reasonSteps += steps;
|
|
278
|
+
// The accounting the law's witnesses added, summed by the law's own function
|
|
279
|
+
// over the tail of the state's list — and computed ONLY when a meter is
|
|
280
|
+
// attached: this used to slice and MAP a fresh array on every response, for a
|
|
281
|
+
// counter that usually does not exist. One allocation, under the meter.
|
|
282
|
+
ctx.meter.reasonCarriedBytes += unaccountedBytes(closed_.accounted.slice(d0.accounted.length));
|
|
237
283
|
}
|
|
238
|
-
t?.done([rItem(
|
|
239
|
-
|
|
284
|
+
t?.done([rItem(closed_.product, "answer", resolve(ctx, closed_.product) ?? undefined)],
|
|
285
|
+
// A FIXPOINT: no further step was offered, or the law refused the one that
|
|
286
|
+
// was. There is no allowance to exhaust, so the note is true by
|
|
287
|
+
// construction.
|
|
288
|
+
"the multi-hop chain's fixpoint");
|
|
289
|
+
return closed_;
|
|
240
290
|
}
|
|
241
291
|
/** Fuse independent points of attention into one answer (multi-topic).
|
|
242
292
|
* When the consensus climb finds more than one dominant point, each
|
|
243
293
|
* independent point grounds its own answer; they are bridged together
|
|
244
294
|
* by any learnt connector the graph holds between them. */
|
|
245
|
-
export async function fuseAttention(ctx, query,
|
|
295
|
+
export async function fuseAttention(ctx, query, state, pre,
|
|
246
296
|
/** True when `primary` never touched the consensus climb at all — e.g. a
|
|
247
297
|
* pure ALU computation, which has no anchor of its own. commitVotes
|
|
248
298
|
* ALWAYS admits the dominant root regardless of its vote (attention.ts:
|
|
@@ -257,6 +307,11 @@ unclimbed = false,
|
|
|
257
307
|
* fuseAttention just reads a position from it. Empty or absent preserves
|
|
258
308
|
* the original behaviour exactly. */
|
|
259
309
|
primarySpans = []) {
|
|
310
|
+
// THE STATE, NOT BARE BYTES: fusion is one more transition of the derivation
|
|
311
|
+
// the walk returned, so it reads that state's product and hands back a state.
|
|
312
|
+
// Its own structural gates stay here — this is the layer that can resolve
|
|
313
|
+
// those witnesses, and the law never re-derives one.
|
|
314
|
+
const primary = state.product;
|
|
260
315
|
// When the answer is structurally drawn from the query itself
|
|
261
316
|
// (extraction), it already spans all the query's pieces — fusion
|
|
262
317
|
// would only add noise from unrelated stored contexts. The gate is
|
|
@@ -265,7 +320,7 @@ primarySpans = []) {
|
|
|
265
320
|
// short answers over long queries, silently starving multi-topic queries
|
|
266
321
|
// of fusion.
|
|
267
322
|
if (containsSpan(ctx, query, primary))
|
|
268
|
-
return
|
|
323
|
+
return state;
|
|
269
324
|
// The committed points of attention ARE the shared climb's roots (same
|
|
270
325
|
// query, same k, same DF mode) — read them from Precomputed instead of
|
|
271
326
|
// re-climbing, so even a traced response pays for the climb once.
|
|
@@ -306,7 +361,7 @@ primarySpans = []) {
|
|
|
306
361
|
const lonePromotes = unclimbed && forest.length === 1 &&
|
|
307
362
|
forest[0].breadth > 0.5 && independentOfPrimary(forest[0]);
|
|
308
363
|
if (forest.length === 0 || (forest.length <= 1 && !lonePromotes)) {
|
|
309
|
-
return
|
|
364
|
+
return state;
|
|
310
365
|
}
|
|
311
366
|
// WHERE THE QUERY ASKED FOR IT. The sort below orders the fused pieces by
|
|
312
367
|
// query position, which is the whole point of the `start` field: a
|
|
@@ -382,7 +437,13 @@ primarySpans = []) {
|
|
|
382
437
|
// it fires on exact content-addressed recurrence, not on how strongly
|
|
383
438
|
// the root resonates.
|
|
384
439
|
const cont = await follow(ctx, root.anchor, qv);
|
|
385
|
-
if (cont !== null && cont.length > 0 &&
|
|
440
|
+
if (cont !== null && cont.length > 0 &&
|
|
441
|
+
// The law's positional reading: the caller knows the material it is
|
|
442
|
+
// looking for lies AT OR AFTER this root, which is the witness; the law
|
|
443
|
+
// owns the containment test itself. The `cont.length > 0` guard stays
|
|
444
|
+
// here because an EMPTY continuation indexes at every offset — the law
|
|
445
|
+
// has no opinion about whether "nothing" counts as said.
|
|
446
|
+
restates(query, cont, 0, { from: root.end })) {
|
|
386
447
|
ctx.trace?.step("alreadyAnswered", [rNode(ctx, root.anchor, "point", root.vote)], [rItem(cont, "continuation")], "this point's own learnt continuation already appears later in the query — already answered, not fused");
|
|
387
448
|
continue;
|
|
388
449
|
}
|
|
@@ -395,7 +456,7 @@ primarySpans = []) {
|
|
|
395
456
|
}
|
|
396
457
|
if (pieces.length === 1) {
|
|
397
458
|
t?.done([rItem(primary, "answer")], "no further independent point grounded");
|
|
398
|
-
return
|
|
459
|
+
return state;
|
|
399
460
|
}
|
|
400
461
|
pieces.sort((a, b) => a.start - b.start);
|
|
401
462
|
let out = pieces[0].bytes;
|
|
@@ -405,8 +466,31 @@ primarySpans = []) {
|
|
|
405
466
|
out = await joinWithBridge(ctx, out, pieces[i].bytes);
|
|
406
467
|
}
|
|
407
468
|
t?.done([rItem(out, "answer", resolve(ctx, out) ?? undefined)], `fused ${pieces.length} independent points of attention into one answer`);
|
|
408
|
-
return
|
|
469
|
+
// THE FACT IS THE FUSED ANSWER, not the call: every early return above hands
|
|
470
|
+
// back `primary` untouched. Untraced on purpose (meter.ts contract 1).
|
|
471
|
+
if (ctx.meter)
|
|
472
|
+
ctx.meter.fuseRuns++;
|
|
473
|
+
// A FUSION IS A TRANSITION, and the only one in the engine that can splice the
|
|
474
|
+
// QUESTION'S OWN material into the product. So it is OFFERED to the law rather
|
|
475
|
+
// than built by hand: the law reads the window the fused product holds — the
|
|
476
|
+
// same reading the carries admission uses, one definition — accounts for the
|
|
477
|
+
// span it carried, and lets the question's remainder drain on EVIDENCE. A
|
|
478
|
+
// fusion that carries nothing consumes nothing, because the reading finds no
|
|
479
|
+
// window; and `reaches` is declared only when the fusion composed another root,
|
|
480
|
+
// which is structure this derivation had not stood on.
|
|
481
|
+
const fusedT = {
|
|
482
|
+
product: out,
|
|
483
|
+
contains: true,
|
|
484
|
+
reaches: rest.length > 0,
|
|
485
|
+
cost: STEP,
|
|
486
|
+
};
|
|
487
|
+
const fusedWitness = admissible(state, fusedT, query, ctx.space.maxGroup);
|
|
488
|
+
if (fusedWitness === null)
|
|
489
|
+
return { ...state, product: out };
|
|
490
|
+
const fused = advance(state, fusedT, fusedWitness);
|
|
491
|
+
if (ctx.meter) {
|
|
492
|
+
ctx.meter.closureDrainedBytes += unaccountedBytes(state.remainder) -
|
|
493
|
+
unaccountedBytes(fused.remainder);
|
|
494
|
+
}
|
|
495
|
+
return fused;
|
|
409
496
|
}
|
|
410
|
-
// (resonance.js is already a static dependency above — `bridge` — so the old
|
|
411
|
-
// dynamic import of pivotInto guarded against a cycle that does not exist.)
|
|
412
|
-
import { containsSpan } from "./match.js";
|
|
@@ -519,8 +519,11 @@ function recogniseImpl(ctx, bytes) {
|
|
|
519
519
|
if (end - start < W)
|
|
520
520
|
return false;
|
|
521
521
|
if (flatProbe(start, end) === null) {
|
|
522
|
-
if (!canonBudget)
|
|
522
|
+
if (!canonBudget) {
|
|
523
|
+
if (ctx.meter)
|
|
524
|
+
ctx.meter.canonProbesDenied++;
|
|
523
525
|
return false;
|
|
526
|
+
}
|
|
524
527
|
if (!canonAdmits(start, end))
|
|
525
528
|
return false;
|
|
526
529
|
}
|
|
@@ -543,13 +546,6 @@ function recogniseImpl(ctx, bytes) {
|
|
|
543
546
|
// endpoints in order and running out partway along the query. A form
|
|
544
547
|
// longer than that is out of this tier's reach — but so is a form the
|
|
545
548
|
// chain cannot span, and that is exactly the trade the budget prices.
|
|
546
|
-
// The factor is chainReach(W), the same W² scale the chain already
|
|
547
|
-
// trusts; no new constant.
|
|
548
|
-
// The factor is chainReach(W) — the same W² scale the chain itself
|
|
549
|
-
// trusts — so the cap is derived from the fold's geometry, never tuned.
|
|
550
|
-
// (It was briefly an environment variable while the cost was being
|
|
551
|
-
// measured; an env-read here would make inference non-reproducible,
|
|
552
|
-
// which the determinism contract forbids outright.)
|
|
553
549
|
// The factor is chainReach(W) — the same W² scale the chain itself
|
|
554
550
|
// trusts — so the cap is derived from the fold's geometry, never tuned.
|
|
555
551
|
// (It was briefly an environment variable while the cost was being
|
|
@@ -249,7 +249,14 @@ export async function joinWithBridge(ctx, left, right) {
|
|
|
249
249
|
* overlap, so C3's genuine further hop — "Mona Lisa", a term inside the seat
|
|
250
250
|
* sentence but part of NEITHER analog — still fires. */
|
|
251
251
|
export async function pivotInto(ctx, answer, consumed, voiced = []) {
|
|
252
|
-
|
|
252
|
+
// The pivot's OWN shortlist capacity — not `recallQueryK`: they are different
|
|
253
|
+
// quantities (a mechanical sweep's probe budget vs the bridge's candidate-read
|
|
254
|
+
// allowance), and one number serving both means tightening either silently
|
|
255
|
+
// starves the other. The reason is the duplication, NOT a measured strangle:
|
|
256
|
+
// an earlier version of this comment claimed "at recallQueryK 1 the pivot finds
|
|
257
|
+
// no pivot", and that was probed and is FALSE on test/23's fixture — the
|
|
258
|
+
// strangle is fixture-specific, so it is not the evidence for this split.
|
|
259
|
+
const k = ctx.cfg.pivotProbeK;
|
|
253
260
|
// ONE perception of the answer, shared by the probe budget and the walk —
|
|
254
261
|
// this used to fold the same bytes twice, back to back, on every hop.
|
|
255
262
|
const tree = perceive(ctx, answer);
|
|
@@ -288,6 +295,18 @@ export async function pivotInto(ctx, answer, consumed, voiced = []) {
|
|
|
288
295
|
for (const c of n.kids)
|
|
289
296
|
queue.push(c); // breadth-first: larger regions first
|
|
290
297
|
}
|
|
298
|
+
// The sweep's two FACTS, untraced (meter.ts contract 1: a counter never
|
|
299
|
+
// reaches a decision). `probes` is the work done; the shortfall is the
|
|
300
|
+
// capacity the cap withheld — the branches the breadth-first order never got
|
|
301
|
+
// to. Whether that withheld anything that mattered is NOT said here: the
|
|
302
|
+
// order spends the largest regions first, and recognition below still
|
|
303
|
+
// contributes every exact containment candidate regardless of the budget.
|
|
304
|
+
if (ctx.meter) {
|
|
305
|
+
ctx.meter.pivotProbes += probes;
|
|
306
|
+
const unprobed = branchCount - probeCap;
|
|
307
|
+
if (unprobed > 0)
|
|
308
|
+
ctx.meter.pivotBranchesUnprobed += unprobed;
|
|
309
|
+
}
|
|
291
310
|
// THE FULL recognition, memo-shared with every other reader of these bytes.
|
|
292
311
|
// A "skip the edge trims here" variant was refuted (see recognise's own
|
|
293
312
|
// note): those trims are what find a WHOLE trained form embedded at an
|
package/dist/src/mind/trace.js
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
import { cosine } from "../vec.js";
|
|
9
9
|
import { gistOf, read } from "./primitives.js";
|
|
10
10
|
import { canonicalWindows, leafIdPrefix, leafIdRun } from "./canonical.js";
|
|
11
|
+
// Imported at the TOP, where every other import is. They used to sit 800 lines
|
|
12
|
+
// down under a note claiming the position mattered ("before trace module is
|
|
13
|
+
// loaded") — it does not: an ES module's static imports are HOISTED, so the
|
|
14
|
+
// file's line order never decides load order. The note described an intention
|
|
15
|
+
// the runtime does not honour; the imports move and the claim goes.
|
|
16
|
+
import { decodeText } from "./rationale.js";
|
|
11
17
|
//
|
|
12
18
|
// Budgeted on the same terms as the reach memo below (caches.md): these three
|
|
13
19
|
// maps are cleared on every write, but a long read-only session over a large
|
|
@@ -704,8 +710,6 @@ export function chooseAmong(ctx, candidates, guide) {
|
|
|
704
710
|
? { id: found.item, score: found.score }
|
|
705
711
|
: { id: candidates[0], score: -Infinity };
|
|
706
712
|
}
|
|
707
|
-
// ── Trace shim (used by chooseNext before trace module is loaded) ────────
|
|
708
|
-
import { decodeText } from "./rationale.js";
|
|
709
713
|
function rItemShort(ctx, id, role, score) {
|
|
710
714
|
return {
|
|
711
715
|
text: decodeText(read(ctx, id)),
|
package/dist/src/mind/types.d.ts
CHANGED
|
@@ -35,15 +35,21 @@ export interface GraphSearchHost {
|
|
|
35
35
|
starts: ReadonlySet<number>;
|
|
36
36
|
};
|
|
37
37
|
chooseNext?(node: number): number | undefined;
|
|
38
|
-
/** The
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
38
|
+
/** The lengths `p` for which `prefix ‖ tail[0..p]` IS A STORED NODE, ascending
|
|
39
|
+
* — the join's candidate set, in the tail's own coordinates. Optional: a host
|
|
40
|
+
* that cannot answer makes the join fall back to every prefix, which is exact
|
|
41
|
+
* and complete but pays a `resolve` per offset.
|
|
42
|
+
*
|
|
43
|
+
* WHY NOT THE FOLD'S CUTS. A key names a relation exactly when the
|
|
44
|
+
* concatenation is a node, and a node's end is the end of ITS OWN stream —
|
|
45
|
+
* where the fold never emits a cut (geometry's `emit` guards `at >= n`). So a
|
|
46
|
+
* key can end strictly inside the tail with no boundary anywhere near it:
|
|
47
|
+
* measured, "stockholm mayor" exists, leads on to the mayor fact, and its
|
|
48
|
+
* boundary 6 is in neither the tail's cuts ([4,7]) nor the concatenation's.
|
|
49
|
+
* The fold's boundaries are a SUBSET of the real ends, not a proxy for them,
|
|
50
|
+
* and using them skipped the shortest names first — which is a semantic law,
|
|
51
|
+
* not an optimisation (test/106, test/108 pin it). */
|
|
52
|
+
contentKeyEnds?(prefix: Uint8Array, tail: Uint8Array): readonly number[];
|
|
47
53
|
/** The admission predicate — `traverse.ts`'s `leadsSomewhere`, its ONE
|
|
48
54
|
* definition: does this node bear an edge or a halo? Optional, so a bare
|
|
49
55
|
* host (a raw Store and nothing else) still works; when present, the search
|
|
@@ -104,11 +110,28 @@ export interface Attention {
|
|
|
104
110
|
* strength and its place.
|
|
105
111
|
* `vote` is a sum over every region that agreed, so it grows with how many
|
|
106
112
|
* places corroborated; `peak` is what the strongest one of them said on its
|
|
107
|
-
* own.
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
113
|
+
* own. THIS USED TO PRESCRIBE THE WRONG OPERAND. It read: "a consumer
|
|
114
|
+
* holding this point to consensusFloor(N) — a bar that prices ONE region's
|
|
115
|
+
* maximally-discriminative evidence — must read `peak`, not `vote`." The
|
|
116
|
+
* engine reads the POOLED vote, and thresholds.md §2 derives the floor for
|
|
117
|
+
* exactly that ("Pooled-vote significance floor": one maximally-specific
|
|
118
|
+
* region contributes at most ln N, and ln(N)+1/2 demands corroboration
|
|
119
|
+
* BEYOND one region). MEASURED across 27 anchors on 6 queries: all 11
|
|
120
|
+
* admissions cleared the floor by the sum and NONE by `peak` alone — a gate
|
|
121
|
+
* reading `peak` would refuse every root the engine elects. `peak` remains
|
|
122
|
+
* what it is: the strongest SINGLE region's contribution. */
|
|
111
123
|
peak: number;
|
|
124
|
+
/** The IDF-WEIGHTED sum behind this point — the quantity `consensusFloor` is
|
|
125
|
+
* derived for, and therefore the one the floor gates must read. It is
|
|
126
|
+
* MODE-INDEPENDENT by construction (its per-region weight is
|
|
127
|
+
* `mutual · idf / roots`, never the mode-dependent `wf`), so gating on it
|
|
128
|
+
* makes an anchor's admission the same in `inverse`, `direct` and `combined`.
|
|
129
|
+
* In `inverse` — the only mode the engine runs — it equals `vote` exactly
|
|
130
|
+
* (measured, test/55 test 17), so nothing about today's verdicts changes.
|
|
131
|
+
* MEASURED before this field existed: gating on `vote` DID flip a verdict,
|
|
132
|
+
* anchor 87 of test/55's query (inverse 2.682 admitted, direct 1.468
|
|
133
|
+
* refused, floor 2.292). */
|
|
134
|
+
idfVote: number;
|
|
112
135
|
/** SCALE-INVARIANT confidence: the fraction of the query's OWN regions
|
|
113
136
|
* whose evidence this point accounts for (Σ RegionVote.absorbed among
|
|
114
137
|
* its contributors, over the query's total region count) — read PER-
|
package/dist/src/mind/types.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// GraphSearchHost is defined first (minimal imports) so GraphSearch can import
|
|
4
4
|
// it without pulling in the full MindContext.
|
|
5
|
-
import { bytesEqual, concatBytes
|
|
5
|
+
import { bytesEqual, concatBytes } from "../bytes.js";
|
|
6
|
+
import { restates } from "./derivation.js";
|
|
6
7
|
import { dominates } from "../geometry.js";
|
|
7
8
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
8
9
|
// FREE FUNCTIONS (pure, no state)
|
|
@@ -32,12 +33,14 @@ export function spliceAll(segs) {
|
|
|
32
33
|
export function segRestatesQuery(s, query, queryLen, W) {
|
|
33
34
|
if (!s.rec)
|
|
34
35
|
return false;
|
|
36
|
+
// THE LITERAL EXEMPTION IS THE CALLER'S. A span that IS the site's own bytes
|
|
37
|
+
// at its own position is naming what is already there, not substituting for
|
|
38
|
+
// it — and only this caller knows that, so it says so by not asking.
|
|
35
39
|
const literal = s.j - s.i === s.bytes.length &&
|
|
36
40
|
bytesEqual(s.bytes, query.subarray(s.i, s.j));
|
|
37
41
|
if (literal)
|
|
38
42
|
return false;
|
|
39
|
-
return s.bytes
|
|
40
|
-
indexOf(query, s.bytes, 0) >= 0;
|
|
43
|
+
return restates(query, s.bytes, W, { proper: true });
|
|
41
44
|
}
|
|
42
45
|
/** Lift the answer out of the cover for think: the recognised region, free of
|
|
43
46
|
* the asker's surrounding (unrecognised) framing — and free of any chosen
|