@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/graph-search.ts
CHANGED
|
@@ -177,6 +177,30 @@ export interface Seg {
|
|
|
177
177
|
/** Read the chosen spans back off a derivation: the goal is a chain of bridge
|
|
178
178
|
* steps, each whose second premise is the `out` it crossed. Walk the chain to
|
|
179
179
|
* the axiom and reverse into left-to-right order. */
|
|
180
|
+
/** The BYTE TERM of a derivation's cost: how many bytes its bridge rule charged
|
|
181
|
+
* at PASS each — read back off the rule that charged them (`bridgeRule`:
|
|
182
|
+
* `o.rec ? MICRO : PASS * (o.j - o.i)`), so the split exists in ONE place and
|
|
183
|
+
* the engine's generic accumulator is left alone.
|
|
184
|
+
*
|
|
185
|
+
* `derivation.cost - PASS * readBridgedBytes(derivation)` is therefore the
|
|
186
|
+
* derivation's DISCRETE work — the number a mechanism reports as `moves`, which
|
|
187
|
+
* the pipeline's one formula then prices together with `PASS * unaccounted`.
|
|
188
|
+
* The two readings of the byte term coincide by construction: the spans this
|
|
189
|
+
* sums are exactly the ones the chart could not recognise (`rec === false`),
|
|
190
|
+
* which are the spans the candidate leaves unaccounted. */
|
|
191
|
+
function readBridgedBytes(derivation: Derivation<GItem>): number {
|
|
192
|
+
let bytes = 0;
|
|
193
|
+
let node: Derivation<GItem> | undefined = derivation;
|
|
194
|
+
while (node && node.rule) {
|
|
195
|
+
const o = node.premises[1]?.item;
|
|
196
|
+
if (o !== undefined && o.kind === "out" && o.rec === false) {
|
|
197
|
+
bytes += o.j - o.i;
|
|
198
|
+
}
|
|
199
|
+
node = node.premises[0];
|
|
200
|
+
}
|
|
201
|
+
return bytes;
|
|
202
|
+
}
|
|
203
|
+
|
|
180
204
|
function readCover(derivation: Derivation<GItem>): Seg[] {
|
|
181
205
|
const segs: Seg[] = [];
|
|
182
206
|
let node: Derivation<GItem> | undefined = derivation;
|
|
@@ -238,6 +262,14 @@ export interface DerivationItem {
|
|
|
238
262
|
* {@link GraphSearch}'s rules fired, recovered from the rule's premise/
|
|
239
263
|
* conclusion shape (the rules carry no label, so this classifies by structure,
|
|
240
264
|
* the single place that maps rule geometry to a name). */
|
|
265
|
+
// CLOSED ON PURPOSE — AND ONLY THIS ONE IS. A derivation move is BRANCHED ON
|
|
266
|
+
// (`classifyMove`, the rationale's readers, MOVE_NOTE's fallback), so it is a
|
|
267
|
+
// closed union: adding one without teaching every reader is a compile error,
|
|
268
|
+
// which is what a closed vocabulary buys. The MECHANISM names in
|
|
269
|
+
// `rationale.ts` are the opposite case — written and displayed, never
|
|
270
|
+
// branched on — and they stay free strings that COMPOSE with the nesting
|
|
271
|
+
// (`["respond", "think", "recognise"]`). That asymmetry is deliberate; do not
|
|
272
|
+
// "fix" it by uniting the two (see test/126 for the pipeline half of it).
|
|
241
273
|
export type DerivationMove =
|
|
242
274
|
| "axiom" // a seed: a perceived leaf, a recognised form, or a computed result
|
|
243
275
|
| "follow-edge" // form→form via a continuation edge (STEP) — the core "what follows what"
|
|
@@ -418,7 +450,6 @@ export class GraphSearch {
|
|
|
418
450
|
conceptTarget: ReadonlyMap<number, number>,
|
|
419
451
|
leaves: ReadonlyArray<Leaf>,
|
|
420
452
|
splits: ReadonlySet<number>,
|
|
421
|
-
starts: ReadonlySet<number>,
|
|
422
453
|
substitutions?: ReadonlyMap<number, Uint8Array>,
|
|
423
454
|
connectors?: ReadonlyMap<string, Uint8Array>,
|
|
424
455
|
computedResults?: ReadonlyArray<ComputedResult>,
|
|
@@ -429,7 +460,7 @@ export class GraphSearch {
|
|
|
429
460
|
* the rationale instead of stopping at the first layer. Off by default, so
|
|
430
461
|
* the search pays nothing when no one inspects. */
|
|
431
462
|
onDerivation?: (steps: DerivationStep[]) => void,
|
|
432
|
-
): { segs: Seg[]; cost: number } | null {
|
|
463
|
+
): { segs: Seg[]; cost: number; moves: number } | null {
|
|
433
464
|
// Top-level entry: reset the per-call recursion state, then run the one
|
|
434
465
|
// {@link solve} routine that both the query and any produced composite go
|
|
435
466
|
// through (completion is cover, recursively — see {@link recompleteNode}).
|
|
@@ -441,12 +472,7 @@ export class GraphSearch {
|
|
|
441
472
|
this.derivationSink = onDerivation;
|
|
442
473
|
const solved = this.solve(
|
|
443
474
|
queryLen,
|
|
444
|
-
{
|
|
445
|
-
sites,
|
|
446
|
-
leaves,
|
|
447
|
-
splits,
|
|
448
|
-
starts,
|
|
449
|
-
},
|
|
475
|
+
{ sites, leaves, splits },
|
|
450
476
|
conceptTarget,
|
|
451
477
|
substitutions,
|
|
452
478
|
connectors,
|
|
@@ -460,9 +486,11 @@ export class GraphSearch {
|
|
|
460
486
|
// continuations instead of following the chain the answer itself licensed.
|
|
461
487
|
// With deepening only at the top, `recompleteNode` walks the accepted chain
|
|
462
488
|
// one link at a time (its own memo and stack), so the work is the answer's.
|
|
463
|
-
return solved === null
|
|
464
|
-
|
|
465
|
-
|
|
489
|
+
return solved === null ? null : {
|
|
490
|
+
segs: this.deepen(solved.segs),
|
|
491
|
+
cost: solved.cost,
|
|
492
|
+
moves: solved.moves,
|
|
493
|
+
};
|
|
466
494
|
}
|
|
467
495
|
/** Build the deduction system for one span and return its lightest cover's
|
|
468
496
|
* chosen spans — the SINGLE routine the query and every produced composite
|
|
@@ -483,21 +511,19 @@ export class GraphSearch {
|
|
|
483
511
|
sites: ReadonlyArray<Site>;
|
|
484
512
|
leaves: ReadonlyArray<Leaf>;
|
|
485
513
|
splits: ReadonlySet<number>;
|
|
486
|
-
starts: ReadonlySet<number>;
|
|
487
514
|
},
|
|
488
515
|
conceptTarget: ReadonlyMap<number, number>,
|
|
489
516
|
substitutions?: ReadonlyMap<number, Uint8Array>,
|
|
490
517
|
connectors?: ReadonlyMap<string, Uint8Array>,
|
|
491
518
|
computedResults?: ReadonlyArray<ComputedResult>,
|
|
492
519
|
onDerivation?: (steps: DerivationStep[]) => void,
|
|
493
|
-
): { segs: Seg[]; cost: number } | null {
|
|
520
|
+
): { segs: Seg[]; cost: number; moves: number } | null {
|
|
494
521
|
const system = this.buildSearch(
|
|
495
522
|
spanLen,
|
|
496
523
|
recognition.sites,
|
|
497
524
|
conceptTarget,
|
|
498
525
|
recognition.leaves,
|
|
499
526
|
recognition.splits,
|
|
500
|
-
recognition.starts,
|
|
501
527
|
substitutions,
|
|
502
528
|
connectors,
|
|
503
529
|
computedResults,
|
|
@@ -520,7 +546,11 @@ export class GraphSearch {
|
|
|
520
546
|
onDerivation(readDerivation(derivation, substitutions !== undefined));
|
|
521
547
|
}
|
|
522
548
|
return derivation
|
|
523
|
-
? {
|
|
549
|
+
? {
|
|
550
|
+
segs: readCover(derivation),
|
|
551
|
+
cost: derivation.cost,
|
|
552
|
+
moves: derivation.cost - PASS * readBridgedBytes(derivation),
|
|
553
|
+
}
|
|
524
554
|
: null;
|
|
525
555
|
}
|
|
526
556
|
|
|
@@ -560,7 +590,6 @@ export class GraphSearch {
|
|
|
560
590
|
conceptTarget: ReadonlyMap<number, number>,
|
|
561
591
|
leaves: ReadonlyArray<Leaf>,
|
|
562
592
|
splits: ReadonlySet<number>,
|
|
563
|
-
starts: ReadonlySet<number>,
|
|
564
593
|
substitutions?: ReadonlyMap<number, Uint8Array>,
|
|
565
594
|
connectors?: ReadonlyMap<string, Uint8Array>,
|
|
566
595
|
computedResults?: ReadonlyArray<ComputedResult>,
|
|
@@ -722,7 +751,6 @@ export class GraphSearch {
|
|
|
722
751
|
return this.outRules(it, {
|
|
723
752
|
W,
|
|
724
753
|
splits,
|
|
725
|
-
starts,
|
|
726
754
|
atomsAreHubs,
|
|
727
755
|
coversDone,
|
|
728
756
|
outsByStart,
|
|
@@ -1135,6 +1163,7 @@ export class GraphSearch {
|
|
|
1135
1163
|
// concepts/connectors either (those need the caller's async
|
|
1136
1164
|
// pre-resolution) — the recursion follows edges and fusion, which is what
|
|
1137
1165
|
// a deeper rewrite chain is made of.
|
|
1166
|
+
if (this.host.meter) this.host.meter.recompletes++;
|
|
1138
1167
|
const rec = this.host.recogniseSpan(bytes);
|
|
1139
1168
|
const kids = new Set(nrec.kids);
|
|
1140
1169
|
// THE NODE'S OWN KIDS ARE SITES BY STRUCTURE — recognition cannot be the
|
|
@@ -1181,7 +1210,6 @@ export class GraphSearch {
|
|
|
1181
1210
|
sites: [...recognised, ...structural],
|
|
1182
1211
|
leaves: rec.leaves,
|
|
1183
1212
|
splits: rec.splits,
|
|
1184
|
-
starts: rec.starts,
|
|
1185
1213
|
},
|
|
1186
1214
|
new Map(),
|
|
1187
1215
|
undefined,
|
|
@@ -1391,20 +1419,36 @@ export class GraphSearch {
|
|
|
1391
1419
|
// duplicate read and a branch that could never be taken.
|
|
1392
1420
|
let next: number | null = null;
|
|
1393
1421
|
let keyBytes = c.bytes;
|
|
1394
|
-
// THE
|
|
1395
|
-
//
|
|
1396
|
-
//
|
|
1397
|
-
//
|
|
1398
|
-
//
|
|
1399
|
-
//
|
|
1400
|
-
//
|
|
1401
|
-
//
|
|
1402
|
-
// boundary
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1422
|
+
// THE CANDIDATE ENDS ARE THE PREFIXES THAT ARE STORED NODES, ASCENDING.
|
|
1423
|
+
// The key is `entity + prefix`, and it names a relation exactly when that
|
|
1424
|
+
// concatenation IS a node — so the ends come from a content-addressed
|
|
1425
|
+
// probe per offset (the host's `contentKeyEnds`, the learning path's own
|
|
1426
|
+
// mechanism: one leaf walk plus one `findBranch` per offset, no `resolve`),
|
|
1427
|
+
// never from the fold's boundaries. A boundary is not a proxy: a stored
|
|
1428
|
+
// member's end is the end of ITS OWN stream, and the fold never cuts at a
|
|
1429
|
+
// stream's end — measured, "stockholm mayor" exists, leads on to the mayor
|
|
1430
|
+
// fact, and its boundary 6 sits in neither the tail's cuts ([4,7]) nor the
|
|
1431
|
+
// concatenation's. Filtering the scan by "is this a node?" cannot change
|
|
1432
|
+
// the winner: a position that is not a node cannot resolve, so skipping it
|
|
1433
|
+
// is invisible; and the order stays SHORTEST FIRST, which is a semantic
|
|
1434
|
+
// law, not an optimisation (test/106, test/108 pin it).
|
|
1435
|
+
//
|
|
1436
|
+
// A host that cannot answer falls back to every prefix: exact and
|
|
1437
|
+
// complete, at a `resolve` per offset. A host that CAN answer is
|
|
1438
|
+
// authoritative even when it answers "none" — if no prefix is a node then
|
|
1439
|
+
// no key exists to resolve, so enumerating would only pay nulls. (A key
|
|
1440
|
+
// reachable through the CANONICAL equivalence alone and ending off every
|
|
1441
|
+
// node end is therefore not tried here; that dimension is unreachable on
|
|
1442
|
+
// this path by construction and is not part of the exact-key law.)
|
|
1443
|
+
const ends = this.host.contentKeyEnds?.(c.bytes, tail);
|
|
1444
|
+
const candidateEnds = function* (): Generator<number> {
|
|
1445
|
+
if (ends !== undefined) {
|
|
1446
|
+
yield* ends;
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
for (let p = 1; p <= tail.length; p++) yield p;
|
|
1450
|
+
};
|
|
1451
|
+
for (const len of candidateEnds()) {
|
|
1408
1452
|
keyBytes = concat2(c.bytes, tail.subarray(0, len));
|
|
1409
1453
|
const k = this.host.resolve(keyBytes) ??
|
|
1410
1454
|
this.host.canonResolve?.(keyBytes) ??
|
|
@@ -1459,7 +1503,6 @@ export class GraphSearch {
|
|
|
1459
1503
|
ctx: {
|
|
1460
1504
|
W: number;
|
|
1461
1505
|
splits: ReadonlySet<number>;
|
|
1462
|
-
starts: ReadonlySet<number>;
|
|
1463
1506
|
atomsAreHubs: boolean;
|
|
1464
1507
|
coversDone: Set<number>;
|
|
1465
1508
|
outsByStart: Map<number, OutItem[]>;
|
|
@@ -1622,7 +1665,6 @@ export class GraphSearch {
|
|
|
1622
1665
|
r: OutItem,
|
|
1623
1666
|
ctx: {
|
|
1624
1667
|
W: number;
|
|
1625
|
-
starts: ReadonlySet<number>;
|
|
1626
1668
|
atomsAreHubs: boolean;
|
|
1627
1669
|
findLeafU: (b: Uint8Array) => number | undefined;
|
|
1628
1670
|
findBranchU: (k: number[]) => number | undefined;
|
package/src/mind/match.ts
CHANGED
|
@@ -723,7 +723,7 @@ export function frameSlots(
|
|
|
723
723
|
* ANCHOR that the query displaced. Neither implies the other, and the
|
|
724
724
|
* observed failures pass the restatement guard cleanly.
|
|
725
725
|
*
|
|
726
|
-
*
|
|
726
|
+
* Four conditions, all byte-exact and all necessary:
|
|
727
727
|
*
|
|
728
728
|
* 1. the query and the anchor must be ONE STRUCTURE — what they share has to
|
|
729
729
|
* dominate the query, or the query is not a variant of the anchor at all
|
|
@@ -811,8 +811,10 @@ export function substituteAll(
|
|
|
811
811
|
const usable = pairs.filter((p) => p.needle.length > 0);
|
|
812
812
|
if (usable.length === 0) return hay;
|
|
813
813
|
// Longest needle first, so a needle that is a prefix of another can never
|
|
814
|
-
// pre-empt it. Ties cannot arise:
|
|
815
|
-
// pairwise distinct
|
|
814
|
+
// pre-empt it. Ties cannot arise: a consumer that VOICES checks the
|
|
815
|
+
// fillers pairwise with `distinct` and refuses such an instance itself —
|
|
816
|
+
// `frameSlots` reports and does not judge (see its own doc), so the refusal
|
|
817
|
+
// lives with the mechanism that needs it, not here.
|
|
816
818
|
const order = [...usable].sort((a, b) => b.needle.length - a.needle.length);
|
|
817
819
|
const out: number[] = [];
|
|
818
820
|
let i = 0;
|
|
@@ -1002,6 +1004,8 @@ export function spanHalo(
|
|
|
1002
1004
|
return found ? normalize(out) : null;
|
|
1003
1005
|
}
|
|
1004
1006
|
|
|
1007
|
+
/** A TEST SURFACE: exported for the tests that pin the synonym strength ladder
|
|
1008
|
+
* (they are its only consumers); nothing in `src/` calls it. */
|
|
1005
1009
|
/** Distributional synonym evidence between arbitrary byte spans. Whole words
|
|
1006
1010
|
* need not be independently interned: their stored W-window occurrences are
|
|
1007
1011
|
* lifted to episode halos, bundled, and compared. The caller chooses the
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
import type { Alu } from "../../alu/src/alu.js";
|
|
11
11
|
import { STEP } from "../graph-search.js";
|
|
12
|
-
import { unexplainedLabel } from "../rationale.js";
|
|
13
12
|
import type { PipelineMechanism } from "../pipeline-mechanism.js";
|
|
14
13
|
|
|
15
14
|
/** Wrap the ALU as a {@link PipelineMechanism}. */
|
|
@@ -35,7 +34,6 @@ export function aluToMechanism(alu: Alu): PipelineMechanism {
|
|
|
35
34
|
bytes: u.bytes,
|
|
36
35
|
accounted: [[u.i, u.j]],
|
|
37
36
|
moves: STEP,
|
|
38
|
-
unexplained: unexplainedLabel(query, [[u.i, u.j]]),
|
|
39
37
|
}));
|
|
40
38
|
},
|
|
41
39
|
};
|
|
@@ -30,15 +30,11 @@ import {
|
|
|
30
30
|
sharedFrameStrengthOf,
|
|
31
31
|
} from "../match.js";
|
|
32
32
|
import { joinWithBridge } from "../resonance.js";
|
|
33
|
-
import { restatesQuery } from "../reasoning.js";
|
|
34
33
|
import { CONCEPT, STEP } from "../graph-search.js";
|
|
35
34
|
import { concat2, indexOf } from "../../bytes.js";
|
|
36
35
|
import { consensusFloor, dominates } from "../../geometry.js";
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
unexplainedLabel,
|
|
40
|
-
unexplainedSpans,
|
|
41
|
-
} from "../rationale.js";
|
|
36
|
+
import { decodeText } from "../rationale.js";
|
|
37
|
+
import { restates, unexplainedSpans } from "../derivation.js";
|
|
42
38
|
import { rItem, rNode } from "../trace.js";
|
|
43
39
|
import { dismissedKnownContent } from "../bridge.js";
|
|
44
40
|
import { leafIdRun } from "../canonical.js";
|
|
@@ -92,7 +88,6 @@ export interface CastResult {
|
|
|
92
88
|
/** A human-readable label for the query bytes this schema left
|
|
93
89
|
* unexplained — purely diagnostic, never priced (see the module's
|
|
94
90
|
* Task 2 note in pipeline.ts's Candidate interface). */
|
|
95
|
-
unexplained: string;
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
/** The seat that establishes a node's role in an analogical comparison:
|
|
@@ -129,7 +124,7 @@ export interface CastResult {
|
|
|
129
124
|
* describes id ("...painted by Leonardo da Vinci." contains "Leonardo da
|
|
130
125
|
* Vinci"). An incidental adjacency predecessor never does — it merely
|
|
131
126
|
* preceded id in some unrelated document without ever mentioning it. No
|
|
132
|
-
* new tuned constant: containment is the same primitive `
|
|
127
|
+
* new tuned constant: containment is the same primitive `restates`
|
|
133
128
|
* and `dominates`-style checks already use throughout this codebase.
|
|
134
129
|
*
|
|
135
130
|
* `allowForward` (default true) gates the FORWARD branch specifically —
|
|
@@ -499,7 +494,6 @@ export async function counterfactualTransfer(
|
|
|
499
494
|
used: used ?? new Set(),
|
|
500
495
|
accounted,
|
|
501
496
|
moves,
|
|
502
|
-
unexplained: unexplainedLabel(query, accounted),
|
|
503
497
|
});
|
|
504
498
|
};
|
|
505
499
|
ctx.trace?.step(
|
|
@@ -613,7 +607,7 @@ export async function counterfactualTransfer(
|
|
|
613
607
|
const fwd = await follow(ctx, proj.anchor, qv);
|
|
614
608
|
if (
|
|
615
609
|
fwd !== null && indexOf(answer, fwd, 0) < 0 &&
|
|
616
|
-
!
|
|
610
|
+
!restates(query, fwd, 0, { proper: true })
|
|
617
611
|
) {
|
|
618
612
|
// THROUGH THE SHARED JOINER, not a bare concatenation.
|
|
619
613
|
//
|
|
@@ -695,7 +689,7 @@ export async function counterfactualTransfer(
|
|
|
695
689
|
// cap the test reads "none of the established continuations appears".
|
|
696
690
|
const domNext = ctx.store.nextFirst(dominant.anchor, hubBound(ctx));
|
|
697
691
|
const displaced = domNext
|
|
698
|
-
.every((n) =>
|
|
692
|
+
.every((n) => !restates(query, read(ctx, n), 0));
|
|
699
693
|
// The SUBSTITUTE is what redirection speaks — the answer IS `project(last)`,
|
|
700
694
|
// its own fact — so the same bar applies. The displaced structure is only
|
|
701
695
|
// recognised as the slot being overridden and is never voiced, so it is
|
|
@@ -791,7 +785,7 @@ export async function counterfactualTransfer(
|
|
|
791
785
|
queryScale(p.ctx.length) &&
|
|
792
786
|
indexOf(dominant.ctx, p.ctx, 0) < 0 &&
|
|
793
787
|
indexOf(p.ctx, dominant.ctx, 0) < 0 &&
|
|
794
|
-
|
|
788
|
+
!restates(query, p.ctx, 0)
|
|
795
789
|
) {
|
|
796
790
|
analogs.push({ anchor: p.anchor, point: p, src: p });
|
|
797
791
|
}
|
|
@@ -810,7 +804,7 @@ export async function counterfactualTransfer(
|
|
|
810
804
|
!queryScale(nctx.length) ||
|
|
811
805
|
indexOf(dominant.ctx, nctx, 0) >= 0 ||
|
|
812
806
|
indexOf(nctx, dominant.ctx, 0) >= 0 ||
|
|
813
|
-
|
|
807
|
+
restates(query, nctx, 0)
|
|
814
808
|
) continue;
|
|
815
809
|
analogs.push({ anchor: nid, point: null, src: p });
|
|
816
810
|
}
|
|
@@ -867,7 +861,9 @@ export async function counterfactualTransfer(
|
|
|
867
861
|
// grounds") — fine for ORIENTING mechanisms, not for voicing learnt
|
|
868
862
|
// content the query never asked about. Computed once here; both the
|
|
869
863
|
// hub fallback below and the comparison gate consume it.
|
|
870
|
-
const rootTrusted = roots.some((r) =>
|
|
864
|
+
const rootTrusted = roots.some((r) =>
|
|
865
|
+
r.idfVote >= consensusFloor(corpusN(ctx))
|
|
866
|
+
); // the IDF sum: the bar's own quantity
|
|
871
867
|
// The context that ESTABLISHES a filler — the same reverse context, under
|
|
872
868
|
// the same naming test, `seatOfNode` uses to VOICE an analog (a predecessor
|
|
873
869
|
// whose bytes CONTAIN the node's: it names or describes it, rather than
|
|
@@ -1284,13 +1280,16 @@ export async function counterfactualTransfer(
|
|
|
1284
1280
|
// consulting it here is the same fallback `resolve` already makes when an
|
|
1285
1281
|
// exact content lookup misses, and it keeps this mechanism from carrying
|
|
1286
1282
|
// any idea of its own about what a character is.
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1283
|
+
// TWO WITNESSES, ONE LAW: the byte reading, and — when the response carries
|
|
1284
|
+
// one — the same reading under the response's own equivalence. Asked twice
|
|
1285
|
+
// rather than branched on, because the equivalence is a property of the
|
|
1286
|
+
// injected canonicalizer (a substring-monotone function is the usual case,
|
|
1287
|
+
// not a guarantee), and the two readings are OR-ed here exactly as they were
|
|
1288
|
+
// before this became one definition.
|
|
1289
|
+
const echoesQuery = (x: Uint8Array): boolean =>
|
|
1290
|
+
restates(query, x, 0, { proper: true }) ||
|
|
1291
|
+
(ctx.canon !== null &&
|
|
1292
|
+
restates(query, x, 0, { equate: ctx.canon, proper: true }));
|
|
1294
1293
|
if (echoesQuery(b)) {
|
|
1295
1294
|
const fwd = await follow(ctx, bestAnalog.anchor, qv);
|
|
1296
1295
|
if (fwd !== null && fwd.length > 0 && !echoesQuery(fwd)) b = fwd;
|
|
@@ -1404,7 +1403,6 @@ export const castMechanism: PipelineMechanism = {
|
|
|
1404
1403
|
accounted: c.accounted,
|
|
1405
1404
|
moves: c.moves,
|
|
1406
1405
|
used: c.used,
|
|
1407
|
-
unexplained: c.unexplained,
|
|
1408
1406
|
}));
|
|
1409
1407
|
},
|
|
1410
1408
|
};
|
|
@@ -44,7 +44,7 @@ import { read } from "../primitives.js";
|
|
|
44
44
|
import { corpusN, reachOf } from "../traverse.js";
|
|
45
45
|
import { dominates } from "../../geometry.js";
|
|
46
46
|
import { STEP } from "../graph-search.js";
|
|
47
|
-
import {
|
|
47
|
+
import { insideAnsweredTurn } from "../derivation.js";
|
|
48
48
|
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
49
49
|
import { rItem, rNode } from "../trace.js";
|
|
50
50
|
|
|
@@ -58,9 +58,6 @@ export interface JoinResult {
|
|
|
58
58
|
used: ReadonlySet<number>;
|
|
59
59
|
accounted: Array<[number, number]>;
|
|
60
60
|
moves: number;
|
|
61
|
-
/** A human-readable label for the query bytes the meet left unexplained —
|
|
62
|
-
* purely diagnostic, never priced. */
|
|
63
|
-
unexplained: string;
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
/** The main confluence entry point. Given a query, detect whether it weaves
|
|
@@ -98,14 +95,9 @@ export async function confluenceJoin(
|
|
|
98
95
|
// Recognition and attention still see the full transcript; only this
|
|
99
96
|
// mechanism's constraint population excludes answered spans.
|
|
100
97
|
const queryWin = new Map<number, number>();
|
|
101
|
-
|
|
98
|
+
const answered = { at: 0 };
|
|
102
99
|
for (const [off, id] of pre.queryWindows) {
|
|
103
|
-
|
|
104
|
-
answered < ctx.answeredSpans.length &&
|
|
105
|
-
ctx.answeredSpans[answered][1] <= off
|
|
106
|
-
) answered++;
|
|
107
|
-
const span = ctx.answeredSpans[answered];
|
|
108
|
-
if (span && span[0] <= off && off + W <= span[1]) continue;
|
|
100
|
+
if (insideAnsweredTurn(ctx.answeredSpans, answered, off, off + W)) continue;
|
|
109
101
|
queryWin.set(off, id);
|
|
110
102
|
}
|
|
111
103
|
const queryIds = new Set(queryWin.values());
|
|
@@ -146,6 +138,30 @@ export async function confluenceJoin(
|
|
|
146
138
|
const bindsAConstituent = (cover: Array<[number, number]>): boolean =>
|
|
147
139
|
cover.some(([cs, ce]) => ce - cs >= 2 * W);
|
|
148
140
|
|
|
141
|
+
// THE VOTE ENTERS AS ORDER, NEVER AS A BAR. This is the only one of the
|
|
142
|
+
// climb's four consumers (recall, fuseAttention, cast, here) that uses the
|
|
143
|
+
// evidence's MAGNITUDE without a floor, and it is legitimate by construction:
|
|
144
|
+
// `ranked` answers "which anchor is stronger" — a question about votes, so the
|
|
145
|
+
// comparison stays within one dimension — and the vote is otherwise only
|
|
146
|
+
// REPORTED (Stream.vote travels to the rationale's constraint nodes). What
|
|
147
|
+
// actually SELECTS a constraint is byte-structural and never the magnitude: a
|
|
148
|
+
// run of at least 2W (`bindsAConstituent`, with its accidental-sharing
|
|
149
|
+
// counter-examples above), disjoint covers (`disjoint`), and scaffolding never
|
|
150
|
+
// binds at all (`dominates(reachOf(…), N)`). The MEET such a stream may
|
|
151
|
+
// produce is selected the same way: a span shorter than 2W is rejected, and
|
|
152
|
+
// the winner is the one with the smallest `reach` (ties broken by the longer
|
|
153
|
+
// span) — a corpus quantity and bytes, never the vote, which appears only in
|
|
154
|
+
// the trace item.
|
|
155
|
+
// binds at all (`dominates(reachOf(…), N)`). The only cut in this loop is a
|
|
156
|
+
// BUDGET, and it is measured: stopping the scan at 2W anchors saves 50-70% of
|
|
157
|
+
// confluence's cost on non-conjunctive queries while preserving every genuinely
|
|
158
|
+
// conjunctive case, whose top anchors ARE its constraints.
|
|
159
|
+
// MEASURED (this goal, on THIS file's own conjunctive fixture): the two
|
|
160
|
+
// streams appear at ranks 1 and 4 against a budget of 2W = 8, on a query whose
|
|
161
|
+
// `ranked` is 9 — so the cut IS live (it would have returned null at the 8th
|
|
162
|
+
// anchor) and it does NOT prune the case it exists to protect. The other
|
|
163
|
+
// conjunctive fixture (the Leonardo one) finds them at ranks 0 and 1. Scope:
|
|
164
|
+
// these are the repo's conjunctive fixtures, and no more.
|
|
149
165
|
const streams: Stream[] = [];
|
|
150
166
|
const rankedCapped = ranked.length > pre.k ? ranked.slice(0, pre.k) : ranked;
|
|
151
167
|
// CONJUNCTIVITY EARLY-EXIT: a conjunctive query's top-ranked anchors
|
|
@@ -291,7 +307,6 @@ export async function confluenceJoin(
|
|
|
291
307
|
used: new Set([met.a.anchor, met.b.anchor]),
|
|
292
308
|
accounted,
|
|
293
309
|
moves: 3 * STEP,
|
|
294
|
-
unexplained: unexplainedLabel(query, accounted),
|
|
295
310
|
};
|
|
296
311
|
}
|
|
297
312
|
|
|
@@ -320,7 +335,6 @@ export const confluenceMechanism: PipelineMechanism = {
|
|
|
320
335
|
accounted: met.accounted,
|
|
321
336
|
moves: met.moves,
|
|
322
337
|
used: met.used,
|
|
323
|
-
unexplained: met.unexplained,
|
|
324
338
|
}];
|
|
325
339
|
},
|
|
326
340
|
};
|
|
@@ -16,7 +16,8 @@ import { guidedFirst, hubBound } from "../traverse.js";
|
|
|
16
16
|
import { conceptHop } from "../match.js";
|
|
17
17
|
import { bridge } from "../resonance.js";
|
|
18
18
|
import { liftAnswer, liftedScaffolding, segRestatesQuery } from "../types.js";
|
|
19
|
-
import { decodeText
|
|
19
|
+
import { decodeText } from "../rationale.js";
|
|
20
|
+
import { insideAnsweredTurn, restates } from "../derivation.js";
|
|
20
21
|
import { indexOf } from "../../bytes.js";
|
|
21
22
|
import type { RationaleItem } from "../rationale.js";
|
|
22
23
|
import { rItem, rNode, traceDerivation } from "../trace.js";
|
|
@@ -62,16 +63,13 @@ export async function resolveConnectors(
|
|
|
62
63
|
// discarded — a semantically neutral gate (it removes work whose product
|
|
63
64
|
// liftAnswer throws away), and a cumulative (multi-turn) query is exactly
|
|
64
65
|
// where such already-answered continuations recur.
|
|
65
|
-
|
|
66
|
+
const answered = { at: 0 };
|
|
66
67
|
const ordered = [...sites]
|
|
67
68
|
.sort((a, b) => a.start - b.start)
|
|
68
69
|
.filter((s) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
) answered++;
|
|
73
|
-
const span = ctx.answeredSpans[answered];
|
|
74
|
-
if (span && span[0] <= s.start && s.end <= span[1]) return false;
|
|
70
|
+
if (insideAnsweredTurn(ctx.answeredSpans, answered, s.start, s.end)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
75
73
|
if (query === undefined || ctx.answeredSpans.length === 0) return true;
|
|
76
74
|
const continuations = ctx.store.nextFirst(s.payload, hubBound(ctx));
|
|
77
75
|
return !continuations.some((answer) => {
|
|
@@ -93,7 +91,11 @@ export async function resolveConnectors(
|
|
|
93
91
|
// 3 bytes this reads 4 bytes per candidate instead of the ~231 it
|
|
94
92
|
// averaged before.
|
|
95
93
|
const bytes = read(ctx, answer, query.length + 1);
|
|
96
|
-
|
|
94
|
+
// THE CONTENT READING of the same exclusion: the site's own
|
|
95
|
+
// continuation already occurs in the query, so voicing it back adds
|
|
96
|
+
// nothing. It is the restatement law with no `proper` flag — the
|
|
97
|
+
// containment reading that also admits the whole query.
|
|
98
|
+
return restates(query, bytes, 0);
|
|
97
99
|
});
|
|
98
100
|
});
|
|
99
101
|
const bridgePair = async (l: number, r: number) => {
|
|
@@ -210,18 +212,11 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
210
212
|
)
|
|
211
213
|
: await resolveConnectors(ctx, sites, query);
|
|
212
214
|
let splits = rec.splits;
|
|
213
|
-
let starts = rec.starts;
|
|
214
215
|
if (computed.length > 0) {
|
|
215
216
|
splits = new Set(rec.splits);
|
|
216
|
-
starts = new Set(rec.starts);
|
|
217
217
|
for (const u of computed) {
|
|
218
218
|
splits.add(u.i);
|
|
219
219
|
splits.add(u.j);
|
|
220
|
-
// A computation's own boundaries carry the same fold-level evidence
|
|
221
|
-
// a chunk boundary does — "computation always wins" (see the header
|
|
222
|
-
// comment) extends to being trusted ground for cross-leaf recovery.
|
|
223
|
-
starts.add(u.i);
|
|
224
|
-
starts.add(u.j);
|
|
225
220
|
}
|
|
226
221
|
}
|
|
227
222
|
const concepts = ctx.meter
|
|
@@ -262,7 +257,6 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
262
257
|
concepts,
|
|
263
258
|
rec.leaves,
|
|
264
259
|
splits,
|
|
265
|
-
starts,
|
|
266
260
|
undefined,
|
|
267
261
|
connectors,
|
|
268
262
|
computedResults,
|
|
@@ -330,9 +324,12 @@ export const coverMechanism: PipelineMechanism = {
|
|
|
330
324
|
return [{
|
|
331
325
|
bytes: composed,
|
|
332
326
|
accounted,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
327
|
+
// The derivation's DISCRETE work. The bytes the chart could not
|
|
328
|
+
// recognise are NOT priced here: they are exactly the spans `accounted`
|
|
329
|
+
// leaves uncovered, and the pipeline's one formula charges them at PASS —
|
|
330
|
+
// the same formula that prices every other mechanism's candidate. No
|
|
331
|
+
// mechanism spells a cost of its own.
|
|
332
|
+
moves: solved!.moves,
|
|
336
333
|
// How much of the composed answer is the asker's own unexplained words
|
|
337
334
|
// (the spans the liftAnswer trace above labels "scaffolding"). Cover is
|
|
338
335
|
// the mechanism that can carry them, because a PASS span still lands in
|
|
@@ -11,7 +11,7 @@ import type { MindContext } from "../types.js";
|
|
|
11
11
|
import { read } from "../primitives.js";
|
|
12
12
|
import { follow, isSpanShaped, locate, skillExemplar } from "../match.js";
|
|
13
13
|
import { concatBytes, indexOf } from "../../bytes.js";
|
|
14
|
-
import { decodeText
|
|
14
|
+
import { decodeText } from "../rationale.js";
|
|
15
15
|
import type {
|
|
16
16
|
PipelineMechanism,
|
|
17
17
|
Precomputed,
|
|
@@ -46,7 +46,6 @@ export async function extractBySkill(
|
|
|
46
46
|
{
|
|
47
47
|
bytes: Uint8Array;
|
|
48
48
|
accounted: Array<[number, number]>;
|
|
49
|
-
unexplained: string;
|
|
50
49
|
} | null
|
|
51
50
|
> {
|
|
52
51
|
const t = ctx.trace?.enter("extractBySkill", [
|
|
@@ -102,6 +101,12 @@ export async function extractBySkill(
|
|
|
102
101
|
const searched = ranked.slice(0, pre.k);
|
|
103
102
|
let shapeMisses = 0;
|
|
104
103
|
let subQuantum = 0;
|
|
104
|
+
// A SECOND refusal counter, because the note below used to call an UNANCHORED
|
|
105
|
+
// read "sub-quantum" — which is false, and it is the kind of instrumentation
|
|
106
|
+
// defect AGENTS §6 says to close where it lives: a reader could not tell from
|
|
107
|
+
// the trace which of the two gates refused. Neither counter reaches a
|
|
108
|
+
// decision (meter.ts contract 1); they exist so the refusal is legible.
|
|
109
|
+
let unanchored = 0;
|
|
105
110
|
for (const cand of searched) {
|
|
106
111
|
const exemplar = await pre.spanShapedOf(cand.anchor);
|
|
107
112
|
if (!exemplar) {
|
|
@@ -137,22 +142,23 @@ export async function extractBySkill(
|
|
|
137
142
|
// Here the field is this mechanism's own output and carries its documented
|
|
138
143
|
// meaning, so the test is sound exactly where the convention does not reach.
|
|
139
144
|
if (built.accounted.length === 0) {
|
|
140
|
-
|
|
145
|
+
unanchored++;
|
|
141
146
|
continue;
|
|
142
147
|
}
|
|
143
|
-
if (shapeMisses > 0 || subQuantum > 0) {
|
|
148
|
+
if (shapeMisses > 0 || subQuantum > 0 || unanchored > 0) {
|
|
144
149
|
ctx.trace?.step(
|
|
145
150
|
"trySkillAnchors",
|
|
146
151
|
[
|
|
147
152
|
rItem(
|
|
148
153
|
query.subarray(0, 0),
|
|
149
|
-
`skipped ${shapeMisses + subQuantum}`,
|
|
154
|
+
`skipped ${shapeMisses + subQuantum + unanchored}`,
|
|
150
155
|
),
|
|
151
156
|
rNode(ctx, cand.anchor, "chosen"),
|
|
152
157
|
],
|
|
153
158
|
[],
|
|
154
|
-
`skipped ${shapeMisses} non-exemplar
|
|
155
|
-
|
|
159
|
+
`skipped ${shapeMisses} non-exemplar, ${subQuantum} sub-quantum and ` +
|
|
160
|
+
`${unanchored} unanchored anchor(s) before one yielded a usable ` +
|
|
161
|
+
`extraction`,
|
|
156
162
|
);
|
|
157
163
|
}
|
|
158
164
|
t?.done(
|
|
@@ -170,7 +176,6 @@ export async function extractBySkill(
|
|
|
170
176
|
return {
|
|
171
177
|
bytes: built.bytes,
|
|
172
178
|
accounted: built.accounted,
|
|
173
|
-
unexplained: unexplainedLabel(query, built.accounted),
|
|
174
179
|
};
|
|
175
180
|
}
|
|
176
181
|
if (shapeMisses === searched.length) {
|
|
@@ -403,7 +408,6 @@ export const extractionMechanism: PipelineMechanism = {
|
|
|
403
408
|
bytes: ex.bytes,
|
|
404
409
|
accounted: ex.accounted,
|
|
405
410
|
moves: CONCEPT + STEP * ex.accounted.length,
|
|
406
|
-
unexplained: ex.unexplained,
|
|
407
411
|
}];
|
|
408
412
|
},
|
|
409
413
|
};
|
|
@@ -300,7 +300,6 @@ export const prefixMechanism: PipelineMechanism = {
|
|
|
300
300
|
// IDENTITY bridge takes.
|
|
301
301
|
accounted: [[0, query.length]],
|
|
302
302
|
moves: STEP,
|
|
303
|
-
unexplained: "",
|
|
304
303
|
// NOT complete: the query is a proper PREFIX, so the form may carry more
|
|
305
304
|
// past the remainder this voiced.
|
|
306
305
|
}];
|