@hviana/sema 0.8.0 → 0.8.1
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 +22 -1
- package/DATASETS.md +1 -1
- package/dist/example/train_base/config.js +2 -2
- package/dist/example/train_base/corpora/massive.js +1 -1
- package/dist/example/train_base/readers.js +1 -1
- package/dist/src/geometry.d.ts +10 -10
- package/dist/src/geometry.js +25 -24
- package/dist/src/meter.d.ts +4 -12
- package/dist/src/meter.js +14 -14
- package/dist/src/mind/attention.js +12 -12
- package/dist/src/mind/bridge.d.ts +8 -8
- package/dist/src/mind/bridge.js +33 -32
- package/dist/src/mind/graph-search.d.ts +0 -8
- package/dist/src/mind/graph-search.js +9 -8
- package/dist/src/mind/junction.d.ts +1 -1
- package/dist/src/mind/junction.js +8 -8
- package/dist/src/mind/learning.js +36 -35
- package/dist/src/mind/match.js +14 -13
- package/dist/src/mind/mechanisms/cover.js +13 -12
- package/dist/src/mind/mechanisms/prefix-completion.js +24 -24
- package/dist/src/mind/mechanisms/recall.js +38 -40
- package/dist/src/mind/mechanisms/reference.js +16 -16
- package/dist/src/mind/mind.d.ts +6 -7
- package/dist/src/mind/pipeline-mechanism.d.ts +10 -8
- package/dist/src/mind/pipeline-mechanism.js +25 -21
- package/dist/src/mind/pipeline.d.ts +9 -9
- package/dist/src/mind/pipeline.js +24 -23
- package/dist/src/mind/primitives.d.ts +5 -5
- package/dist/src/mind/primitives.js +5 -5
- package/dist/src/mind/recognition.d.ts +14 -13
- package/dist/src/mind/recognition.js +23 -23
- package/dist/src/mind/resonance.js +21 -21
- package/dist/src/mind/traverse.d.ts +54 -52
- package/dist/src/mind/traverse.js +74 -72
- package/dist/src/mind/types.d.ts +4 -4
- package/dist/src/store.d.ts +12 -12
- package/dist/src/store.js +12 -12
- package/docs/INDEX.md +2 -2
- package/docs/architecture/exact-vs-approximate.md +2 -1
- package/docs/architecture/fold-contract.md +1 -1
- package/docs/failures/tempting-but-wrong.md +2 -3
- package/docs/harness/gates.md +7 -7
- package/example/train_base/config.ts +2 -2
- package/example/train_base/corpora/massive.ts +1 -1
- package/example/train_base/readers.ts +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/geometry.ts +25 -24
- package/src/meter.ts +14 -14
- package/src/mind/attention.ts +12 -12
- package/src/mind/bridge.ts +33 -32
- package/src/mind/graph-search.ts +9 -8
- package/src/mind/junction.ts +8 -8
- package/src/mind/learning.ts +36 -35
- package/src/mind/match.ts +20 -19
- package/src/mind/mechanisms/cover.ts +13 -12
- package/src/mind/mechanisms/prefix-completion.ts +24 -24
- package/src/mind/mechanisms/recall.ts +38 -40
- package/src/mind/mechanisms/reference.ts +16 -16
- package/src/mind/mind.ts +6 -7
- package/src/mind/pipeline-mechanism.ts +25 -21
- package/src/mind/pipeline.ts +33 -32
- package/src/mind/primitives.ts +5 -5
- package/src/mind/recognition.ts +23 -23
- package/src/mind/resonance.ts +21 -21
- package/src/mind/traverse.ts +74 -72
- package/src/mind/types.ts +4 -4
- package/src/store.ts +20 -20
- package/test/08-storage.test.mjs +1 -1
- package/test/35-prefix-edge.test.mjs +1 -1
- package/test/40-choosenext-scale-guard.test.mjs +16 -17
- package/test/56-bridge-identity-admission.test.mjs +6 -6
- package/test/70-prefix-completion.test.mjs +4 -3
- package/test/72-prefix-candidate-supply.test.mjs +3 -3
- package/test/73-scaffolding-only-bridge-abstains.test.mjs +6 -6
- package/test/75-multiturn-context-optimisation.test.mjs +5 -5
- package/test/84-composed-answer-honesty.test.mjs +5 -6
- package/test/88-dependency-footprint.test.mjs +1 -1
- package/test/89-completion-recursion.test.mjs +17 -14
- package/test/90-connector-read-cap.test.mjs +10 -8
- package/test/93-regime-prediction.test.mjs +10 -10
- package/test/94-cross-region-budget.test.mjs +2 -2
- package/test/95-wide-resonance-removed.test.mjs +8 -7
- package/test/96-bytes-walk-termination.test.mjs +3 -3
package/src/mind/traverse.ts
CHANGED
|
@@ -32,11 +32,11 @@ interface StructCache {
|
|
|
32
32
|
hasParents: Map<number, boolean>;
|
|
33
33
|
}
|
|
34
34
|
//
|
|
35
|
-
// Budgeted on the same terms as the reach memo below (
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
35
|
+
// Budgeted on the same terms as the reach memo below (caches.md): these three
|
|
36
|
+
// maps are cleared on every write, but a long read-only session over a large
|
|
37
|
+
// store converges on one entry per node per map with nothing to bound it. Past
|
|
38
|
+
// the cap all three are dropped together and re-derived, costing cold
|
|
39
|
+
// structural probes and never a wrong answer.
|
|
40
40
|
const STRUCT_MEMO_MAX = 100_000;
|
|
41
41
|
const structCaches = new WeakMap<object, StructCache>();
|
|
42
42
|
|
|
@@ -55,21 +55,22 @@ const structCaches = new WeakMap<object, StructCache>();
|
|
|
55
55
|
// battery repeatedly reaches the same corpus scaffolding even when its
|
|
56
56
|
// surface questions differ.
|
|
57
57
|
//
|
|
58
|
-
// Budgeted, not unbounded (
|
|
59
|
-
//
|
|
58
|
+
// Budgeted, not unbounded (caches.md): past the cap the whole map is dropped
|
|
59
|
+
// and
|
|
60
|
+
// re-derived, costing a cold climb and never a wrong answer.
|
|
60
61
|
const REACH_MEMO_MAX = 100_000;
|
|
61
62
|
const reachCaches = new WeakMap<object, Map<number, AncestorReach>>();
|
|
62
63
|
|
|
63
64
|
/** The reach memo this ask should use — see the note above.
|
|
64
65
|
*
|
|
65
|
-
* A TRACED response always gets a fresh, empty one.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
66
|
+
* A TRACED response always gets a fresh, empty one. `AncestorReach`'s
|
|
67
|
+
* `visited`/`maxDepth`/`saturation` fields are populated only when a trace is
|
|
68
|
+
* attached, so an entry deposited by an untraced earlier turn would silently
|
|
69
|
+
* black out the reach detail of a later traced one; and the trace's reach
|
|
70
|
+
* payload is serialised by ITERATING this map, which must therefore hold what
|
|
71
|
+
* THIS climb consulted, not the whole conversation's history. Consistent with
|
|
72
|
+
* memoization.md: a traced response is a different machine — never benchmark
|
|
73
|
+
* with a trace attached. */
|
|
73
74
|
export function sharedReachMemo(
|
|
74
75
|
ctx: MindContext,
|
|
75
76
|
): Map<number, AncestorReach> {
|
|
@@ -481,13 +482,14 @@ export function bearsEdge(ctx: MindContext, id: number): boolean {
|
|
|
481
482
|
}
|
|
482
483
|
|
|
483
484
|
/** Whether a node LEADS SOMEWHERE — it bears a continuation edge or a halo.
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
485
|
+
* The admission predicate recognition filters sites with (cover.md): a form
|
|
486
|
+
* that
|
|
487
|
+
* leads nowhere contributes nothing to any derivation. Runs once per candidate
|
|
488
|
+
* span on the recognition hot path — `hasNext` is cached per response (the same
|
|
489
|
+
* flat-branch ids are probed across prefix variants by canonicalChunkId).
|
|
490
|
+
* `hasHalo` is not cached: it's a single indexed point probe per candidate, and
|
|
491
|
+
* the candidates that reach this check have already been filtered by hasNext
|
|
492
|
+
* above in edgeAncestors. */
|
|
491
493
|
export function leadsSomewhere(ctx: MindContext, id: number): boolean {
|
|
492
494
|
const memo = getStructCache(ctx);
|
|
493
495
|
if (cachedHasNext(ctx, id, memo)) return true;
|
|
@@ -539,10 +541,10 @@ function boundFor(contextCount: number): number {
|
|
|
539
541
|
}
|
|
540
542
|
|
|
541
543
|
/** Cap a candidate list at the hub bound √N (insertion order) — the ONE
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
544
|
+
* fan-out convention every walk and disambiguation uses (see bounded-reads.md).
|
|
545
|
+
* A node connected to more than √N others is a hub whose individual connections
|
|
546
|
+
* carry ~no discriminative information; materialising or scoring them all would
|
|
547
|
+
* make single decisions scale with the corpus. */
|
|
546
548
|
export function hubCap<T>(
|
|
547
549
|
ctx: MindContext,
|
|
548
550
|
ids: readonly T[],
|
|
@@ -581,16 +583,16 @@ export function contains(
|
|
|
581
583
|
* the EXACT half's veto on calling them synonyms.
|
|
582
584
|
*
|
|
583
585
|
* Halos measure company, and the strongest company any two forms can keep is
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
586
|
+
* standing next to each other: a question and its answer co-occur in every
|
|
587
|
+
* episode that taught the pair, so their halos SHOULD be similar, and on a
|
|
588
|
+
* conversational store they are (measured on the CONV fixture: consecutive
|
|
589
|
+
* turns at 0.809 against a 0.516 concept threshold). A gate reading halo cosine
|
|
590
|
+
* alone therefore reads adjacency as synonymy and revoices an answer in the
|
|
591
|
+
* words of the question it answers — "it hangs in madrid" spliced back into
|
|
592
|
+
* "where is it kept now". The distributional layer cannot tell the two
|
|
593
|
+
* relations apart, because to it they are the same observation; the exact half
|
|
594
|
+
* can, for free, because it stored the edge. halo-sketch.md's division of
|
|
595
|
+
* labour exactly: approximate proposes, exact decides.
|
|
594
596
|
*
|
|
595
597
|
* Read LIMITed in both directions at the hub bound — a common continuation's
|
|
596
598
|
* fan-in is corpus-sized, and no single decision may scale with it. */
|
|
@@ -745,19 +747,18 @@ export function chooseNext(
|
|
|
745
747
|
// NO consensusFloor gate here (tried and reverted — see
|
|
746
748
|
// test/40-choosenext-scale-guard.test.mjs): that floor is calibrated for
|
|
747
749
|
// POOLED, IDF-weighted CLIMB VOTES (recallByResonance, commitVotes), where
|
|
748
|
-
// each corroborating region contributes at most ln N and the floor grows
|
|
749
|
-
//
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
//
|
|
753
|
-
// N-
|
|
754
|
-
//
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
//
|
|
760
|
-
// chooseNext pseudocode, which has no such floor.
|
|
750
|
+
// each corroborating region contributes at most ln N and the floor grows with
|
|
751
|
+
// N exactly as that per-region ceiling does (thresholds.md). `bestSupport`
|
|
752
|
+
// here is a different kind of quantity — a raw prevCount of how many training
|
|
753
|
+
// contexts predicted ONE destination, bounded by how often that specific fact
|
|
754
|
+
// was retold, never by corpus size N. Gating an N-invariant count against an
|
|
755
|
+
// N-growing threshold guarantees failure once N is large enough, discarding
|
|
756
|
+
// genuinely, structurally dominant edges (observed: a fact corroborated
|
|
757
|
+
// 2-to-1-1-1 refused at N≈325K, falling back to a noisy concept-hop). The
|
|
758
|
+
// loop above already IS the "genuinely competing" test: a tie leaves
|
|
759
|
+
// first-inserted as the pick (test/30's own pinned behaviour); a strict
|
|
760
|
+
// winner is real evidence regardless of corpus scale. Matches `chooseNext`'s
|
|
761
|
+
// own pseudocode, which has no such floor.
|
|
761
762
|
|
|
762
763
|
// Trace is built lazily — the filter + map below only execute when a
|
|
763
764
|
// trace listener is attached, so the common (no-trace) path pays only
|
|
@@ -838,12 +839,13 @@ function rItemShort(
|
|
|
838
839
|
* W-window it spells is contained by more places than the hub bound allows,
|
|
839
840
|
* i.e. the whole query is corpus-global scaffolding.
|
|
840
841
|
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
842
|
+
* WHAT IT IS FOR. Several mechanisms ground a query through the literal spans
|
|
843
|
+
* it
|
|
844
|
+
* did NOT explain, and those spans are the whole of their evidence. When every
|
|
845
|
+
* one of them is a hub, the query says nothing the corpus can be held to, and
|
|
846
|
+
* grounding it means picking one of thousands of continuations it gives no
|
|
847
|
+
* evidence for — a fabrication whatever the answer happens to be. Answering
|
|
848
|
+
* with silence there is the honest degradation contract (INVARIANTS.md).
|
|
847
849
|
*
|
|
848
850
|
* MEASURED SEPARATION (trained store, hubBound 571) — this is categorical,
|
|
849
851
|
* not marginal, and it is why the predicate lives here rather than being
|
|
@@ -860,11 +862,11 @@ function rItemShort(
|
|
|
860
862
|
* evidence and sit on the SAME side as the correct ones, so this predicate
|
|
861
863
|
* is not what makes them silent and cannot be credited for them.
|
|
862
864
|
*
|
|
863
|
-
* NO NEW THRESHOLD (
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
865
|
+
* NO NEW THRESHOLD (thresholds.md): `hubBound` is the √N reading of "hub" used
|
|
866
|
+
* everywhere, and the containment read is clamped to it exactly as every other
|
|
867
|
+
* fan-out read is (bounded-reads.md). A query with no stored window at all is
|
|
868
|
+
* NOT scaffolding-only — it has no evidence either way, and its callers already
|
|
869
|
+
* refuse it on their own terms. */
|
|
868
870
|
export function allWindowsAreScaffolding(
|
|
869
871
|
ctx: MindContext,
|
|
870
872
|
query: Uint8Array,
|
|
@@ -916,19 +918,19 @@ export function allWindowsAreScaffolding(
|
|
|
916
918
|
* by climbing containment then parents. Nothing is added to the write side;
|
|
917
919
|
* this reads an index training already built.
|
|
918
920
|
*
|
|
919
|
-
* BOUNDED (
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
925
|
-
*
|
|
926
|
-
*
|
|
927
|
-
*
|
|
921
|
+
* BOUNDED (bounded-reads.md), AND WITH NO NEW THRESHOLD. The window whose
|
|
922
|
+
* containment is SMALLEST carries the most evidence, and one saturated at
|
|
923
|
+
* `hubBound` carries none — that is the same √N reading of "hub" the rest of
|
|
924
|
+
* the mind uses, not a tuned knob. The upward walk spends a budget of
|
|
925
|
+
* `hubBound` nodes and fans out by W, so a hub query enumerates nothing and the
|
|
926
|
+
* caller stays silent rather than guessing (INVARIANTS.md). Measured on the
|
|
927
|
+
* trained store: the photosynthesis form at a one-byte truncation picks a
|
|
928
|
+
* window with 52 containers, visits 446 nodes, and yields exactly ONE candidate
|
|
929
|
+
* that survives the caller's byte compare — the form itself.
|
|
928
930
|
*
|
|
929
|
-
* These are PROPOSALS only.
|
|
930
|
-
*
|
|
931
|
-
*
|
|
931
|
+
* These are PROPOSALS only. Every candidate still faces the byte-exact prefix
|
|
932
|
+
* compare and all three guards below, so a wrong proposal costs one bounded
|
|
933
|
+
* read and can never be voiced (exact-vs-approximate.md). */
|
|
932
934
|
export function formsOpenedBy(
|
|
933
935
|
ctx: MindContext,
|
|
934
936
|
query: Uint8Array,
|
package/src/mind/types.ts
CHANGED
|
@@ -297,10 +297,10 @@ export type AItem =
|
|
|
297
297
|
export interface MindContext extends GraphSearchHost {
|
|
298
298
|
store: Store;
|
|
299
299
|
/** The work accumulator for the inference call in flight, or null when
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
300
|
+
* nothing is profiling — see src/meter.ts. WRITE-ONLY from the engine's point
|
|
301
|
+
* of view: no inference decision may read a counter, or determinism is gone
|
|
302
|
+
* (determinism.md). Every call site is `ctx.meter?.x++`, so an unprofiled
|
|
303
|
+
* response allocates nothing. */
|
|
304
304
|
meter: Meter | null;
|
|
305
305
|
space: Space;
|
|
306
306
|
alphabet: Alphabet;
|
package/src/store.ts
CHANGED
|
@@ -541,16 +541,16 @@ export interface Store {
|
|
|
541
541
|
// selected by identity hash so the choice is a property of each constituent
|
|
542
542
|
// and never of where it sits in the fold.
|
|
543
543
|
//
|
|
544
|
-
// DURABLE DERIVED STATE, NOT A CACHE.
|
|
544
|
+
// DURABLE DERIVED STATE, NOT A CACHE. caches.md permits a cache to cost only
|
|
545
545
|
// speed; this decides which terms enter a halo — a learned relation — so an
|
|
546
|
-
// eviction would change the geometry rather than slow it down.
|
|
546
|
+
// eviction would change the geometry rather than slow it down. It is
|
|
547
547
|
// therefore written like the canon index: computed once, kept, never
|
|
548
|
-
// budgeted.
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
//
|
|
548
|
+
// budgeted. Soundness rests on the set being INTRINSIC — minimality, `len ≥
|
|
549
|
+
// W` and non-domination are properties of the node's own subtree and do not
|
|
550
|
+
// move as the corpus grows. The one corpus-dependent reading, the hub
|
|
551
|
+
// exclusion, is deliberately NOT stored: it is applied by the caller at pour
|
|
552
|
+
// time over the ≤ k candidates, which is the drift companyProfile already
|
|
553
|
+
// documents as benign and one-directional.
|
|
554
554
|
//
|
|
555
555
|
// Backends that do not implement the pair leave both absent; companyProfile
|
|
556
556
|
// then recomputes the sketch per pour and simply loses the amortisation.
|
|
@@ -1789,18 +1789,18 @@ export abstract class AbstractStore implements Store {
|
|
|
1789
1789
|
* remainders must fit the budget. Scattered differences leave a wide
|
|
1790
1790
|
* middle and are rejected.
|
|
1791
1791
|
*
|
|
1792
|
-
* Every read here is CAPPED (
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
1800
|
-
*
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1792
|
+
* Every read here is CAPPED (bounded-reads.md). It used to open with
|
|
1793
|
+
* `bytesPrefix(k, Number.MAX_SAFE_INTEGER)` — the ALL sentinel, i.e. the full
|
|
1794
|
+
* materialising `bytes()` read — on the deposit hot path, and only then
|
|
1795
|
+
* compare lengths. So a candidate the length test was about to reject had
|
|
1796
|
+
* already been reconstructed byte for byte. The LENGTHS decide first instead,
|
|
1797
|
+
* from the `contentLen` memo the interning order has already built bottom-up,
|
|
1798
|
+
* and the target's length is itself read under a cap: a target longer than
|
|
1799
|
+
* `la + W` is rejected without touching one of its bytes. Same semantics —
|
|
1800
|
+
* the old capped `b` read would have produced `a.length + W + 1` here and
|
|
1801
|
+
* failed the very same test — strictly fewer byte reads. The `+ 1` on each
|
|
1802
|
+
* byte cap keeps `_prefix`'s "complete reconstruction" test true, so the
|
|
1803
|
+
* results still cache. */
|
|
1804
1804
|
private differsByOneWindow(
|
|
1805
1805
|
kids: NodeId[],
|
|
1806
1806
|
targetId: NodeId,
|
package/test/08-storage.test.mjs
CHANGED
|
@@ -361,7 +361,7 @@ test("a halo accumulates poured signatures and gates on mass", async () => {
|
|
|
361
361
|
});
|
|
362
362
|
|
|
363
363
|
// A multi-turn conversation is deposited as ACCUMULATED-CONTEXT episodes — the
|
|
364
|
-
// pattern
|
|
364
|
+
// pattern example/train.ts uses:
|
|
365
365
|
// (t0) → t1
|
|
366
366
|
// (t0 + t1) → t2
|
|
367
367
|
// (t0 + t1 + t2) → t3
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// parents, or (halo > 0 ∧ already an edge source). Pure answers do
|
|
6
6
|
// not qualify — they are destinations, not sources.
|
|
7
7
|
//
|
|
8
|
-
// All phrases verified via instrumentation first
|
|
8
|
+
// All phrases verified via instrumentation first.
|
|
9
9
|
|
|
10
10
|
import { test } from "node:test";
|
|
11
11
|
import assert from "node:assert/strict";
|
|
@@ -10,23 +10,22 @@
|
|
|
10
10
|
// already requires strict dominance; a tie leaves first-inserted as the
|
|
11
11
|
// pick, exactly the "no real winner" case a floor would matter for.
|
|
12
12
|
//
|
|
13
|
-
// But chooseNext ALSO gated this pick on `bestSupport < consensusFloor(N)`
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// strict-dominance loop already IS the "genuinely competing" test.
|
|
13
|
+
// But chooseNext ALSO gated this pick on `bestSupport < consensusFloor(N)` once
|
|
14
|
+
// the corpus scale crosses atomIsHub's threshold (traverse.ts:541-546) —
|
|
15
|
+
// reusing the SAME ln(N)+0.5 floor recallByResonance and commitVotes use for
|
|
16
|
+
// POOLED, IDF-weighted CLIMB VOTES (each region worth up to ln N, so a sum
|
|
17
|
+
// exceeding ln N + 0.5 is more than any one region could say alone —
|
|
18
|
+
// thresholds.md). `prevCount(candidate)` is a different kind of quantity: a raw
|
|
19
|
+
// count of how many training contexts independently predicted ONE destination,
|
|
20
|
+
// bounded by how many times that specific fact was retold — NOT by corpus size
|
|
21
|
+
// N. Gating an N-invariant count against an N-growing threshold guarantees
|
|
22
|
+
// failure once N is large enough (verified live: N≈325K gives a floor of
|
|
23
|
+
// ≈13.19, so a genuinely dominant but only-doubly-attested fact like "capital
|
|
24
|
+
// of France → Paris" was refused, falling back to a noisy concept-hop that
|
|
25
|
+
// produced the wrong answer). The canonical `chooseNext` pseudocode has NO such
|
|
26
|
+
// floor — it is undocumented implementation drift, not a deliberate design
|
|
27
|
+
// surface. Fix: remove the gate; chooseNext's existing strict-dominance loop
|
|
28
|
+
// already IS the "genuinely competing" test.
|
|
30
29
|
|
|
31
30
|
import { test } from "node:test";
|
|
32
31
|
import assert from "node:assert/strict";
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
// separated from the query only by material that does not change what the
|
|
4
4
|
// text SAYS, is the SAME learnt form and grounds through its own edge.
|
|
5
5
|
//
|
|
6
|
-
// "Material that does not change what it says" has ONE definition here, and
|
|
7
|
-
//
|
|
6
|
+
// "Material that does not change what it says" has ONE definition here, and it
|
|
7
|
+
// is read from the corpus, never tuned (commonality.md, corpus-global
|
|
8
8
|
// population): a span is EXPLAINED when it is sub-quantum (< W — typographic
|
|
9
|
-
// glue) or every W-window in it is COMMON by the store's own climb (the
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
9
|
+
// glue) or every W-window in it is COMMON by the store's own climb (the ascent
|
|
10
|
+
// saturates, or it reaches a majority of contexts). A window that reaches
|
|
11
|
+
// NOTHING is novel content and is never explained — the reading that separates
|
|
12
|
+
// a droppable "the process of " from a load-bearing "heavy ".
|
|
13
13
|
//
|
|
14
14
|
// THE GAP THIS CLOSES (measured on the 17.9M-node trained store). The query
|
|
15
15
|
// `Who wrote Romeo and Juliet?` against the trained `Who wrote "Romeo and
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// 70-prefix-completion.test.mjs — a query that IS the opening of one trained
|
|
2
2
|
// form is completed by that form's remainder; anything less is refused.
|
|
3
3
|
//
|
|
4
|
-
// WHAT THE MECHANISM DOES (src/mind/prefix-completion.ts): when
|
|
5
|
-
// tier has declined, scan the candidate list recall's refusal path
|
|
6
|
-
// fetched and look for a trained form whose bytes literally BEGIN
|
|
4
|
+
// WHAT THE MECHANISM DOES (src/mind/mechanisms/prefix-completion.ts): when
|
|
5
|
+
// every other tier has declined, scan the candidate list recall's refusal path
|
|
6
|
+
// has ALREADY fetched and look for a trained form whose bytes literally BEGIN
|
|
7
|
+
// with the whole
|
|
7
8
|
// query. The answer is that form's own remainder — never an invention.
|
|
8
9
|
//
|
|
9
10
|
// WHY IT IS NEEDED, measured on the 15.7M-node trained store:
|
|
@@ -98,9 +98,9 @@ test("a proper prefix reaches its trained form through the window supply", async
|
|
|
98
98
|
"a FORM is grounded whole, never a slice cut at the query's end",
|
|
99
99
|
);
|
|
100
100
|
|
|
101
|
-
// HONEST DEGRADATION (
|
|
102
|
-
// propose nothing rather than guess — silence is the correct answer, and
|
|
103
|
-
// supply that widened until it found something would be the real defect.
|
|
101
|
+
// HONEST DEGRADATION (INVARIANTS.md). A query with no discriminative window
|
|
102
|
+
// must propose nothing rather than guess — silence is the correct answer, and
|
|
103
|
+
// a supply that widened until it found something would be the real defect.
|
|
104
104
|
const hub = enc("The ");
|
|
105
105
|
assert.equal(
|
|
106
106
|
prefixCompletion(m, hub, formsOpenedBy(m, hub)),
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
// ABSTAIN when every literal span it did not substitute is corpus-global
|
|
3
3
|
// scaffolding.
|
|
4
4
|
//
|
|
5
|
-
// THE DEFECT THIS PINS.
|
|
6
|
-
// substitute; those anchors are the whole of its evidence.
|
|
5
|
+
// THE DEFECT THIS PINS. A bridge grounds through the literal spans it did NOT
|
|
6
|
+
// substitute; those anchors are the whole of its evidence. The anchor scan
|
|
7
7
|
// ranked them by containment but rejected only the ones with ZERO containers,
|
|
8
8
|
// so a query made entirely of scaffolding still bridged — the single
|
|
9
9
|
// substituted span carried the whole semantic load, and the answer was voiced
|
|
10
|
-
// with confidence.
|
|
10
|
+
// with confidence. Measured on the trained store (hubBound 571): "What is the
|
|
11
11
|
// capital of" has 19 anchors, ALL saturated ("What":572, "hat ":572, "at i":572
|
|
12
|
-
// …), and answered with an unrelated trained context about an integral.
|
|
13
|
-
// breaks honest silence (
|
|
14
|
-
// fabrication is not.
|
|
12
|
+
// …), and answered with an unrelated trained context about an integral. That
|
|
13
|
+
// breaks honest silence (INVARIANTS.md), which is worse than a gap: a gap is
|
|
14
|
+
// visible, a fabrication is not.
|
|
15
15
|
//
|
|
16
16
|
// WHY THIS IS NOT A PROBE-SHAPED PATCH. The gate was falsified against the
|
|
17
17
|
// queries the bridge answers CORRECTLY before it was written, and every one of
|
|
@@ -966,11 +966,11 @@ test("F1: every turn of a trained conversation is answered exactly", async () =>
|
|
|
966
966
|
test("F1b: attaching a trace changes no answer — the audit layer is inert", async () => {
|
|
967
967
|
// The mind's ONLY text-shaped code lives in the rationale/trace payloads:
|
|
968
968
|
// attention.ts's `dec` helper decodes bytes and collapses whitespace so an
|
|
969
|
-
// audit line is readable, and frame-filler builds diagnostic strings the
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
//
|
|
969
|
+
// audit line is readable, and frame-filler builds diagnostic strings the same
|
|
970
|
+
// way. Neither may ever reach a decision — nothing in the core knows what
|
|
971
|
+
// "whitespace" is (see canon.ts's header, and memoization.md: profile and
|
|
972
|
+
// trace must not move an answer). Asserted here rather than assumed, because
|
|
973
|
+
// the formatting sits inside the same functions that decide.
|
|
974
974
|
const pairs = [
|
|
975
975
|
[
|
|
976
976
|
"who painted the weeping woman",
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
//
|
|
33
33
|
// TO REPRODUCE THE REAL FAILURE: build the same chain, then ingest ~6,000
|
|
34
34
|
// deposits produced by the Taskmaster adapter (example/train_base/corpora/
|
|
35
|
-
// taskmaster.ts) from
|
|
36
|
-
// TM-2/TM-3/TM-4, and ask the two-hop question. See FINDINGS.md §A1/§A4.
|
|
35
|
+
// taskmaster.ts) from TM-2/TM-3/TM-4, and ask the two-hop question.
|
|
37
36
|
|
|
38
37
|
import { test } from "node:test";
|
|
39
38
|
import assert from "node:assert/strict";
|
|
@@ -93,10 +92,10 @@ test("each hop still answers on its own — the substrate is intact", async () =
|
|
|
93
92
|
|
|
94
93
|
test("a two-hop query composes or stays silent — it never fabricates", async () => {
|
|
95
94
|
// THE CONTRACT. Three outcomes are conceivable and only two are acceptable:
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
95
|
+
// compose -> the answer contains Paris silence -> the empty answer, which is
|
|
96
|
+
// honest (INVARIANTS.md) fabricate-> an assembly carrying content from an
|
|
97
|
+
// unrelated deposit The third is what a store past the real-text ceiling
|
|
98
|
+
// actually does.
|
|
100
99
|
const mind = await storeWithDistractors();
|
|
101
100
|
const answer = await mind.respondText(TWO_HOP);
|
|
102
101
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// 88 — the dependency footprint is a PRODUCT PROPERTY, so it is tested.
|
|
2
2
|
//
|
|
3
|
-
// AGENTS.md §
|
|
3
|
+
// AGENTS.md §7: "do not add runtime dependencies casually — the near-zero-
|
|
4
4
|
// dependency footprint is a product feature." A feature stated only in prose
|
|
5
5
|
// erodes; this suite pins it at the two places it can actually break.
|
|
6
6
|
//
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
// 89-completion-recursion.test.mjs — the completion recursion must be
|
|
2
2
|
// OUTPUT-SENSITIVE.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// What no guard covered is the NUMBER of reads: `recompleteNode`
|
|
4
|
+
// bounded-reads.md: "No per-query read may grow with the corpus." That law is
|
|
5
|
+
// enforced per READ (nextFirst, bytesPrefix, …), and every one of those caps
|
|
6
|
+
// holds. What no guard covered is the NUMBER of reads: `recompleteNode`
|
|
7
7
|
// (src/mind/graph-search.ts) re-covers a produced node by calling `solve`
|
|
8
|
-
// recursively, and each nested solve builds its own agenda and chart.
|
|
8
|
+
// recursively, and each nested solve builds its own agenda and chart. Its own
|
|
9
9
|
// doc states the intent —
|
|
10
10
|
//
|
|
11
11
|
// "its cost tracks the ANSWER's own structure, not how densely the corpus
|
|
12
12
|
// interconnects the nodes passed through"
|
|
13
13
|
//
|
|
14
14
|
// — but argues termination from "Distinct node ids are finite and each finished
|
|
15
|
-
// completion is memoised".
|
|
16
|
-
// forbids, and the recursion is emitted at `cost: 0` while the
|
|
17
|
-
// own `cost` is computed and discarded, so A* has no gradient
|
|
15
|
+
// completion is memoised". Finite-in-the-corpus is exactly the bound
|
|
16
|
+
// bounded-reads.md forbids, and the recursion is emitted at `cost: 0` while the
|
|
17
|
+
// nested cover's own `cost` is computed and discarded, so A* has no gradient
|
|
18
|
+
// against depth.
|
|
18
19
|
//
|
|
19
20
|
// MEASURED on a trained store (18,938,834 nodes, edgeSourceCount 796,528):
|
|
20
21
|
// `respond("Hi")` reached recursion depth 331 and 9.1 GB RSS in 56 s without
|
|
@@ -221,8 +222,9 @@ test("completion recursion: per-query work does not grow with the corpus", async
|
|
|
221
222
|
} (agenda pops) — target ≪ 1 (sublinear in the corpus)`,
|
|
222
223
|
);
|
|
223
224
|
|
|
224
|
-
// THE LAW.
|
|
225
|
-
//
|
|
225
|
+
// THE LAW. Same answer, more corpus, so cost must not move. k ≈ 0 is flat, k
|
|
226
|
+
// ≈
|
|
227
|
+
// 1 is linear in the corpus — the bound bounded-reads.md forbids outright.
|
|
226
228
|
//
|
|
227
229
|
// `searches` counts nested solve() calls, which is the recursion itself and
|
|
228
230
|
// nothing else, so it gets 14-scaling.test.mjs's stricter 0.6 bar. Measured
|
|
@@ -234,17 +236,18 @@ test("completion recursion: per-query work does not grow with the corpus", async
|
|
|
234
236
|
`nested solve() builds its own agenda and chart, so this is the ` +
|
|
235
237
|
`completion recursion doing work the answer never asked for`,
|
|
236
238
|
);
|
|
237
|
-
// A LOOSER BAR, FOR A REASON.
|
|
239
|
+
// A LOOSER BAR, FOR A REASON. `searchPops` aggregates the TOP-LEVEL cover's
|
|
238
240
|
// agenda too, and that one legitimately carries some corpus sensitivity: a
|
|
239
241
|
// bigger store recognises more sites inside the same query, so more items are
|
|
240
|
-
// admissible.
|
|
241
|
-
// this asserts the law itself, k < 1, rather than the
|
|
242
|
-
// a counter the fix governs end to end.
|
|
242
|
+
// admissible. Only outright linear growth is the forbidden case
|
|
243
|
+
// (bounded-reads.md), so this asserts the law itself, k < 1, rather than the
|
|
244
|
+
// stricter 0.6 that suits a counter the fix governs end to end. Measured 1.40
|
|
245
|
+
// unfixed, 0.54 fixed.
|
|
243
246
|
assert.ok(
|
|
244
247
|
kPops < 1,
|
|
245
248
|
`agenda pops grew with exponent k=${kPops.toFixed(2)} in corpus size (${
|
|
246
249
|
pops.join(" → ")
|
|
247
250
|
}) for a byte-identical answer — k≈1 is work LINEAR in the corpus, which ` +
|
|
248
|
-
`is the bound
|
|
251
|
+
`is the bound bounded-reads.md forbids outright`,
|
|
249
252
|
);
|
|
250
253
|
});
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
// substring search, so it needs the candidate's bytes; it used to reconstruct
|
|
8
8
|
// them in FULL via `read(ctx, answer)`, whose maxLen defaults to ALL.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
10
|
+
// bounded-reads.md, prefix-capped reads: "a candidate that exceeds the cap is
|
|
11
11
|
// rejected without reconstructing it — the weave, the junction walks and the
|
|
12
12
|
// bridge all read this way, and uncapped reads there cost seconds per query on
|
|
13
|
-
// a large store."
|
|
13
|
+
// a large store." This probe was the exception: it runs hubBound(ctx) = √N
|
|
14
14
|
// times PER SITE.
|
|
15
15
|
//
|
|
16
16
|
// The probe's corpus-scale cost was once claimed from a trained-store
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
// prompt" — but that number was measured on a `respond()` query, where the
|
|
19
19
|
// probe does NOT execute (`answeredSpans` is empty there, so the enclosing
|
|
20
20
|
// guard returns first). It is therefore not attributable to the probe and is
|
|
21
|
-
// not repeated here (
|
|
21
|
+
// not repeated here (a comment asserting a measurement inherits Gate 1).
|
|
22
22
|
// The probe runs only on a multi-turn `respondTurn` response; its benefit there
|
|
23
23
|
// is still unmeasured.
|
|
24
24
|
//
|
|
25
|
-
// WHAT THIS PINS.
|
|
26
|
-
// could (see below).
|
|
27
|
-
// and what rescues a SHORT query: candidates averaged 231
|
|
28
|
-
// against a 3-byte prompt.
|
|
25
|
+
// WHAT THIS PINS. The cap cannot reduce the read COUNT — only a semantic change
|
|
26
|
+
// could (see below). It bounds each read by the QUERY, which is what
|
|
27
|
+
// bounded-reads.md asks and what rescues a SHORT query: candidates averaged 231
|
|
28
|
+
// B reconstructed against a 3-byte prompt. So the invariant here is per-read
|
|
29
|
+
// SIZE.
|
|
29
30
|
//
|
|
30
31
|
// It is measured by calling `resolveConnectors` DIRECTLY and diffing the meter
|
|
31
32
|
// across it. A whole-response counter cannot express this: `bytesRead` sums
|
|
@@ -121,7 +122,8 @@ test("connector probe reads by the query, not by the learnt continuation", async
|
|
|
121
122
|
`the connector probe averaged ${perRead.toFixed(0)} B per read for a ` +
|
|
122
123
|
`${QUERY.length} B query — a candidate longer than the query cannot ` +
|
|
123
124
|
`occur inside it, so it must be rejected on an overflow probe of ` +
|
|
124
|
-
`${QUERY.length + 1} B, not reconstructed in full
|
|
125
|
+
`${QUERY.length + 1} B, not reconstructed in full ` +
|
|
126
|
+
`(bounded-reads.md)`,
|
|
125
127
|
);
|
|
126
128
|
} finally {
|
|
127
129
|
mind.endResponse();
|