@hviana/sema 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -10
- package/README.md +17 -38
- package/dist/example/demo.js +85 -34
- package/dist/src/geometry.d.ts +0 -10
- package/dist/src/geometry.js +0 -12
- package/dist/src/meter.d.ts +11 -0
- package/dist/src/meter.js +11 -0
- package/dist/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.js +2 -1
- package/dist/src/mind/derivation.d.ts +201 -0
- package/dist/src/mind/derivation.js +327 -0
- package/dist/src/mind/graph-search.d.ts +2 -1
- package/dist/src/mind/graph-search.js +37 -15
- package/dist/src/mind/match.d.ts +2 -0
- package/dist/src/mind/match.js +2 -0
- package/dist/src/mind/mechanisms/alu.js +0 -2
- package/dist/src/mind/mechanisms/cast.d.ts +1 -5
- package/dist/src/mind/mechanisms/cast.js +15 -18
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -3
- package/dist/src/mind/mechanisms/confluence.js +3 -9
- package/dist/src/mind/mechanisms/cover.js +17 -20
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -1
- package/dist/src/mind/mechanisms/extraction.js +13 -8
- package/dist/src/mind/mechanisms/prefix-completion.js +0 -1
- package/dist/src/mind/mechanisms/recall.d.ts +0 -1
- package/dist/src/mind/mechanisms/recall.js +8 -9
- package/dist/src/mind/mechanisms/reference.js +3 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +1 -4
- package/dist/src/mind/pipeline.js +106 -41
- package/dist/src/mind/rationale.d.ts +0 -11
- package/dist/src/mind/rationale.js +6 -32
- package/dist/src/mind/reasoning.d.ts +4 -30
- package/dist/src/mind/reasoning.js +183 -151
- package/dist/src/mind/types.js +6 -3
- package/docs/INDEX.md +23 -24
- package/docs/INVARIANTS.md +16 -17
- package/docs/architecture/bounded-reads.md +4 -4
- package/docs/architecture/closure.md +65 -0
- package/docs/architecture/commonality.md +27 -18
- package/docs/architecture/cost-model.md +5 -5
- package/docs/architecture/exact-vs-approximate.md +4 -4
- package/docs/architecture/factored-machinery.md +14 -14
- package/docs/architecture/mechanism-market.md +9 -9
- package/docs/architecture/meter.md +4 -5
- package/docs/architecture/store.md +2 -2
- package/docs/architecture/thresholds.md +1 -1
- package/docs/failures/tempting-but-wrong.md +11 -1
- package/docs/harness/gates.md +6 -6
- package/docs/mechanisms/cover.md +2 -2
- package/example/demo.ts +90 -37
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/geometry.ts +0 -13
- package/src/meter.ts +11 -0
- package/src/mind/articulation.ts +0 -1
- package/src/mind/attention.ts +2 -1
- package/src/mind/derivation.ts +473 -0
- package/src/mind/graph-search.ts +37 -20
- package/src/mind/match.ts +2 -0
- package/src/mind/mechanisms/alu.ts +0 -2
- package/src/mind/mechanisms/cast.ts +17 -21
- package/src/mind/mechanisms/confluence.ts +3 -13
- package/src/mind/mechanisms/cover.ts +17 -20
- package/src/mind/mechanisms/extraction.ts +13 -9
- package/src/mind/mechanisms/prefix-completion.ts +0 -1
- package/src/mind/mechanisms/recall.ts +7 -9
- package/src/mind/mechanisms/reference.ts +2 -3
- package/src/mind/pipeline-mechanism.ts +1 -4
- package/src/mind/pipeline.ts +121 -46
- package/src/mind/rationale.ts +6 -36
- package/src/mind/reasoning.ts +208 -178
- package/src/mind/types.ts +5 -2
- package/test/133-the-decision-point-renders-the-state.test.mjs +204 -0
- package/test/134-the-law-explains-the-engines-own-refusal.test.mjs +237 -0
- package/test/135-one-law-any-producer.test.mjs +289 -0
- package/test/136-the-two-named-limits.test.mjs +205 -0
- package/test/137-the-law-lives-once-and-below.test.mjs +400 -0
- package/test/138-the-remainder-drains-only-what-a-move-declares.test.mjs +62 -0
- package/test/139-the-witness-is-engagement-not-explanation.test.mjs +51 -0
- package/test/140-irrelevant-supply-does-not-change-an-answer.test.mjs +48 -0
- package/test/141-the-question-is-paid-at-construction.test.mjs +98 -0
- package/test/36-already-answered-fusion.test.mjs +20 -2
- package/test/37-cluster-dispersion-fusion.test.mjs +30 -3
- package/test/38-reason-restate-guard.test.mjs +22 -2
- package/test/55-cost-meter.test.mjs +4 -1
package/src/mind/pipeline.ts
CHANGED
|
@@ -15,8 +15,17 @@ import type { ComputedSpan } from "../extension.js";
|
|
|
15
15
|
import { gistOf, read, resolve } from "./primitives.js";
|
|
16
16
|
import { recognise } from "./recognition.js";
|
|
17
17
|
import { fuseAttention, reason } from "./reasoning.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
closed,
|
|
20
|
+
type DerivationState,
|
|
21
|
+
remainderOf,
|
|
22
|
+
type Span,
|
|
23
|
+
unaccountedBytes,
|
|
24
|
+
unexplainedSpans,
|
|
25
|
+
windowOf,
|
|
26
|
+
} from "./derivation.js";
|
|
19
27
|
import { rItem } from "./trace.js";
|
|
28
|
+
import { unexplainedLabel } from "./rationale.js";
|
|
20
29
|
import { hubBound } from "./traverse.js";
|
|
21
30
|
import { type PipelineMechanism, Precomputed } from "./pipeline-mechanism.js";
|
|
22
31
|
import { coverMechanism } from "./mechanisms/cover.js";
|
|
@@ -244,7 +253,6 @@ export async function think(
|
|
|
244
253
|
weight: number;
|
|
245
254
|
used?: ReadonlySet<number>;
|
|
246
255
|
accounted: ReadonlyArray<[number, number]>;
|
|
247
|
-
unexplained: string;
|
|
248
256
|
complete?: boolean;
|
|
249
257
|
/** Bytes of this candidate's ANSWER that came from spans nothing
|
|
250
258
|
* recognised — query words carried through verbatim (see
|
|
@@ -393,14 +401,16 @@ export async function think(
|
|
|
393
401
|
? await meter.time(`${mech.name}.run`, () => mech.run(ctx, query, pre))
|
|
394
402
|
: await mech.run(ctx, query, pre);
|
|
395
403
|
for (const r of results) {
|
|
396
|
-
|
|
404
|
+
// ONE FORMULA, EVERY CANDIDATE: the chart's derivation reports how many
|
|
405
|
+
// discrete moves it made and which bytes it could not recognise; the
|
|
406
|
+
// currency prices both. No mechanism passes a price of its own.
|
|
407
|
+
const weight = weigh(r.accounted, r.moves);
|
|
397
408
|
consider({
|
|
398
409
|
bytes: r.bytes,
|
|
399
410
|
provenance: r.provenance ?? mech.provenance,
|
|
400
411
|
weight,
|
|
401
412
|
used: r.used,
|
|
402
413
|
accounted: r.accounted,
|
|
403
|
-
unexplained: r.unexplained,
|
|
404
414
|
complete: r.complete,
|
|
405
415
|
scaffolding: r.scaffolding,
|
|
406
416
|
});
|
|
@@ -433,14 +443,21 @@ export async function think(
|
|
|
433
443
|
: null;
|
|
434
444
|
ctx.trace?.step(
|
|
435
445
|
"decideGrounding",
|
|
436
|
-
|
|
437
|
-
|
|
446
|
+
// THE LABEL IS RENDERED WHERE IT IS SHOWN. It was a field on every
|
|
447
|
+
// mechanism's result, and at every one of them it was exactly
|
|
448
|
+
// `unexplainedLabel(query, accounted)` — a second representation of a
|
|
449
|
+
// quantity one pure function already yields, computed on every response
|
|
450
|
+
// whether or not anyone looked. Here it is computed only when a rationale
|
|
451
|
+
// is attached, because `trace?.step` short-circuits its arguments.
|
|
452
|
+
candidates.map((c) => {
|
|
453
|
+
const label = unexplainedLabel(query, c.accounted);
|
|
454
|
+
return rItem(
|
|
438
455
|
c.bytes,
|
|
439
456
|
`${c.provenance} (weight ${c.weight.toFixed(3)}${
|
|
440
|
-
|
|
457
|
+
label ? `, unexplained: "${label}"` : ""
|
|
441
458
|
})`,
|
|
442
|
-
)
|
|
443
|
-
),
|
|
459
|
+
);
|
|
460
|
+
}),
|
|
444
461
|
decided ? [rItem(decided.bytes, decided.provenance)] : [],
|
|
445
462
|
"the lightest grounding derivation wins — every mechanism weighed in the one cost ladder",
|
|
446
463
|
undefined,
|
|
@@ -505,7 +522,61 @@ export async function think(
|
|
|
505
522
|
const provenance = decided.provenance as Provenance;
|
|
506
523
|
const declaredUsed = decided.used;
|
|
507
524
|
|
|
508
|
-
// ──
|
|
525
|
+
// ── THE DERIVATION STATE ─────────────────────────────────────────────
|
|
526
|
+
//
|
|
527
|
+
// THE REASONER JUDGES ITS OWN EXTENSIONS BY THE PIPELINE'S REMAINDER, not by
|
|
528
|
+
// the ladder's `accounted` — and by the SAME reading the fuse gate below uses,
|
|
529
|
+
// with the same W floor. `accounted` is a COST quantity (measured: a query
|
|
530
|
+
// fully explained by one computed span plus bridged connectors reports
|
|
531
|
+
// `accounted: []` while nothing is unexplained), and a remainder under one
|
|
532
|
+
// river-fold quantum is bridging punctuation, never a second topic — so it
|
|
533
|
+
// licenses no extension and blocks none.
|
|
534
|
+
//
|
|
535
|
+
// The state is built HERE, where these quantities are already computed, so it
|
|
536
|
+
// costs nothing new: `accounted` is what the winning transition priced,
|
|
537
|
+
// `remainder` is the coverage reading over `accounted ∪ the response's
|
|
538
|
+
// computed spans` (the union is what makes the two different quantities, and
|
|
539
|
+
// both are kept), `cost` is the ladder position, and the two declarations are
|
|
540
|
+
// the producer's own (`fixed`, `used`). What follows reads THIS state rather
|
|
541
|
+
// than a tuple rebuilt at each call site.
|
|
542
|
+
// A DERIVATION IS BORN OWING WHAT ITS ANSWER DOES NOT CARRY. The winning
|
|
543
|
+
// transition PRICED these spans, and pricing is not carrying: coverage claimed
|
|
544
|
+
// without evidence stays owed, and a later transition pays it only by carrying
|
|
545
|
+
// it (the law reads the window; see derivation.ts). Same reading, one
|
|
546
|
+
// definition — not a second spelling of it here.
|
|
547
|
+
const explained: Array<[number, number]> = [
|
|
548
|
+
...decided.accounted,
|
|
549
|
+
...pre.computed.map((u): [number, number] => [u.i, u.j]),
|
|
550
|
+
].filter(([a, b]) =>
|
|
551
|
+
windowOf([a, b], answer, query, ctx.space.maxGroup) !== null
|
|
552
|
+
);
|
|
553
|
+
// WHAT THE CONSTRUCTION WITHHOLDS, at or above one quantum: the difference between
|
|
554
|
+
// the remainder paid in full and the remainder paid by carrying. Both readings
|
|
555
|
+
// are the law's, so the floor is applied once and in one place.
|
|
556
|
+
const paidInFull = remainderOf(
|
|
557
|
+
query.length,
|
|
558
|
+
[
|
|
559
|
+
...decided.accounted,
|
|
560
|
+
...pre.computed.map((u): [number, number] => [u.i, u.j]),
|
|
561
|
+
],
|
|
562
|
+
ctx.space.maxGroup,
|
|
563
|
+
);
|
|
564
|
+
const paid = remainderOf(query.length, explained, ctx.space.maxGroup);
|
|
565
|
+
if (ctx.meter) {
|
|
566
|
+
ctx.meter.groundingWithheldBytes += unaccountedBytes(paid) -
|
|
567
|
+
unaccountedBytes(paidInFull);
|
|
568
|
+
}
|
|
569
|
+
const state: DerivationState = {
|
|
570
|
+
product: answer,
|
|
571
|
+
accounted: decided.accounted,
|
|
572
|
+
remainder: paid,
|
|
573
|
+
cost: decided.weight,
|
|
574
|
+
fixed: decided.complete,
|
|
575
|
+
used: decided.used,
|
|
576
|
+
};
|
|
577
|
+
const uncovered = state.remainder;
|
|
578
|
+
|
|
579
|
+
// ── Post-grounding, gated by the declaration and the remainder ────────
|
|
509
580
|
const preConsumed = declaredUsed ??
|
|
510
581
|
new Set(recognise(ctx, answer).sites.map((s) => s.payload));
|
|
511
582
|
// A grounding that DECLARED itself complete is not extended: the answer is
|
|
@@ -540,10 +611,13 @@ export async function think(
|
|
|
540
611
|
(id) => ctx.store.nextFirst(id, hubBound(ctx)).map((n) => read(ctx, n)),
|
|
541
612
|
);
|
|
542
613
|
// WHAT THIS BRANCH READ, published where it was read. Post-grounding decides
|
|
543
|
-
// by `decided.used
|
|
544
|
-
//
|
|
545
|
-
//
|
|
546
|
-
//
|
|
614
|
+
// by the DECLARATION (`decided.used`, which becomes `voiced`), by what the
|
|
615
|
+
// recognition already consumed (`preConsumed`) and by the derivation's own
|
|
616
|
+
// remainder — never by the provenance NAME, which is REPORTED throughout and
|
|
617
|
+
// compared nowhere (a stale comment here claimed otherwise; the register caught
|
|
618
|
+
// it, and this is the correction). The operands were invisible in the trace, so
|
|
619
|
+
// a change to the branching could not be shown equivalent or otherwise from
|
|
620
|
+
// outside — three separate investigations failed on exactly that gap. A gap in instrumentation is a defect IN the instrumentation
|
|
547
621
|
// (AGENTS.md §6): closed here, once, as counts only — never content.
|
|
548
622
|
ctx.trace?.step(
|
|
549
623
|
"postGrounding",
|
|
@@ -558,6 +632,14 @@ export async function think(
|
|
|
558
632
|
usedDeclared: decided.used !== undefined,
|
|
559
633
|
preConsumed: preConsumed.size,
|
|
560
634
|
voiced: voiced.length,
|
|
635
|
+
// THE STATE THE LAW GOVERNS, rendered where it is decided: what the asker
|
|
636
|
+
// said that no step has accounted for, in spans at or above one quantum,
|
|
637
|
+
// and whether the producer supplied a fixed point. Counts only, like
|
|
638
|
+
// every other operand here — and the spans are the state's, so a reader
|
|
639
|
+
// can check them against the meter's aggregate of the same remainder.
|
|
640
|
+
remainderSpans: state.remainder.length,
|
|
641
|
+
remainderBytes: unaccountedBytes(state.remainder),
|
|
642
|
+
fixed: state.fixed === true,
|
|
561
643
|
},
|
|
562
644
|
);
|
|
563
645
|
// REPORTABLE, NOT SILENT. A declared-complete grounding ends the derivation
|
|
@@ -575,19 +657,6 @@ export async function think(
|
|
|
575
657
|
"post-grounding extension is skipped",
|
|
576
658
|
);
|
|
577
659
|
}
|
|
578
|
-
// THE REASONER JUDGES ITS OWN EXTENSIONS BY THE PIPELINE'S REMAINDER, not by
|
|
579
|
-
// the ladder's `accounted` — and by the SAME reading the fuse gate below uses,
|
|
580
|
-
// with the same W floor. `accounted` is a COST quantity (measured: a query
|
|
581
|
-
// fully explained by one computed span plus bridged connectors reports
|
|
582
|
-
// `accounted: []` while nothing is unexplained), and a remainder under one
|
|
583
|
-
// river-fold quantum is bridging punctuation, never a second topic — so it
|
|
584
|
-
// licenses no extension and blocks none.
|
|
585
|
-
const explained: Array<[number, number]> = [
|
|
586
|
-
...decided.accounted,
|
|
587
|
-
...pre.computed.map((u): [number, number] => [u.i, u.j]),
|
|
588
|
-
];
|
|
589
|
-
const uncovered = unexplainedSpans(query.length, explained)
|
|
590
|
-
.filter(([a, b]) => b - a >= ctx.space.maxGroup);
|
|
591
660
|
// PUBLISHED, NOT RECOMPUTED: the same `uncovered` the gates below read. A
|
|
592
661
|
// write-only accounting (meter contract 1), so the number that licenses an
|
|
593
662
|
// extension or a fusion stops being invisible.
|
|
@@ -595,17 +664,18 @@ export async function think(
|
|
|
595
664
|
meter.postGroundingRemainderSpans += uncovered.length;
|
|
596
665
|
meter.postGroundingRemainderBytes += unaccountedBytes(uncovered);
|
|
597
666
|
}
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
//
|
|
667
|
+
// THE WALK CONSUMES AND RETURNS A STATE. It is handed the derivation's own —
|
|
668
|
+
// the grounding's product, accounting, remainder and cost — and hands back the
|
|
669
|
+
// state it advanced to, so what follows reads a state rather than bytes plus a
|
|
670
|
+
// tuple rebuilt here. A supplied fixed point is the one case where the walk
|
|
671
|
+
// does not run at all, and then the state is the grounding's own.
|
|
602
672
|
const extension = decided.complete ? undefined : meter
|
|
603
673
|
? await meter.time(
|
|
604
674
|
"reason",
|
|
605
|
-
() => reason(ctx, query,
|
|
675
|
+
() => reason(ctx, query, state, preConsumed, pre, voiced),
|
|
606
676
|
)
|
|
607
|
-
: await reason(ctx, query,
|
|
608
|
-
const reasoned = extension
|
|
677
|
+
: await reason(ctx, query, state, preConsumed, pre, voiced);
|
|
678
|
+
const reasoned = extension ?? state;
|
|
609
679
|
|
|
610
680
|
// Fuse only when the query has a genuine REMAINDER no mechanism's
|
|
611
681
|
// structural evidence touched at all. `decided.accounted` alone
|
|
@@ -623,7 +693,15 @@ export async function think(
|
|
|
623
693
|
// observed: a single space between two fully-computed arithmetic spans
|
|
624
694
|
// ("2+2 3+3") registered as "unaccounted" and pulled in an unrelated
|
|
625
695
|
// corpus fact, corrupting "4 6" into "4 63".
|
|
626
|
-
|
|
696
|
+
// THE GATE ASKS THE LAW, and that is an OPTIMISATION, not a tidy-up: the state
|
|
697
|
+
// above ALREADY carries the remainder (`remainderOf`, per-span, with the W
|
|
698
|
+
// floor applied), so asking it costs nothing, while the total this line used to
|
|
699
|
+
// compute (`unaccounted(explained)`) was one more sum over the spans on every
|
|
700
|
+
// response. The two readings are the same condition, not two: the ACCOUNTING
|
|
701
|
+
// applies the same W floor the gate does, so a gap below one quantum never
|
|
702
|
+
// survives into `explained` and the total cannot reach W without some single
|
|
703
|
+
// gap reaching it. Measured over twelve constructions at W = 4 (test/136.3,
|
|
704
|
+
// which pins the equivalence and both sides of it).
|
|
627
705
|
// Whether the winning candidate's entire recognised substance is
|
|
628
706
|
// COMPUTED — every accounted span exactly a pre.computed span, nothing
|
|
629
707
|
// from a genuinely recognised/climbed site. fuseAttention's lone-root
|
|
@@ -633,8 +711,8 @@ export async function think(
|
|
|
633
711
|
// `unclimbed` parameter, gated there by Attention.breadth so a
|
|
634
712
|
// coincidental echo (which this flag alone cannot distinguish) is still
|
|
635
713
|
// rejected.
|
|
636
|
-
const unclimbed =
|
|
637
|
-
|
|
714
|
+
const unclimbed = state.accounted.length > 0 &&
|
|
715
|
+
state.accounted.every(([i, j]) =>
|
|
638
716
|
pre.computed.some((u) => u.i === i && u.j === j)
|
|
639
717
|
);
|
|
640
718
|
// Where the winning grounding stands in the query — fusion places primary
|
|
@@ -644,13 +722,10 @@ export async function think(
|
|
|
644
722
|
// Exactly the cost-ladder-vs-coverage distinction `explained` above draws,
|
|
645
723
|
// read here for POSITION instead of for coverage — and resolved here, where
|
|
646
724
|
// both readings are in hand, rather than inside fuseAttention.
|
|
647
|
-
const primarySpans: ReadonlyArray<
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const fused = remainder < ctx.space.maxGroup
|
|
652
|
-
? reasoned
|
|
653
|
-
: meter
|
|
725
|
+
const primarySpans: ReadonlyArray<Span> = state.accounted.length > 0
|
|
726
|
+
? state.accounted
|
|
727
|
+
: pre.computed.map((u): [number, number] => [u.i, u.j]);
|
|
728
|
+
const fused = closed(state) ? reasoned : meter
|
|
654
729
|
? await meter.time(
|
|
655
730
|
"fuse",
|
|
656
731
|
() => fuseAttention(ctx, query, reasoned, pre, unclimbed, primarySpans),
|
|
@@ -665,7 +740,7 @@ export async function think(
|
|
|
665
740
|
);
|
|
666
741
|
|
|
667
742
|
done(
|
|
668
|
-
fused,
|
|
743
|
+
fused.product,
|
|
669
744
|
// NO CLAIM ABOUT FUSION HERE. `fuseAttention` is entered whenever a
|
|
670
745
|
// remainder ≥ W exists and returns early when there is nothing to bridge, so
|
|
671
746
|
// this note used to assert a fusion that frequently did not happen (measured:
|
|
@@ -674,5 +749,5 @@ export async function think(
|
|
|
674
749
|
// did the work is the layer that says so.
|
|
675
750
|
"grounded, reasoned forward",
|
|
676
751
|
);
|
|
677
|
-
return { bytes: fused, provenance };
|
|
752
|
+
return { bytes: fused.product, provenance };
|
|
678
753
|
}
|
package/src/mind/rationale.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
// fan-out / fan-in is visible in their lengths.
|
|
22
22
|
|
|
23
23
|
import type { Vec } from "../vec.js";
|
|
24
|
+
import { unexplainedSpans } from "./derivation.js";
|
|
24
25
|
|
|
25
26
|
/** One element of a step's input or output vector.
|
|
26
27
|
*
|
|
@@ -112,42 +113,11 @@ export function decodeText(bytes: Uint8Array): string {
|
|
|
112
113
|
return new TextDecoder().decode(bytes.filter((b) => b !== 0x00));
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
* collapsed them. Same value at every site — the control diff is identical. */
|
|
121
|
-
export function unaccountedBytes(
|
|
122
|
-
spans: ReadonlyArray<readonly [number, number]>,
|
|
123
|
-
): number {
|
|
124
|
-
let total = 0;
|
|
125
|
-
for (const [a, b] of spans) total += b - a;
|
|
126
|
-
return total;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** The `[start, end)` gaps of `[0, queryLen)` NOT covered by `accounted` —
|
|
130
|
-
* the same union-of-spans reading think's grounding decider prices at PASS
|
|
131
|
-
* per byte, exposed here so a mechanism can turn it into a human label. */
|
|
132
|
-
export function unexplainedSpans(
|
|
133
|
-
queryLen: number,
|
|
134
|
-
accounted: ReadonlyArray<[number, number]>,
|
|
135
|
-
): Array<[number, number]> {
|
|
136
|
-
const sorted = accounted
|
|
137
|
-
.map(([s, e]) =>
|
|
138
|
-
[Math.max(0, s), Math.min(queryLen, e)] as [number, number]
|
|
139
|
-
)
|
|
140
|
-
.filter(([s, e]) => e > s)
|
|
141
|
-
.sort((a, b) => a[0] - b[0]);
|
|
142
|
-
const gaps: Array<[number, number]> = [];
|
|
143
|
-
let reach = 0;
|
|
144
|
-
for (const [s, e] of sorted) {
|
|
145
|
-
if (s > reach) gaps.push([reach, s]);
|
|
146
|
-
if (e > reach) reach = e;
|
|
147
|
-
}
|
|
148
|
-
if (reach < queryLen) gaps.push([reach, queryLen]);
|
|
149
|
-
return gaps;
|
|
150
|
-
}
|
|
116
|
+
// THE SPAN ALGEBRA LIVES IN derivation.ts. `unaccountedBytes` and
|
|
117
|
+
// `unexplainedSpans` are the closure law's vocabulary — gap arithmetic over the
|
|
118
|
+
// asker's own bytes — and they moved to the layer that owns the law, so they
|
|
119
|
+
// have ONE home and are no longer asked of the tracer. This module keeps the
|
|
120
|
+
// inference TOLD as it happens, and reads the gaps only to render a label.
|
|
151
121
|
|
|
152
122
|
/** A human-readable label for the query bytes a mechanism's `accounted`
|
|
153
123
|
* spans leave unexplained — purely diagnostic (Task 2's negative evidence):
|