@hviana/sema 0.9.0 → 0.9.2
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 +7 -7
- package/dist/src/alu/src/index.d.ts +1 -1
- package/dist/src/alu/src/index.js +1 -1
- package/dist/src/alu/src/parser.js +2 -6
- package/dist/src/alu/src/resonance.d.ts +13 -0
- package/dist/src/alu/src/resonance.js +41 -0
- package/dist/src/alu/test/alu.test.js +39 -0
- package/dist/src/bytes.d.ts +6 -2
- package/dist/src/bytes.js +10 -4
- package/dist/src/canon.js +44 -0
- package/dist/src/geometry.d.ts +19 -1
- package/dist/src/geometry.js +125 -141
- package/dist/src/meter.d.ts +33 -0
- package/dist/src/meter.js +34 -1
- package/dist/src/mind/articulation.js +14 -1
- package/dist/src/mind/attention.d.ts +12 -0
- package/dist/src/mind/attention.js +44 -16
- package/dist/src/mind/bridge.js +3 -3
- package/dist/src/mind/derivation.d.ts +40 -0
- package/dist/src/mind/derivation.js +34 -0
- package/dist/src/mind/evidence.d.ts +24 -0
- package/dist/src/mind/evidence.js +90 -0
- package/dist/src/mind/graph-search.d.ts +89 -15
- package/dist/src/mind/graph-search.js +345 -174
- package/dist/src/mind/learning.js +1 -1
- package/dist/src/mind/mechanisms/cover.d.ts +19 -3
- package/dist/src/mind/mechanisms/cover.js +142 -61
- package/dist/src/mind/mechanisms/recall.js +10 -3
- package/dist/src/mind/mind.d.ts +6 -0
- package/dist/src/mind/mind.js +5 -2
- package/dist/src/mind/pipeline.d.ts +5 -1
- package/dist/src/mind/pipeline.js +220 -90
- package/dist/src/mind/primitives.d.ts +25 -5
- package/dist/src/mind/primitives.js +107 -44
- package/dist/src/mind/reasoning.d.ts +18 -4
- package/dist/src/mind/reasoning.js +487 -328
- package/dist/src/mind/recognition.js +29 -13
- package/dist/src/mind/resonance.js +1 -11
- package/dist/src/mind/traverse.d.ts +45 -5
- package/dist/src/mind/traverse.js +285 -8
- package/dist/src/mind/types.d.ts +16 -1
- package/dist/src/store-sqlite.d.ts +25 -0
- package/dist/src/store-sqlite.js +89 -1
- package/dist/src/store.d.ts +48 -4
- package/dist/src/store.js +86 -6
- package/docs/INDEX.md +20 -19
- package/docs/INVARIANTS.md +17 -16
- package/docs/architecture/bounded-reads.md +1 -1
- package/docs/architecture/caches.md +5 -4
- package/docs/architecture/closure.md +45 -5
- package/docs/architecture/cost-model.md +16 -0
- package/docs/architecture/evidence.md +113 -0
- package/docs/architecture/exact-vs-approximate.md +10 -9
- package/docs/architecture/factored-machinery.md +14 -13
- package/docs/architecture/fold-contract.md +51 -1
- package/docs/architecture/mechanism-market.md +21 -0
- package/docs/architecture/memoization.md +3 -3
- package/docs/architecture/meter.md +2 -1
- package/docs/architecture/saturation.md +12 -0
- package/docs/architecture/store.md +25 -2
- package/docs/failures/tempting-but-wrong.md +13 -2
- package/docs/harness/gates.md +12 -10
- package/docs/mechanisms/cover.md +23 -6
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +10 -2
- package/src/alu/src/index.ts +1 -0
- package/src/alu/src/parser.ts +6 -6
- package/src/alu/src/resonance.ts +42 -0
- package/src/alu/test/alu.test.ts +40 -0
- package/src/bytes.ts +13 -3
- package/src/canon.ts +40 -0
- package/src/geometry.ts +183 -154
- package/src/meter.ts +34 -1
- package/src/mind/articulation.ts +14 -2
- package/src/mind/attention.ts +47 -25
- package/src/mind/bridge.ts +3 -3
- package/src/mind/derivation.ts +77 -0
- package/src/mind/evidence.ts +107 -0
- package/src/mind/graph-search.ts +449 -221
- package/src/mind/learning.ts +1 -7
- package/src/mind/match.ts +1 -2
- package/src/mind/mechanisms/cast.ts +1 -2
- package/src/mind/mechanisms/cover.ts +207 -87
- package/src/mind/mechanisms/extraction.ts +1 -2
- package/src/mind/mechanisms/prefix-completion.ts +1 -1
- package/src/mind/mechanisms/recall.ts +17 -5
- package/src/mind/mechanisms/reference.ts +1 -1
- package/src/mind/mind.ts +9 -30
- package/src/mind/pipeline.ts +263 -104
- package/src/mind/primitives.ts +119 -43
- package/src/mind/reasoning.ts +611 -419
- package/src/mind/recognition.ts +24 -9
- package/src/mind/resonance.ts +2 -16
- package/src/mind/trace.ts +1 -1
- package/src/mind/traverse.ts +321 -8
- package/src/mind/types.ts +15 -11
- package/src/store-sqlite.ts +92 -1
- package/src/store.ts +113 -7
- package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
- package/test/106-the-join-fires.test.mjs +21 -0
- package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
- package/test/136-the-two-named-limits.test.mjs +3 -2
- package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
- package/test/148-exact-shortcuts-agree.test.mjs +188 -0
- package/test/149-the-closure-engine.test.mjs +138 -0
- package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
- package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
- package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
- package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
- package/test/154-the-question-names-the-step.test.mjs +281 -0
- package/test/24-generalization.test.mjs +32 -0
- package/test/36-bloom.test.mjs +53 -0
- package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
- package/test/48-recognise-turn-connective.test.mjs +3 -2
- package/test/55-cost-meter.test.mjs +4 -4
- package/test/90-connector-read-cap.test.mjs +7 -7
package/dist/src/store-sqlite.js
CHANGED
|
@@ -124,6 +124,17 @@ CREATE TABLE IF NOT EXISTS canon (
|
|
|
124
124
|
id INTEGER NOT NULL,
|
|
125
125
|
PRIMARY KEY (h, id)
|
|
126
126
|
) WITHOUT ROWID;
|
|
127
|
+
-- The canon index's negative filter, persisted so an open does not rescan the
|
|
128
|
+
-- h column (seconds on a trained store). Written in the SAME transaction as
|
|
129
|
+
-- the canon rows it covers, stamped with the meta 'canon.upto' of that commit;
|
|
130
|
+
-- a stamp that disagrees with the meta (rows a writer added without it) makes
|
|
131
|
+
-- the filter stale, and it is rebuilt from the table instead.
|
|
132
|
+
CREATE TABLE IF NOT EXISTS canon_bloom (
|
|
133
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
134
|
+
bits BLOB NOT NULL,
|
|
135
|
+
n INTEGER NOT NULL,
|
|
136
|
+
upto TEXT NOT NULL
|
|
137
|
+
);
|
|
127
138
|
-- CONSTITUENT SKETCH (Store.sketchGet/sketchPut): the bottom-k minimal
|
|
128
139
|
-- constituents of a node's subtree, k = √D, chosen by identity hash. The blob is
|
|
129
140
|
-- a packed int32 little-endian run, already in hash order; an EMPTY blob is a
|
|
@@ -223,6 +234,20 @@ export class SQliteStore extends AbstractStore {
|
|
|
223
234
|
_bloom = null;
|
|
224
235
|
/** Dedup probes answered by the filter alone this session (observability). */
|
|
225
236
|
bloomSkips = 0;
|
|
237
|
+
/** The same negative filter over the CANON index's key hashes. Recognition's
|
|
238
|
+
* canonical admission and the join's canonical entity scan ask `canonFind`
|
|
239
|
+
* once per probed span, and almost every answer is "no such key" — on an
|
|
240
|
+
* index that is often EMPTY (it is built only by `buildCanonIndex`).
|
|
241
|
+
* Loaded on first use from `canon_bloom` when its stamp matches the meta,
|
|
242
|
+
* else built by one sequential scan of the h column; kept exact on
|
|
243
|
+
* `canonAdd` (rebuilt bigger from the table, uncommitted rows included,
|
|
244
|
+
* when growth saturates it) and persisted with the commit that wrote the
|
|
245
|
+
* rows. The canon table is never deleted from, so the filter can never
|
|
246
|
+
* hold a false negative: a miss it reports is a miss the query would have
|
|
247
|
+
* returned. */
|
|
248
|
+
_canonBloom = null;
|
|
249
|
+
/** The in-memory canon filter differs from the persisted one. */
|
|
250
|
+
_canonBloomDirty = false;
|
|
226
251
|
_insertNode = null;
|
|
227
252
|
_insertKid = null;
|
|
228
253
|
_selContain = null;
|
|
@@ -453,8 +478,10 @@ export class SQliteStore extends AbstractStore {
|
|
|
453
478
|
"n = excluded.n, upto = excluded.upto").run(this._bloom.bits, this._bloom.n, this._nextId);
|
|
454
479
|
}
|
|
455
480
|
_dbClose() {
|
|
456
|
-
if (this.sqlite)
|
|
481
|
+
if (this.sqlite) {
|
|
457
482
|
this._bloomPersist();
|
|
483
|
+
this._canonBloomPersist();
|
|
484
|
+
}
|
|
458
485
|
if (this.content) {
|
|
459
486
|
this.content.close();
|
|
460
487
|
this.content = null;
|
|
@@ -478,6 +505,8 @@ export class SQliteStore extends AbstractStore {
|
|
|
478
505
|
_dbCommitTx() {
|
|
479
506
|
if (!this._inTx || !this.sqlite)
|
|
480
507
|
return;
|
|
508
|
+
// The canon filter commits WITH the rows it covers — see canon_bloom.
|
|
509
|
+
this._canonBloomPersist();
|
|
481
510
|
this._inTx = false; // clear first so a throw can't wedge us mid-commit
|
|
482
511
|
this.sqlite.exec("COMMIT");
|
|
483
512
|
}
|
|
@@ -534,6 +563,16 @@ export class SQliteStore extends AbstractStore {
|
|
|
534
563
|
const row = this._selFlat.get(h, bytes);
|
|
535
564
|
return row ? row.id : null;
|
|
536
565
|
}
|
|
566
|
+
/** The node filter alone: never a false negative (every inserted hash is
|
|
567
|
+
* added before any probe can see it), so `false` is exact. */
|
|
568
|
+
_dbFlatMayExist(h, bytes) {
|
|
569
|
+
if (this._bloom === null)
|
|
570
|
+
return super._dbFlatMayExist(h, bytes);
|
|
571
|
+
if (this._bloom.mightContain(h))
|
|
572
|
+
return true;
|
|
573
|
+
this.bloomSkips++;
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
537
576
|
_dbFindBranchByKids(h, packed) {
|
|
538
577
|
if (this._bloom && !this._bloom.mightContain(h)) {
|
|
539
578
|
this.bloomSkips++;
|
|
@@ -855,8 +894,57 @@ export class SQliteStore extends AbstractStore {
|
|
|
855
894
|
// bulk index build coalesces instead of paying autocommit per row.
|
|
856
895
|
this._dbBeginTx();
|
|
857
896
|
this._insCanon.run(h, id);
|
|
897
|
+
const b = this._canonFilter();
|
|
898
|
+
b.add(h);
|
|
899
|
+
// Rebuilt bigger at once, from the table as THIS connection sees it — the
|
|
900
|
+
// persisted filter cannot stand in, it lacks this transaction's rows.
|
|
901
|
+
if (b.saturated)
|
|
902
|
+
this._canonBloom = this._canonScan();
|
|
903
|
+
this._canonBloomDirty = true;
|
|
904
|
+
}
|
|
905
|
+
/** The canon filter: the persisted one when its stamp matches the meta,
|
|
906
|
+
* else built from the index as it stands. */
|
|
907
|
+
_canonFilter() {
|
|
908
|
+
if (this._canonBloom === null) {
|
|
909
|
+
const row = this.sqlite.prepare("SELECT bits, n, upto FROM canon_bloom WHERE id = 1").get();
|
|
910
|
+
if (row !== undefined && row.upto === this._canonStamp()) {
|
|
911
|
+
const b = new NodeBloom(31 - Math.clz32(row.bits.length * 8));
|
|
912
|
+
b.bits.set(row.bits);
|
|
913
|
+
b.n = row.n;
|
|
914
|
+
this._canonBloom = b;
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
this._canonBloom = this._canonScan();
|
|
918
|
+
this._canonBloomDirty = true;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return this._canonBloom;
|
|
922
|
+
}
|
|
923
|
+
_canonScan() {
|
|
924
|
+
const b = new NodeBloom(bloomLog2For(this.canonCount()));
|
|
925
|
+
const scan = this.sqlite.prepare("SELECT h FROM canon");
|
|
926
|
+
scan.setReturnArrays(true);
|
|
927
|
+
for (const r of scan.iterate())
|
|
928
|
+
b.add(r[0]);
|
|
929
|
+
return b;
|
|
930
|
+
}
|
|
931
|
+
/** What a persisted canon filter is stamped with: the incremental build's
|
|
932
|
+
* cursor, which every canon writer advances in the transaction it writes. */
|
|
933
|
+
_canonStamp() {
|
|
934
|
+
return this._dbGetMeta("canon.upto") ?? "";
|
|
935
|
+
}
|
|
936
|
+
_canonBloomPersist() {
|
|
937
|
+
const b = this._canonBloom;
|
|
938
|
+
if (b === null || !this._canonBloomDirty || !this.sqlite)
|
|
939
|
+
return;
|
|
940
|
+
this.sqlite.prepare("INSERT INTO canon_bloom (id, bits, n, upto) VALUES (1, ?, ?, ?) " +
|
|
941
|
+
"ON CONFLICT(id) DO UPDATE SET bits = excluded.bits, " +
|
|
942
|
+
"n = excluded.n, upto = excluded.upto").run(b.bits, b.n, this._canonStamp());
|
|
943
|
+
this._canonBloomDirty = false;
|
|
858
944
|
}
|
|
859
945
|
canonFind(h) {
|
|
946
|
+
if (!this._canonFilter().mightContain(h))
|
|
947
|
+
return [];
|
|
860
948
|
if (!this._selCanon) {
|
|
861
949
|
this._selCanon = this.sqlite.prepare("SELECT id FROM canon WHERE h = ?");
|
|
862
950
|
}
|
package/dist/src/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Vec } from "./vec.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { StoreConfig } from "./config.js";
|
|
3
3
|
import type { Meter } from "./meter.js";
|
|
4
4
|
/** A node id: a dense, non-negative integer assigned in creation order. */
|
|
5
5
|
export type NodeId = number;
|
|
@@ -166,6 +166,20 @@ export interface Store {
|
|
|
166
166
|
* bytes — the allocation-free probe span scanners use. Optional: a store
|
|
167
167
|
* without it is simply probed through `findBranch`. */
|
|
168
168
|
findFlatBranch?(bytes: Uint8Array): NodeId | null;
|
|
169
|
+
/** Whether a flat branch with these bytes MAY exist. `false` is EXACT — no
|
|
170
|
+
* such node exists, and a {@link findFlatBranch} would return null; `true`
|
|
171
|
+
* means only that a lookup is needed. The existence half of the probe,
|
|
172
|
+
* without its verification: a caller that will resolve the bytes anyway
|
|
173
|
+
* (and so verify them) asks this to refuse a miss without paying for the
|
|
174
|
+
* lookup a hit would repeat. Optional, like the probe it answers for. */
|
|
175
|
+
flatBranchMayExist?(bytes: Uint8Array): boolean;
|
|
176
|
+
/** A {@link findFlatBranch} over spans of ONE buffer: `probe(start, end)`
|
|
177
|
+
* answers exactly `findFlatBranch(bytes.subarray(start, end))`, but a span's
|
|
178
|
+
* content hash extends the one its start was last probed at, so a scanner
|
|
179
|
+
* that sweeps ends upward per start pays O(1) per probe instead of
|
|
180
|
+
* O(span). The buffer must not change while the prober is used.
|
|
181
|
+
* Optional, like the probe it accelerates. */
|
|
182
|
+
flatSpans?(bytes: Uint8Array): (start: number, end: number) => NodeId | null;
|
|
169
183
|
/** The branch nodes that list `id` among their children — the reverse of
|
|
170
184
|
* `get(id).kids`. Lets the structural DAG be climbed upward, from a
|
|
171
185
|
* recognised fragment to the larger learned forms that contain it. */
|
|
@@ -314,6 +328,12 @@ export interface Store {
|
|
|
314
328
|
* the search's fuse guard) must ask this instead: one row read, a mass
|
|
315
329
|
* compare, no decode. */
|
|
316
330
|
hasHalo(id: NodeId): boolean;
|
|
331
|
+
/** THE ADMISSION PREDICATE — whether `id` LEADS SOMEWHERE: it bears a
|
|
332
|
+
* continuation edge ({@link hasNext}) or a halo ({@link hasHalo}). A form
|
|
333
|
+
* that leads nowhere contributes nothing to any derivation. This is the ONE
|
|
334
|
+
* raw definition; `traverse.ts`'s `leadsSomewhere` is the same predicate with
|
|
335
|
+
* its edge tier memoised for the response. Two point probes at most. */
|
|
336
|
+
leadsSomewhere(id: NodeId): boolean;
|
|
317
337
|
/** How many episode signatures were poured into `id`'s halo — the DIRECT
|
|
318
338
|
* measure of distributional evidence (each training pair pours once, so
|
|
319
339
|
* repetition counts, unlike {@link prevCount}, which counts DISTINCT
|
|
@@ -507,6 +527,9 @@ export declare abstract class AbstractStore implements Store {
|
|
|
507
527
|
/** Exact-content dedup: content-key → node id. Intrinsic compression. */
|
|
508
528
|
protected readonly _leafKey: BoundedMap<string, NodeId>;
|
|
509
529
|
protected readonly _branchKey: BoundedMap<string, NodeId>;
|
|
530
|
+
/** {@link findFlatBranch}'s hits, keyed by the bytes themselves (latin1 —
|
|
531
|
+
* exact, unlike the hash keys above). */
|
|
532
|
+
protected readonly _flatKey: BoundedMap<string, NodeId>;
|
|
510
533
|
/** Reconstructed-bytes read cache (regenerable), keyed by node id. */
|
|
511
534
|
protected readonly _bytesCache: BoundedMap<NodeId, Uint8Array>;
|
|
512
535
|
/** contentLen memo — content is immutable, so entries never invalidate. */
|
|
@@ -643,10 +666,29 @@ export declare abstract class AbstractStore implements Store {
|
|
|
643
666
|
* answers most of those with no I/O at all, so the allocations dominated.
|
|
644
667
|
*
|
|
645
668
|
* Pass a subarray: it is a view, so a caller scanning spans of a query
|
|
646
|
-
* allocates nothing per probe.
|
|
647
|
-
*
|
|
648
|
-
*
|
|
669
|
+
* allocates nothing per probe that misses. HITS ARE MEMOIZED, MISSES ARE
|
|
670
|
+
* NOT: the negative filter answers first, so a span that is not stored
|
|
671
|
+
* builds no key; one that may be pays a key and then usually skips the
|
|
672
|
+
* lookup — the spans the identity fold names are segments, a few bytes
|
|
673
|
+
* each, asked over and over by every span that contains them. A node, once
|
|
674
|
+
* minted, keeps its bytes and its id, so a cached hit never goes stale. */
|
|
649
675
|
findFlatBranch(bytes: Uint8Array): NodeId | null;
|
|
676
|
+
/** {@link Store.flatSpans}. Each start keeps the hash of the span it was
|
|
677
|
+
* last probed to, plus the hash one byte short of it; a probe ending at or
|
|
678
|
+
* past that end extends it, one ending one byte short of it reuses the
|
|
679
|
+
* second, and any other probe hashes from the start. `hashOf` is FNV-1a,
|
|
680
|
+
* a left fold over the bytes, so an extended hash IS the span's hash —
|
|
681
|
+
* same key, same filter answer, same lookup. */
|
|
682
|
+
flatSpans(bytes: Uint8Array): (start: number, end: number) => NodeId | null;
|
|
683
|
+
/** {@link findFlatBranch} past the hash: `h` must be `hashOf(bytes)`. */
|
|
684
|
+
private _findFlatHashed;
|
|
685
|
+
/** {@link Store.flatBranchMayExist} — the backend's negative filter when it
|
|
686
|
+
* keeps one ({@link _dbFlatMayExist}), else the lookup itself. */
|
|
687
|
+
flatBranchMayExist(bytes: Uint8Array): boolean;
|
|
688
|
+
/** Default: no filter, so anything MAY exist and the lookup decides. A
|
|
689
|
+
* backend with a negative filter over node hashes overrides this to answer
|
|
690
|
+
* from the filter alone. */
|
|
691
|
+
protected _dbFlatMayExist(_h: number, _bytes: Uint8Array): boolean;
|
|
650
692
|
findBranch(kids: NodeId[]): NodeId | null;
|
|
651
693
|
parents(id: NodeId): NodeId[];
|
|
652
694
|
parentsFirst(id: NodeId, limit: number): NodeId[];
|
|
@@ -792,6 +834,8 @@ export declare abstract class AbstractStore implements Store {
|
|
|
792
834
|
/** {@link Store.hasHalo} — MUST mirror {@link halo}'s null condition
|
|
793
835
|
* exactly (row present AND mass ≥ minHaloMass), minus the decode. */
|
|
794
836
|
hasHalo(id: NodeId): boolean;
|
|
837
|
+
/** {@link Store.leadsSomewhere} — edge first (the cheaper, commoner probe). */
|
|
838
|
+
leadsSomewhere(id: NodeId): boolean;
|
|
795
839
|
pourHalo(id: NodeId, add: Vec): Promise<void>;
|
|
796
840
|
resonateHalo(v: Vec, k: number): Promise<Hit[]>;
|
|
797
841
|
private pending;
|
package/dist/src/store.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
// essentials required for SQLite + VectorDatabase communication.
|
|
30
30
|
import { addInto, copy, dot, normalize } from "./vec.js";
|
|
31
31
|
import { identityBar } from "./geometry.js";
|
|
32
|
+
import { latin1 } from "./bytes.js";
|
|
32
33
|
/** Entry cap for the two ANN read caches ({@link AbstractStore._resonateCache}
|
|
33
34
|
* / `_resonateHaloCache`). They are only dropped on index writes, so a long
|
|
34
35
|
* query-only session otherwise grows them without bound — a slow memory/GC
|
|
@@ -335,11 +336,13 @@ export function unpackKids(blob) {
|
|
|
335
336
|
}
|
|
336
337
|
/** 32-bit FNV-1a of a byte blob — the integer content hash `idx_node_h` keys
|
|
337
338
|
* on. Collisions are resolved by verifying the stored blob, never trusted. */
|
|
339
|
+
const FNV_OFFSET = 0x811c9dc5 >>> 0;
|
|
340
|
+
const FNV_PRIME = 0x01000193;
|
|
338
341
|
function hashOf(bytes) {
|
|
339
|
-
let h =
|
|
342
|
+
let h = FNV_OFFSET;
|
|
340
343
|
for (let i = 0; i < bytes.length; i++) {
|
|
341
344
|
h ^= bytes[i];
|
|
342
|
-
h = Math.imul(h,
|
|
345
|
+
h = Math.imul(h, FNV_PRIME) >>> 0;
|
|
343
346
|
}
|
|
344
347
|
return h >>> 0;
|
|
345
348
|
}
|
|
@@ -458,6 +461,9 @@ export class AbstractStore {
|
|
|
458
461
|
/** Exact-content dedup: content-key → node id. Intrinsic compression. */
|
|
459
462
|
_leafKey;
|
|
460
463
|
_branchKey;
|
|
464
|
+
/** {@link findFlatBranch}'s hits, keyed by the bytes themselves (latin1 —
|
|
465
|
+
* exact, unlike the hash keys above). */
|
|
466
|
+
_flatKey;
|
|
461
467
|
/** Reconstructed-bytes read cache (regenerable), keyed by node id. */
|
|
462
468
|
_bytesCache;
|
|
463
469
|
/** contentLen memo — content is immutable, so entries never invalidate. */
|
|
@@ -542,6 +548,7 @@ export class AbstractStore {
|
|
|
542
548
|
this.compactEveryNWrites = config.compactEveryNWrites;
|
|
543
549
|
this._leafKey = new BoundedMap(config.dedupCacheMax);
|
|
544
550
|
this._branchKey = new BoundedMap(config.dedupCacheMax);
|
|
551
|
+
this._flatKey = new BoundedMap(config.dedupCacheMax, undefined, "lru", "clock");
|
|
545
552
|
this._bytesCache = new BoundedMap(config.bytesCacheMax, (v) => v.byteLength, "smallest", "clock");
|
|
546
553
|
this._lenCache = new BoundedMap(config.bytesCacheMax, () => 16);
|
|
547
554
|
this._recCache = new BoundedMap(config.recCacheBytes, (r) => (r.leaf?.byteLength ?? 0) + (r.kids?.length ?? 0) * 4 + 12, "lru", "clock");
|
|
@@ -848,13 +855,82 @@ export class AbstractStore {
|
|
|
848
855
|
* answers most of those with no I/O at all, so the allocations dominated.
|
|
849
856
|
*
|
|
850
857
|
* Pass a subarray: it is a view, so a caller scanning spans of a query
|
|
851
|
-
* allocates nothing per probe.
|
|
852
|
-
*
|
|
853
|
-
*
|
|
858
|
+
* allocates nothing per probe that misses. HITS ARE MEMOIZED, MISSES ARE
|
|
859
|
+
* NOT: the negative filter answers first, so a span that is not stored
|
|
860
|
+
* builds no key; one that may be pays a key and then usually skips the
|
|
861
|
+
* lookup — the spans the identity fold names are segments, a few bytes
|
|
862
|
+
* each, asked over and over by every span that contains them. A node, once
|
|
863
|
+
* minted, keeps its bytes and its id, so a cached hit never goes stale. */
|
|
854
864
|
findFlatBranch(bytes) {
|
|
865
|
+
return this._findFlatHashed(hashOf(bytes), bytes);
|
|
866
|
+
}
|
|
867
|
+
/** {@link Store.flatSpans}. Each start keeps the hash of the span it was
|
|
868
|
+
* last probed to, plus the hash one byte short of it; a probe ending at or
|
|
869
|
+
* past that end extends it, one ending one byte short of it reuses the
|
|
870
|
+
* second, and any other probe hashes from the start. `hashOf` is FNV-1a,
|
|
871
|
+
* a left fold over the bytes, so an extended hash IS the span's hash —
|
|
872
|
+
* same key, same filter answer, same lookup. */
|
|
873
|
+
flatSpans(bytes) {
|
|
874
|
+
const n = bytes.length;
|
|
875
|
+
const runEnd = new Int32Array(n + 1).fill(-1);
|
|
876
|
+
const runH = new Uint32Array(n + 1);
|
|
877
|
+
const shortH = new Uint32Array(n + 1);
|
|
878
|
+
return (start, end) => {
|
|
879
|
+
let h;
|
|
880
|
+
let from;
|
|
881
|
+
const e0 = runEnd[start];
|
|
882
|
+
if (e0 >= 0 && e0 <= end) {
|
|
883
|
+
h = runH[start];
|
|
884
|
+
from = e0;
|
|
885
|
+
}
|
|
886
|
+
else if (e0 >= 0 && e0 - 1 === end && end > start) {
|
|
887
|
+
h = shortH[start];
|
|
888
|
+
from = end;
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
h = FNV_OFFSET;
|
|
892
|
+
from = start;
|
|
893
|
+
}
|
|
894
|
+
let prev = h;
|
|
895
|
+
for (let i = from; i < end; i++) {
|
|
896
|
+
prev = h;
|
|
897
|
+
h = Math.imul(h ^ bytes[i], FNV_PRIME) >>> 0;
|
|
898
|
+
}
|
|
899
|
+
if (from < end) {
|
|
900
|
+
runEnd[start] = end;
|
|
901
|
+
runH[start] = h;
|
|
902
|
+
shortH[start] = prev;
|
|
903
|
+
}
|
|
904
|
+
return this._findFlatHashed(h, bytes.subarray(start, end));
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
/** {@link findFlatBranch} past the hash: `h` must be `hashOf(bytes)`. */
|
|
908
|
+
_findFlatHashed(h, bytes) {
|
|
855
909
|
if (this.meter)
|
|
856
910
|
this.meter.branchLookups++;
|
|
857
|
-
|
|
911
|
+
if (!this._dbFlatMayExist(h, bytes))
|
|
912
|
+
return null;
|
|
913
|
+
const key = bytes.length <= DEDUP_KEY_MAX ? latin1(bytes) : null;
|
|
914
|
+
if (key !== null) {
|
|
915
|
+
const hit = this._flatKey.get(key);
|
|
916
|
+
if (hit !== undefined)
|
|
917
|
+
return hit;
|
|
918
|
+
}
|
|
919
|
+
const id = this._dbFindBranchByLeaf(h, bytes);
|
|
920
|
+
if (id !== null && key !== null)
|
|
921
|
+
this._flatKey.set(key, id);
|
|
922
|
+
return id;
|
|
923
|
+
}
|
|
924
|
+
/** {@link Store.flatBranchMayExist} — the backend's negative filter when it
|
|
925
|
+
* keeps one ({@link _dbFlatMayExist}), else the lookup itself. */
|
|
926
|
+
flatBranchMayExist(bytes) {
|
|
927
|
+
return this._dbFlatMayExist(hashOf(bytes), bytes);
|
|
928
|
+
}
|
|
929
|
+
/** Default: no filter, so anything MAY exist and the lookup decides. A
|
|
930
|
+
* backend with a negative filter over node hashes overrides this to answer
|
|
931
|
+
* from the filter alone. */
|
|
932
|
+
_dbFlatMayExist(_h, _bytes) {
|
|
933
|
+
return true;
|
|
858
934
|
}
|
|
859
935
|
findBranch(kids) {
|
|
860
936
|
if (this.meter)
|
|
@@ -1731,6 +1807,10 @@ export class AbstractStore {
|
|
|
1731
1807
|
const r = this._dbGetHalo(id);
|
|
1732
1808
|
return r !== null && r.mass >= this.minHaloMass;
|
|
1733
1809
|
}
|
|
1810
|
+
/** {@link Store.leadsSomewhere} — edge first (the cheaper, commoner probe). */
|
|
1811
|
+
leadsSomewhere(id) {
|
|
1812
|
+
return this.hasNext(id) || this.hasHalo(id);
|
|
1813
|
+
}
|
|
1734
1814
|
async pourHalo(id, add) {
|
|
1735
1815
|
await this._ensureReady();
|
|
1736
1816
|
// A node with a halo is a genuine resonance target — the consensus climb
|
package/docs/INDEX.md
CHANGED
|
@@ -19,24 +19,25 @@ proof in `test/` (pins that fail when the law is broken).
|
|
|
19
19
|
| Change vector search | `docs/architecture/exact-vs-approximate.md` + `docs/architecture/bounded-reads.md` | Scores propose, bytes dispose; ANN is bounded by `hubBound` |
|
|
20
20
|
| Profile or bound work | `docs/architecture/meter.md` + `docs/architecture/bounded-reads.md` | `meter.ts` is write-only; counters are product, phases are hints |
|
|
21
21
|
|
|
22
|
-
## Architecture laws (
|
|
22
|
+
## Architecture laws (15)
|
|
23
23
|
|
|
24
|
-
| Law | File | Summary
|
|
25
|
-
| --- | ------------------------------------------- |
|
|
26
|
-
| 1 | `docs/architecture/determinism.md` | No `Math.random`/`Date.now` in behaviour; seed-derived randomness; corpus-determined tie-breaks
|
|
27
|
-
| 2 | `docs/architecture/thresholds.md` | Every decision cutoff derived in `geometry.ts` over D/W/N; no tunable knobs
|
|
28
|
-
| 3 | `docs/architecture/exact-vs-approximate.md` | Vector scores rank only; identity via content-addressed lookup;
|
|
29
|
-
| 4 | `docs/architecture/cost-model.md` | Single ladder `MICRO`/`STEP`/`CONCEPT`/`PASS`; weight `moves + PASS·unaccounted`; `STEP`-grade compare
|
|
30
|
-
| 5 | `docs/architecture/match-project.md` | Shared `match.ts` family (`locate`/`alignGraded`/`frameSlots`/`project`); voicing gates belong to consumers
|
|
31
|
-
| 6 | `docs/architecture/mechanism-market.md` | `PipelineMechanism` (`floor`/`run`/`parse`); admissible-floor pruning
|
|
32
|
-
| 7 | `docs/architecture/commonality.md` | Three: global (`reachOf`+`dominates`), weave-local (`depth[]`), window rarity
|
|
33
|
-
| 8 | `docs/architecture/bounded-reads.md` | No per-query read grows with N; `hubBound=√N` enforced at store via LIMIT/probe/prefix caps
|
|
34
|
-
| 9 | `docs/architecture/store.md` | `AbstractStore` owns dedup/indexing/batch; `store-sqlite.ts` is thin wrappers; canon index optional
|
|
35
|
-
| 10 | `docs/architecture/fold-contract.md` | `perceiveDeposit` and `perceive` agree; `contentLevels` is single boundary rule; no W/offset dependence
|
|
36
|
-
| 11 | `docs/architecture/memoization.md` | `Precomputed` is per-response lazy cache (promise-cached async); `beginResponse`/`endResponse` lifecycle
|
|
37
|
-
| 12 | `docs/architecture/saturation.md` | Every walk names a deciding saturation beside its cap; cap is safety net, not decision
|
|
38
|
-
| 13 | `docs/architecture/meter.md` | `meter.ts` is write-only work accounting; counts are exact, phases nest
|
|
39
|
-
| 14 | `docs/architecture/closure.md` | A derivation is closed when its structure accounts for the question's remainder; every transition asks that law | `test/133`–`
|
|
24
|
+
| Law | File | Summary | Pins |
|
|
25
|
+
| --- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
|
|
26
|
+
| 1 | `docs/architecture/determinism.md` | No `Math.random`/`Date.now` in behaviour; seed-derived randomness; corpus-determined tie-breaks | `test/20` |
|
|
27
|
+
| 2 | `docs/architecture/thresholds.md` | Every decision cutoff derived in `geometry.ts` over D/W/N; no tunable knobs | `test/40`, `test/64` |
|
|
28
|
+
| 3 | `docs/architecture/exact-vs-approximate.md` | Vector scores rank only; identity via content-addressed lookup; six graded ladders | `test/51` |
|
|
29
|
+
| 4 | `docs/architecture/cost-model.md` | Single ladder `MICRO`/`STEP`/`CONCEPT`/`PASS`; weight `moves + PASS·unaccounted`; `STEP`-grade compare | `test/04`, `test/55` |
|
|
30
|
+
| 5 | `docs/architecture/match-project.md` | Shared `match.ts` family (`locate`/`alignGraded`/`frameSlots`/`project`); voicing gates belong to consumers | `test/24`, `test/76` |
|
|
31
|
+
| 6 | `docs/architecture/mechanism-market.md` | `PipelineMechanism` (`floor`/`run`/`parse`); admissible-floor pruning, investment discipline, run-ahead bounds | `test/01`, `test/04`, `test/153` |
|
|
32
|
+
| 7 | `docs/architecture/commonality.md` | Three: global (`reachOf`+`dominates`), weave-local (`depth[]`), window rarity | `test/17`, `test/34` |
|
|
33
|
+
| 8 | `docs/architecture/bounded-reads.md` | No per-query read grows with N; `hubBound=√N` enforced at store via LIMIT/probe/prefix caps | `test/77`, `test/90` |
|
|
34
|
+
| 9 | `docs/architecture/store.md` | `AbstractStore` owns dedup/indexing/batch; `store-sqlite.ts` is thin wrappers; canon index optional | `test/08` |
|
|
35
|
+
| 10 | `docs/architecture/fold-contract.md` | `perceiveDeposit` and `perceive` agree; the read side names a branch as `intern` does; `contentLevels` is single boundary rule; no W/offset dependence | `test/59`, `test/63`, `test/148`, `test/152` |
|
|
36
|
+
| 11 | `docs/architecture/memoization.md` | `Precomputed` is per-response lazy cache (promise-cached async); `beginResponse`/`endResponse` lifecycle | `test/42` |
|
|
37
|
+
| 12 | `docs/architecture/saturation.md` | Every walk names a deciding saturation beside its cap; cap is safety net, not decision | `test/27`, `test/16` |
|
|
38
|
+
| 13 | `docs/architecture/meter.md` | `meter.ts` is write-only work accounting; counts are exact, phases nest | `test/55` |
|
|
39
|
+
| 14 | `docs/architecture/closure.md` | A derivation is closed when its structure accounts for the question's remainder; every transition asks that law, one engine walks the layers | `test/133`–`151` |
|
|
40
|
+
| 15 | `docs/architecture/evidence.md` | A stored form is identified when the material at hand (question ∪ the node a derivation stands on) witnesses every byte of it, order-free; the question NAMES a continuation through its establishing context; a step it did not name pays from what is still owed | `test/154` |
|
|
40
41
|
|
|
41
42
|
## Mechanisms (8)
|
|
42
43
|
|
|
@@ -65,6 +66,6 @@ proof in `test/` (pins that fail when the law is broken).
|
|
|
65
66
|
exact-decides, one cost currency, bounded reads) with file-level routing.
|
|
66
67
|
- `docs/failures/tempting-but-wrong.md` — refuted simplifications that passed
|
|
67
68
|
review but failed pins.
|
|
68
|
-
- `docs/harness/gates.md` — how `AGENTS.md` recipes,
|
|
69
|
-
`
|
|
69
|
+
- `docs/harness/gates.md` — how `AGENTS.md` recipes, the meter's public path
|
|
70
|
+
(`profile: true` → `mind.lastCost`), and `test/*.test.mjs` enforce the laws.
|
|
70
71
|
- `docs/architecture/` — per-law derivation: each file says why, not how.
|
package/docs/INVARIANTS.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# INVARIANTS — Laws, Proofs, Derivations
|
|
2
2
|
|
|
3
|
-
| # | Law | Defined in
|
|
4
|
-
| -- | ------------------------------------ |
|
|
5
|
-
| 1 | Determinism | `src/config.ts:seed` `src/alphabet.ts:Alphabet` `src/mind/traverse.ts:guidedFirst`
|
|
6
|
-
| 2 | Derived thresholds | `src/geometry.ts:mergeThreshold,identityBar,reachThreshold,significanceBar,consensusFloor,dominates`
|
|
7
|
-
| 3 | Exact decides / approximate proposes | `src/mind/primitives.ts:resolve` `src/mind/match.ts:locate,alignGraded` `src/mind/resonance.ts:bridge`
|
|
8
|
-
| 4 | One cost currency | `src/mind/graph-search.ts:MICRO,STEP,CONCEPT,PASS` `src/derive:lightestDerivation` (min,+) `src/mind/attention.ts:poolVotes` (+,+)
|
|
9
|
-
| 5 | Bounded reads | `src/store.ts:AbstractStore:nextFirst,parentsFirst,containersSlice,hasNext,bytesPrefix` `src/mind/traverse.ts:hubBound,hubCap`
|
|
10
|
-
| 6 | Fold contract | `src/geometry.ts:contentLevels` `src/mind/canonical.ts:canonicalWindows,chainReach` `src/canon.ts:canonicalizer`
|
|
11
|
-
| 7 | Mechanism market | `src/mind/pipeline-mechanism.ts:PipelineMechanism,Precomputed` `src/mind/pipeline.ts:think,worthRunning`
|
|
12
|
-
| 8 | Two commonality measures | `src/mind/traverse.ts:reachOf,dominates,corpusN` (global) `cast.ts:depth[],MIN_WEAVE` (weave-local)
|
|
13
|
-
| 9 | Memoization idempotence | `src/mind/pipeline-mechanism.ts:Precomputed` `src/mind/mind.ts:beginResponse,endResponse,_resolvedSubtrees`
|
|
14
|
-
| 10 | Caches as budgets | `src/store.ts:BoundedMap` `src/config.ts:StoreConfig:bytesCacheMax,recCacheBytes,haloCacheBytes`
|
|
15
|
-
| 11 | Honest degradation | `src/mind/pipeline.ts:weight=moves+PASS*unaccounted` `src/store.ts:BoundedMap:miss→re-derive`
|
|
16
|
-
| 12 | Meter contracts | `src/meter.ts:Meter,PhaseCost,time` `src/mind/pipeline-mechanism.ts:Precomputed.shared`
|
|
17
|
-
| 13 | Saturation | `traverse.ts:edgeAncestors,types.ts:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto`
|
|
18
|
-
| 14 | Closure | `src/mind/derivation.ts:
|
|
3
|
+
| # | Law | Defined in | Pins | Doc |
|
|
4
|
+
| -- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------- |
|
|
5
|
+
| 1 | Determinism | `src/config.ts:seed` `src/alphabet.ts:Alphabet` `src/mind/traverse.ts:guidedFirst` | `test/20` `test/42` | `determinism.md` |
|
|
6
|
+
| 2 | Derived thresholds | `src/geometry.ts:mergeThreshold,identityBar,reachThreshold,significanceBar,consensusFloor,dominates` | `test/64` `test/40` | `thresholds.md` |
|
|
7
|
+
| 3 | Exact decides / approximate proposes | `src/mind/primitives.ts:resolve` `src/mind/match.ts:locate,alignGraded` `src/mind/resonance.ts:bridge` | `test/51` | `exact-vs-approximate.md` |
|
|
8
|
+
| 4 | One cost currency | `src/mind/graph-search.ts:MICRO,STEP,CONCEPT,PASS` `src/derive:lightestDerivation` (min,+) `src/mind/attention.ts:poolVotes` (+,+) | `test/55` `test/04` | `cost-model.md` |
|
|
9
|
+
| 5 | Bounded reads | `src/store.ts:AbstractStore:nextFirst,parentsFirst,containersSlice,hasNext,bytesPrefix` `src/mind/traverse.ts:hubBound,hubCap` | `test/90` `test/14` | `bounded-reads.md` |
|
|
10
|
+
| 6 | Fold contract | `src/geometry.ts:contentLevels,contentIdentity` `src/mind/primitives.ts:branchNaming` `src/mind/canonical.ts:canonicalWindows,chainReach` `src/canon.ts:canonicalizer` | `test/59` `test/63` `test/152` | `fold-contract.md` |
|
|
11
|
+
| 7 | Mechanism market | `src/mind/pipeline-mechanism.ts:PipelineMechanism,Precomputed` `src/mind/pipeline.ts:think,worthRunning` | `test/01` `test/04` `test/153` | `mechanism-market.md` |
|
|
12
|
+
| 8 | Two commonality measures | `src/mind/traverse.ts:reachOf,dominates,corpusN` (global) `cast.ts:depth[],MIN_WEAVE` (weave-local) | `test/17` `test/34` | `commonality.md` |
|
|
13
|
+
| 9 | Memoization idempotence | `src/mind/pipeline-mechanism.ts:Precomputed` `src/mind/mind.ts:beginResponse,endResponse,_resolvedSubtrees` | `test/42` | `memoization.md` |
|
|
14
|
+
| 10 | Caches as budgets | `src/store.ts:BoundedMap` `src/config.ts:StoreConfig:bytesCacheMax,recCacheBytes,haloCacheBytes` | `test/96` `test/91` | `caches.md` |
|
|
15
|
+
| 11 | Honest degradation | `src/mind/pipeline.ts:weight=moves+PASS*unaccounted` `src/store.ts:BoundedMap:miss→re-derive` | `test/28` `test/84` | `store.md`+`caches.md` |
|
|
16
|
+
| 12 | Meter contracts | `src/meter.ts:Meter,PhaseCost,time` `src/mind/pipeline-mechanism.ts:Precomputed.shared` | `test/55` | `meter.md` |
|
|
17
|
+
| 13 | Saturation | `traverse.ts:edgeAncestors,types.ts:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto` | `test/27` `test/16` | `saturation.md` |
|
|
18
|
+
| 14 | Closure | `src/mind/derivation.ts:admissible,advance,closeOver` | `test/133`–`151` | `closure.md` |
|
|
19
|
+
| 15 | Witnessed evidence | `src/mind/evidence.ts:witness,windowIndex` `src/mind/traverse.ts:chooseNext,askedEvidence,namedContinuations,scaffoldExtents` | `test/154` | `evidence.md` |
|
|
@@ -81,5 +81,5 @@ net); a walk with only a cap drifts to the cap.
|
|
|
81
81
|
length; training throughput floor; exact recall at scale.
|
|
82
82
|
- `test/89` — completion recursion stays output-sensitive (nested searches/pops
|
|
83
83
|
sublinear); guards the count of reads, not just per-read size.
|
|
84
|
-
- `test/90` — connector probe (`
|
|
84
|
+
- `test/90` — connector probe (`offerConnectors`) reads by the query length
|
|
85
85
|
(`QUERY.length + 1`), not by the learnt continuation; per-read size bound.
|
|
@@ -31,6 +31,7 @@ candidate window for `"smallest"` never rescans from front.
|
|
|
31
31
|
| Cache | Field | Budget | `sizeOf` | Eviction |
|
|
32
32
|
| ------------------- | -------------------------- | ---------------------------- | ------------------- | -------------- |
|
|
33
33
|
| dedup leaf/branch | `_leafKey` / `_branchKey` | `dedupCacheMax` 1M entries | 1 | lru |
|
|
34
|
+
| flat-branch hits | `_flatKey` | `dedupCacheMax` 1M entries | 1 | lru+clock |
|
|
34
35
|
| reconstructed bytes | `_bytesCache` | `bytesCacheMax` 20 MB | `byteLength` | smallest+clock |
|
|
35
36
|
| content length | `_lenCache` | `bytesCacheMax` | 16 | lru |
|
|
36
37
|
| node records | `_recCache` | `recCacheBytes` 10 MB | leaf+4·kids+12 | lru+clock |
|
|
@@ -76,10 +77,10 @@ must discharge it.
|
|
|
76
77
|
`contentFoldIncremental` segments bit-identically.
|
|
77
78
|
- Wrong: mismatched `prev` reused by offset produced wrong tree (336 vs 400
|
|
78
79
|
bytes) — a coincidental prefix length aliased unrelated content.
|
|
79
|
-
- Now: `perceiveDeposit` keys by `
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
- Now: `perceiveDeposit` keys by `latin1(bytes.subarray(0,L))` (prefix bytes),
|
|
81
|
+
probes longest cached proper prefix first; `_depositTrees` populated only for
|
|
82
|
+
conversational deposits (budget discipline), otherwise cold path always
|
|
83
|
+
correct.
|
|
83
84
|
|
|
84
85
|
## Pins
|
|
85
86
|
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
> **Law:** a step is admitted only when it CLOSES the derivation, or MOVES to
|
|
4
4
|
> structure it has not consumed, or CARRIES material the asker left unaccounted.
|
|
5
5
|
|
|
6
|
-
One unit and one
|
|
7
|
-
chart's frontier, the market's candidates, the post-grounding
|
|
8
|
-
the mechanisms' own gates. None re-spells a condition the law
|
|
6
|
+
One unit, one law and one engine, asked by every tier that decides whether to
|
|
7
|
+
continue: the chart's frontier, the market's candidates, the post-grounding
|
|
8
|
+
walk, fusion, and the mechanisms' own gates. None re-spells a condition the law
|
|
9
|
+
owns.
|
|
9
10
|
|
|
10
11
|
## The unit — `src/mind/derivation.ts`
|
|
11
12
|
|
|
@@ -43,7 +44,35 @@ not carry.
|
|
|
43
44
|
|
|
44
45
|
The walk ends when the layer stops offering or the law refuses; owning no
|
|
45
46
|
search, the law cannot prevent a cycle — that is the layer's, over the structure
|
|
46
|
-
it holds.
|
|
47
|
+
it holds. A transition is only ever `advance`: a fusion the law refuses is not
|
|
48
|
+
taken (there is no hand-built fallback).
|
|
49
|
+
|
|
50
|
+
## The engine — `closeOver`
|
|
51
|
+
|
|
52
|
+
`closeOver(state, query, W, layers, enter?)` closes a derivation under the law,
|
|
53
|
+
layer by layer. A layer (`ClosureLayer`) is a named `Offer` with its own
|
|
54
|
+
instrumentation hooks; each is walked by `closure` to its own end, and the state
|
|
55
|
+
it reaches is the state the next layer is offered against. Layers are PHASES, in
|
|
56
|
+
order, never revisited.
|
|
57
|
+
|
|
58
|
+
- **¬FIXED is the engine's.** A fixed state admits no transition, so no layer is
|
|
59
|
+
entered for it — the walk-skip and fusion-skip a caller used to spell by hand
|
|
60
|
+
are the law's first clause, taken before any layer pays.
|
|
61
|
+
- **Engagement is the layer's.** A layer that has nothing to offer a state
|
|
62
|
+
(`engages`) is not entered: no offer, no meter phase.
|
|
63
|
+
- **The pipeline's post-grounding stage IS the engine**, with two layers: the
|
|
64
|
+
walk (`walkLayer` — forward absorb or pivot) and the fusion (`fusionLayer` —
|
|
65
|
+
one composed transition, engaged only while the derivation is open). There is
|
|
66
|
+
no hand-built state anywhere outside `advance` (`test/137.7`).
|
|
67
|
+
|
|
68
|
+
## The four quantities (trap 13), and where each stands
|
|
69
|
+
|
|
70
|
+
| Quantity | Status |
|
|
71
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| offer of a hop | Consequence. Chart: the corpus's continuations under the READ cap, each a MOVE priced STEP, the search decides (`test/112`). Walk: absorb/pivot, the law admits. |
|
|
73
|
+
| depth of a join | Consequence. The join consumes the shortest tail prefix naming a learnt key that leads — CONTAINS (resolves) ∧ MOVES (leads) — and chains until the tail is consumed; no count (`test/108`). It is priced for the facts a lightest derivation STANDS ON, round by round (`solve`'s join license), never for every fact the exploration reaches — the `deepen` cure for the `recompleteNode` trap. |
|
|
74
|
+
| scope of a gap | Consequence for alignment (`alignAround` walks outward, no cap). NOT for recognition's interior pass: its reach `W⁴ + 2r` bounds an exhaustive embedded-form probe; exact unbounded reach needs a whole-stream index the store does not have. |
|
|
75
|
+
| scope of a substitution | Consequence: the gap between two aligned anchors (`unexplainedSpans`), gated as scaffolding by the global commonality reading. The filler-unanimity window (`chainReach`) is a derived decision scale, not an explosion cap. |
|
|
47
76
|
|
|
48
77
|
## One cost home
|
|
49
78
|
|
|
@@ -51,7 +80,7 @@ it holds.
|
|
|
51
80
|
`weigh`). A mechanism reports `moves` and `accounted` — what it did — and never
|
|
52
81
|
a price; `cover` reports its chart derivation's work.
|
|
53
82
|
|
|
54
|
-
##
|
|
83
|
+
## Three limits, proved and left out
|
|
55
84
|
|
|
56
85
|
1. **The chart cannot evaluate accounting** — its interface has no parameter for
|
|
57
86
|
it, and carrying it per item was measured and rejected. The chart reads the
|
|
@@ -62,6 +91,9 @@ a price; `cover` reports its chart derivation's work.
|
|
|
62
91
|
`reason`'s echo guards stop with the remainder non-empty, and recall's
|
|
63
92
|
reverse tiers close with an empty accounting. That is a fact about the
|
|
64
93
|
asker's material in the store, available only to the layer holding it.
|
|
94
|
+
3. **The extension is not priced into the market** (`test/136.2`): the engine
|
|
95
|
+
closes the WINNER only; pricing each candidate's closure would run the walk
|
|
96
|
+
for every candidate, and that comparison has not been measured.
|
|
65
97
|
|
|
66
98
|
## Layering
|
|
67
99
|
|
|
@@ -78,3 +110,11 @@ vocabulary, not the tracer's.
|
|
|
78
110
|
- `test/142`, `test/143` — the offer's contract, and the cycles.
|
|
79
111
|
- `test/144`, `test/146`, `test/147` — identity is content, the `contains` gate,
|
|
80
112
|
the witness ladder.
|
|
113
|
+
- `test/137.7` — no transition is built by hand outside the law.
|
|
114
|
+
- `test/149` — the engine: ¬FIXED first, layers as phases, engagement, a refusal
|
|
115
|
+
ends one layer, and the pipeline sequences no layer by hand.
|
|
116
|
+
- `test/150` — the join prices the facts a derivation stands on, not every fact
|
|
117
|
+
the exploration reaches (a hub's degree changes nothing).
|
|
118
|
+
- `test/151` — the cover's other async premises (connectors, concept hops) are
|
|
119
|
+
resolved where the search reaches them; a span's cheapest completion
|
|
120
|
+
dominates; a grounding on the whole query pays no fusion climb.
|
|
@@ -56,6 +56,21 @@ minimum per-byte cost in the ladder (every real per-byte cost is `>= MICRO`,
|
|
|
56
56
|
including `PASS`), and only the suffix past `right` is counted, so `h` never
|
|
57
57
|
exceeds the true remaining cost.
|
|
58
58
|
|
|
59
|
+
## Dominance — why `PASS ≫ STEP` does not flood the chart
|
|
60
|
+
|
|
61
|
+
The heuristic charges `MICRO` for a byte the goal will pay `PASS` for, so a
|
|
62
|
+
cover that leaves bytes unexplained lets the search spend up to `PASS / STEP`
|
|
63
|
+
hops looking for one more explained byte. Coverage itself cannot use them: every
|
|
64
|
+
recognised completion of `[i, j)` advances the cover from `i` to `j` at the same
|
|
65
|
+
`MICRO`. So a form or completion of `[i, j)` whose cost has reached that of a
|
|
66
|
+
completion of `[i, j)` already yielded is DOMINATED and fires no rule
|
|
67
|
+
(`buildSearch`, metered as `searchDominated`): every completion it could lead to
|
|
68
|
+
costs at least as much, and a tie goes to the one yielded first. What a
|
|
69
|
+
completion's BYTES could still do — fuse, splice, join — fires from the
|
|
70
|
+
completion the search would stand on for that span, the same cure the join
|
|
71
|
+
license and `deepen` apply. The first hop's stop-here (`STEP + CONCEPT`) is then
|
|
72
|
+
a real horizon: no chain deeper than it is expanded.
|
|
73
|
+
|
|
59
74
|
## Policy is not cost
|
|
60
75
|
|
|
61
76
|
"Computation always wins" is **not** priced into the ladder (a computed result
|
|
@@ -69,3 +84,4 @@ the sole completion there. Keep policy in callers; keep the engine neutral.
|
|
|
69
84
|
- `test/53` — cross-region probe instrumentation
|
|
70
85
|
- `test/54` — evidence `k` instrumentation
|
|
71
86
|
- `test/55` — cost meter (`Meter`, `CostReport`, `searchPops`/`searchPushes`)
|
|
87
|
+
- `test/151` — dominance: a hub's degree generates no chart work
|