@hviana/sema 0.7.3 → 0.7.6
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 +95 -843
- package/README.md +11 -11
- package/dist/src/mind/graph-search.d.ts +32 -22
- package/dist/src/mind/graph-search.js +97 -54
- package/dist/src/mind/mind.js +14 -0
- package/dist/src/store-sqlite.js +17 -0
- package/dist/src/store.d.ts +18 -0
- package/dist/src/store.js +10 -0
- package/docs/INDEX.md +71 -0
- package/docs/INVARIANTS.md +19 -0
- package/docs/architecture/bounded-reads.md +85 -0
- package/docs/architecture/caches.md +89 -0
- package/docs/architecture/commonality.md +45 -0
- package/docs/architecture/cost-model.md +71 -0
- package/docs/architecture/determinism.md +73 -0
- package/docs/architecture/exact-vs-approximate.md +47 -0
- package/docs/architecture/factored-machinery.md +28 -0
- package/docs/architecture/fold-contract.md +87 -0
- package/docs/architecture/halo-sketch.md +99 -0
- package/docs/architecture/match-project.md +62 -0
- package/docs/architecture/mechanism-market.md +95 -0
- package/docs/architecture/memoization.md +96 -0
- package/docs/architecture/meter.md +55 -0
- package/docs/architecture/saturation.md +92 -0
- package/docs/architecture/store.md +79 -0
- package/docs/architecture/thresholds.md +79 -0
- package/docs/failures/tempting-but-wrong.md +144 -0
- package/docs/harness/gates.md +56 -0
- package/docs/mechanisms/alu.md +75 -0
- package/docs/mechanisms/cast.md +75 -0
- package/docs/mechanisms/confluence.md +36 -0
- package/docs/mechanisms/cover.md +54 -0
- package/docs/mechanisms/extraction.md +53 -0
- package/docs/mechanisms/prefix-completion.md +54 -0
- package/docs/mechanisms/recall.md +69 -0
- package/docs/mechanisms/reference.md +58 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/mind/graph-search.ts +101 -55
- package/src/mind/mind.ts +14 -0
- package/src/store-sqlite.ts +19 -0
- package/src/store.ts +22 -0
- package/test/89-completion-recursion.test.mjs +30 -10
- package/test/97-store-seed.test.mjs +105 -0
- package/test/98-completion-chaining.test.mjs +140 -0
- package/HOW_IT_WORKS.md +0 -5836
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ No weights. No gradients. No training loop. No neural network. No GPU.
|
|
|
41
41
|
> Vector Symbolic Architecture (Plate 1995; Kanerva 2009) over a
|
|
42
42
|
> content-addressable memory, with inference by weighted automated deduction
|
|
43
43
|
> (Knuth 1977; Felzenszwalb & McAllester 2007). Each term is grounded in
|
|
44
|
-
> [
|
|
44
|
+
> [docs/INDEX.md](docs/INDEX.md).
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
@@ -314,16 +314,16 @@ start talking — no install, no runtime, no API key.
|
|
|
314
314
|
|
|
315
315
|
## ✦ Learn more
|
|
316
316
|
|
|
317
|
-
| Document | What's inside
|
|
318
|
-
| :----------------------------------------------------------------------------------- |
|
|
319
|
-
| 📘 **[
|
|
320
|
-
| 🛠️ **[AGENTS.md](AGENTS.md)** | The development manual: repo layout, build/test, internals, invariants, and recipes for extending the system.
|
|
321
|
-
| 🎓 **[CITATION.cff](CITATION.cff)** | How to cite Sema in academic work.
|
|
322
|
-
| ⚖️ **[LICENSE.md](LICENSE.md)** | PolyForm Noncommercial License 1.0.0.
|
|
323
|
-
| 📚 **[DATASETS.md](DATASETS.md)** | Training corpora: provenance, per-corpus attribution, and how a trained memory file is licensed.
|
|
324
|
-
| 💼 **[COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md)** | Commercial licensing terms and contact.
|
|
325
|
-
| 🤗 **[Trained examples](https://huggingface.co/buckets/hviana/sema-trained-v1)** | Pre-trained memory files you can download and use directly.
|
|
326
|
-
| 💿 **[Binary examples](https://huggingface.co/buckets/hviana/sema-binary-examples)** | Ready-to-run web chat apps for Windows, Mac, and Linux — one file, no install.
|
|
317
|
+
| Document | What's inside |
|
|
318
|
+
| :----------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
319
|
+
| 📘 **[docs/INDEX.md](docs/INDEX.md)** | Architecture docs: single-system laws (docs/architecture/), mechanisms, invariants, and harness gates — the entry point for theory and implementation. |
|
|
320
|
+
| 🛠️ **[AGENTS.md](AGENTS.md)** | The development manual: repo layout, build/test, internals, invariants, and recipes for extending the system. |
|
|
321
|
+
| 🎓 **[CITATION.cff](CITATION.cff)** | How to cite Sema in academic work. |
|
|
322
|
+
| ⚖️ **[LICENSE.md](LICENSE.md)** | PolyForm Noncommercial License 1.0.0. |
|
|
323
|
+
| 📚 **[DATASETS.md](DATASETS.md)** | Training corpora: provenance, per-corpus attribution, and how a trained memory file is licensed. |
|
|
324
|
+
| 💼 **[COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md)** | Commercial licensing terms and contact. |
|
|
325
|
+
| 🤗 **[Trained examples](https://huggingface.co/buckets/hviana/sema-trained-v1)** | Pre-trained memory files you can download and use directly. |
|
|
326
|
+
| 💿 **[Binary examples](https://huggingface.co/buckets/hviana/sema-binary-examples)** | Ready-to-run web chat apps for Windows, Mac, and Linux — one file, no install. |
|
|
327
327
|
|
|
328
328
|
---
|
|
329
329
|
|
|
@@ -270,37 +270,47 @@ export declare class GraphSearch {
|
|
|
270
270
|
* and recompose into a deeper learnt form (→ FINAL). This is why a single
|
|
271
271
|
* edge-target needs no bespoke logic — it routes back through {@link solve}.
|
|
272
272
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
273
|
+
* The produced bytes are decomposed by the machinery that owns their shape:
|
|
274
|
+
* the span's LEAVES and SPLITS drive the split rule, which resolves each half
|
|
275
|
+
* through findLeaf — so a part that straddles a content-defined cut is still
|
|
276
|
+
* recovered even though the node's tree children need not align with the
|
|
277
|
+
* learnt parts (the fold cuts "p1 p2" as "p1 p"|"2"). Recognised SITES are
|
|
278
|
+
* filtered to the node's own kids: a produced form is completed out of what
|
|
279
|
+
* it was built from, never by re-recognising arbitrary forms inside it. That
|
|
280
|
+
* filter is load-bearing — a 37-byte dialogue sentence carries seven hub
|
|
281
|
+
* openers, and re-covering them chained through the corpus's whole
|
|
282
|
+
* continuation population: 2.5 GB and OOM for `respond("hi.")`.
|
|
276
283
|
*
|
|
277
284
|
* The recovered answer is accepted only when it MOVED and names a LEARNT node
|
|
278
285
|
* ({@link resolve}) — the graph itself gates against re-expanding a contained
|
|
279
|
-
* form ("ice is cold" ⊅→ "ice is cold is cold").
|
|
286
|
+
* form ("ice is cold" ⊅→ "ice is cold is cold"). An ACCEPTED completion is
|
|
287
|
+
* then re-covered in turn, so a chain runs as deep as the graph licenses; a
|
|
288
|
+
* rejected one ends its branch, so no work is spent past it.
|
|
280
289
|
*
|
|
281
|
-
* Termination
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* and each finished completion is memoised". That is a bound of N — the one
|
|
288
|
-
* AGENTS §2.8 forbids — and it was load-bearing, not pedantic: nested, the
|
|
289
|
-
* recursion reached depth 331 and 9.1 GB on an 18.9M-node store for a 2-byte
|
|
290
|
-
* query and did not terminate, which is what killed a 5 h training run at its
|
|
291
|
-
* checkpoint recall. Guard: test/89-completion-recursion.test.mjs. */
|
|
290
|
+
* Termination and cost are STRUCTURAL: {@link recompleteOpen} is the chain's
|
|
291
|
+
* stack (membership is the cycle guard), {@link recompleteMemo} re-covers each
|
|
292
|
+
* node at most once, only accepted completions recurse, and every level
|
|
293
|
+
* deepens only the top derivation — so a cover pays for the chain it finds,
|
|
294
|
+
* not for how densely the corpus interconnects the forms it passes through.
|
|
295
|
+
* Guard: test/89-completion-recursion.test.mjs. */
|
|
292
296
|
private recompleteNode;
|
|
293
297
|
/** Per-cover memo of each produced node's completion (so the many terminal
|
|
294
298
|
* outs of a long query re-cover each distinct node at most once); reset at the
|
|
295
299
|
* top of {@link cover}. */
|
|
296
300
|
private recompleteMemo;
|
|
297
|
-
/** The
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* is
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
*
|
|
301
|
+
/** The derivation sink of the TOP cover, threaded into every nested
|
|
302
|
+
* completion so a produced form's own recompositions are reported in the
|
|
303
|
+
* same trace instead of vanishing after the first layer. Undefined when
|
|
304
|
+
* nothing is inspecting, so an uninspected response pays nothing. */
|
|
305
|
+
private derivationSink?;
|
|
306
|
+
/** The chain of nodes currently being re-completed — the recursion STACK.
|
|
307
|
+
* MEMBERSHIP is the cycle guard ({@link recompleteNode} refuses a node
|
|
308
|
+
* already open on this chain), which is what lets a completion recurse as
|
|
309
|
+
* deep as the graph licenses while the work stays the answer's: the
|
|
310
|
+
* recursion only advances through an ACCEPTED completion, every produced
|
|
311
|
+
* form is decomposed into its own kids, and the memo re-covers each node at
|
|
312
|
+
* most once per cover. A Set, not a flag, because it states WHICH node is
|
|
313
|
+
* open — the invariant a reader needs to check the guard. */
|
|
304
314
|
private recompleteOpen;
|
|
305
315
|
/** out(i,j,bytes,…): index it for the binary rules, then offer splicing a
|
|
306
316
|
* learnt connector (the in-search bridge), splitting (at a sub-leaf form
|
|
@@ -230,12 +230,26 @@ export class GraphSearch {
|
|
|
230
230
|
// through (completion is cover, recursively — see {@link recompleteNode}).
|
|
231
231
|
this.recompleteOpen.clear();
|
|
232
232
|
this.recompleteMemo = new Map();
|
|
233
|
-
|
|
233
|
+
// The top cover's derivation sink is threaded into every nested completion
|
|
234
|
+
// so the recompositions a produced form needs are reported in the same
|
|
235
|
+
// trace instead of vanishing after the first layer.
|
|
236
|
+
this.derivationSink = onDerivation;
|
|
237
|
+
const solved = this.solve(queryLen, {
|
|
234
238
|
sites,
|
|
235
239
|
leaves,
|
|
236
240
|
splits,
|
|
237
241
|
starts,
|
|
238
242
|
}, conceptTarget, substitutions, connectors, computedResults, onDerivation);
|
|
243
|
+
// Deepening runs HERE, once, on the derivation the top cover CHOSE — never
|
|
244
|
+
// inside the nested solve a completion runs. Nesting the deepening is what
|
|
245
|
+
// made per-query cost track how densely the corpus interconnects the forms
|
|
246
|
+
// passed through: every re-cover fanned out into the whole corpus's
|
|
247
|
+
// continuations instead of following the chain the answer itself licensed.
|
|
248
|
+
// With deepening only at the top, `recompleteNode` walks the accepted chain
|
|
249
|
+
// one link at a time (its own memo and stack), so the work is the answer's.
|
|
250
|
+
return solved === null
|
|
251
|
+
? null
|
|
252
|
+
: { segs: this.deepen(solved.segs), cost: solved.cost };
|
|
239
253
|
}
|
|
240
254
|
/** Build the deduction system for one span and return its lightest cover's
|
|
241
255
|
* chosen spans — the SINGLE routine the query and every produced composite
|
|
@@ -270,7 +284,7 @@ export class GraphSearch {
|
|
|
270
284
|
onDerivation(readDerivation(derivation, substitutions !== undefined));
|
|
271
285
|
}
|
|
272
286
|
return derivation
|
|
273
|
-
? { segs:
|
|
287
|
+
? { segs: readCover(derivation), cost: derivation.cost }
|
|
274
288
|
: null;
|
|
275
289
|
}
|
|
276
290
|
/** Re-cover the CHOSEN fixpoint spans, in place.
|
|
@@ -722,55 +736,51 @@ export class GraphSearch {
|
|
|
722
736
|
* and recompose into a deeper learnt form (→ FINAL). This is why a single
|
|
723
737
|
* edge-target needs no bespoke logic — it routes back through {@link solve}.
|
|
724
738
|
*
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
739
|
+
* The produced bytes are decomposed by the machinery that owns their shape:
|
|
740
|
+
* the span's LEAVES and SPLITS drive the split rule, which resolves each half
|
|
741
|
+
* through findLeaf — so a part that straddles a content-defined cut is still
|
|
742
|
+
* recovered even though the node's tree children need not align with the
|
|
743
|
+
* learnt parts (the fold cuts "p1 p2" as "p1 p"|"2"). Recognised SITES are
|
|
744
|
+
* filtered to the node's own kids: a produced form is completed out of what
|
|
745
|
+
* it was built from, never by re-recognising arbitrary forms inside it. That
|
|
746
|
+
* filter is load-bearing — a 37-byte dialogue sentence carries seven hub
|
|
747
|
+
* openers, and re-covering them chained through the corpus's whole
|
|
748
|
+
* continuation population: 2.5 GB and OOM for `respond("hi.")`.
|
|
728
749
|
*
|
|
729
750
|
* The recovered answer is accepted only when it MOVED and names a LEARNT node
|
|
730
751
|
* ({@link resolve}) — the graph itself gates against re-expanding a contained
|
|
731
|
-
* form ("ice is cold" ⊅→ "ice is cold is cold").
|
|
752
|
+
* form ("ice is cold" ⊅→ "ice is cold is cold"). An ACCEPTED completion is
|
|
753
|
+
* then re-covered in turn, so a chain runs as deep as the graph licenses; a
|
|
754
|
+
* rejected one ends its branch, so no work is spent past it.
|
|
732
755
|
*
|
|
733
|
-
* Termination
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
* and each finished completion is memoised". That is a bound of N — the one
|
|
740
|
-
* AGENTS §2.8 forbids — and it was load-bearing, not pedantic: nested, the
|
|
741
|
-
* recursion reached depth 331 and 9.1 GB on an 18.9M-node store for a 2-byte
|
|
742
|
-
* query and did not terminate, which is what killed a 5 h training run at its
|
|
743
|
-
* checkpoint recall. Guard: test/89-completion-recursion.test.mjs. */
|
|
756
|
+
* Termination and cost are STRUCTURAL: {@link recompleteOpen} is the chain's
|
|
757
|
+
* stack (membership is the cycle guard), {@link recompleteMemo} re-covers each
|
|
758
|
+
* node at most once, only accepted completions recurse, and every level
|
|
759
|
+
* deepens only the top derivation — so a cover pays for the chain it finds,
|
|
760
|
+
* not for how densely the corpus interconnects the forms it passes through.
|
|
761
|
+
* Guard: test/89-completion-recursion.test.mjs. */
|
|
744
762
|
recompleteNode(node) {
|
|
745
763
|
if (!this.host.recogniseSpan)
|
|
746
764
|
return null;
|
|
747
765
|
const memo = this.recompleteMemo;
|
|
748
766
|
if (memo.has(node))
|
|
749
767
|
return memo.get(node) ?? null;
|
|
750
|
-
// ONE re-cover per produced node — never a re-cover inside a re-cover.
|
|
751
|
-
//
|
|
752
768
|
// Re-covering is how a PRODUCED node's bytes enter the search at all: the
|
|
753
|
-
// cover machinery otherwise only ever sees the QUERY's spans.
|
|
754
|
-
//
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
// bytes nothing else brings in) needs it.
|
|
769
|
+
// cover machinery otherwise only ever sees the QUERY's spans. The recursion
|
|
770
|
+
// is allowed to nest — a chain IS nested completions — but it is bounded so
|
|
771
|
+
// the work stays the ANSWER's (AGENTS §2.8): the stack below is the cycle
|
|
772
|
+
// guard, only ACCEPTED completions recurse, and the nested solve decomposes
|
|
773
|
+
// the form by its own shape instead of re-recognising the corpus's hub forms
|
|
774
|
+
// inside it.
|
|
760
775
|
//
|
|
761
|
-
//
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
|
|
769
|
-
//
|
|
770
|
-
// `recompleteOpen` is that stack, so a non-empty stack means we are already
|
|
771
|
-
// inside one. This subsumes the old cycle guard: a node cannot recurse
|
|
772
|
-
// back into itself when nothing recurses at all.
|
|
773
|
-
if (this.recompleteOpen.size > 0)
|
|
776
|
+
// `recompleteOpen` IS the stack of the chain being built, so MEMBERSHIP is
|
|
777
|
+
// the cycle guard: a node already open on this chain cannot re-enter it.
|
|
778
|
+
// Testing the NODE — not the stack's size — is what lets a completion
|
|
779
|
+
// recurse as deep as the graph licenses, exactly the intrinsic convergence
|
|
780
|
+
// {@link solve}'s contract states. Work stays the answer's because the
|
|
781
|
+
// recursion only ever advances through an ACCEPTED completion (below) and
|
|
782
|
+
// {@link cover} deepens only the top derivation.
|
|
783
|
+
if (this.recompleteOpen.has(node))
|
|
774
784
|
return null;
|
|
775
785
|
// A leaf or single-child node has no parts to recompose; skip before the
|
|
776
786
|
// costly recognition so a plain terminal answer pays nothing.
|
|
@@ -782,16 +792,43 @@ export class GraphSearch {
|
|
|
782
792
|
const bytes = this.store.bytesPrefix(node, ALL);
|
|
783
793
|
this.recompleteOpen.add(node);
|
|
784
794
|
try {
|
|
785
|
-
// Completion is cover
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
|
|
795
|
+
// Completion is cover, but the produced form is decomposed by its own
|
|
796
|
+
// shape. The LEAVES and SPLITS are kept whole, so the split rule still
|
|
797
|
+
// recovers a part that straddles a content-defined cut (the fold cuts
|
|
798
|
+
// "p1 p2" as "p1 p"|"2", and findLeaf still resolves p1 and p2). The
|
|
799
|
+
// recognised SITES are filtered to the node's own kids, because
|
|
800
|
+
// re-recognising arbitrary forms inside the bytes is what let a hub-heavy
|
|
801
|
+
// utterance explode: a 37-byte dialogue sentence carries seven hub
|
|
802
|
+
// openers, and re-covering them chained through the corpus's whole
|
|
803
|
+
// continuation population (2.5 GB and OOM for `"hi."`). No
|
|
804
|
+
// concepts/connectors either (those need the caller's async
|
|
805
|
+
// pre-resolution) — the recursion follows edges and fusion, which is what
|
|
806
|
+
// a deeper rewrite chain is made of.
|
|
807
|
+
const rec = this.host.recogniseSpan(bytes);
|
|
808
|
+
const kids = new Set(nrec.kids);
|
|
809
|
+
const solved = this.solve(bytes.length, {
|
|
810
|
+
sites: rec.sites.filter((s) => kids.has(s.payload)),
|
|
811
|
+
leaves: rec.leaves,
|
|
812
|
+
splits: rec.splits,
|
|
813
|
+
starts: rec.starts,
|
|
814
|
+
}, new Map(), undefined, undefined, undefined, this.derivationSink);
|
|
790
815
|
const answer = solved && concatBytes(solved.segs.map((s) => s.bytes));
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
816
|
+
// ACCEPT, then CONTINUE THE CHAIN — but only along an accepted
|
|
817
|
+
// completion. A re-cover whose result is not itself a learnt node (the
|
|
818
|
+
// 70→374-byte concatenations that name nothing) ends its branch here
|
|
819
|
+
// instead of recursing into work the answer never asked for, and an
|
|
820
|
+
// accepted one names a node that is re-covered in turn. That is what
|
|
821
|
+
// keeps a deep chain possible while the work stays proportional to the
|
|
822
|
+
// chain rather than to the corpus's interconnections.
|
|
823
|
+
const composed = answer !== null && !bytesEqual(answer, bytes)
|
|
824
|
+
? this.host.resolve(answer)
|
|
794
825
|
: null;
|
|
826
|
+
if (composed === null) {
|
|
827
|
+
memo.set(node, null);
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
const deeper = this.recompleteNode(composed);
|
|
831
|
+
const out = deeper ?? answer;
|
|
795
832
|
memo.set(node, out);
|
|
796
833
|
return out;
|
|
797
834
|
}
|
|
@@ -803,13 +840,19 @@ export class GraphSearch {
|
|
|
803
840
|
* outs of a long query re-cover each distinct node at most once); reset at the
|
|
804
841
|
* top of {@link cover}. */
|
|
805
842
|
recompleteMemo = new Map();
|
|
806
|
-
/** The
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* is
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
*
|
|
843
|
+
/** The derivation sink of the TOP cover, threaded into every nested
|
|
844
|
+
* completion so a produced form's own recompositions are reported in the
|
|
845
|
+
* same trace instead of vanishing after the first layer. Undefined when
|
|
846
|
+
* nothing is inspecting, so an uninspected response pays nothing. */
|
|
847
|
+
derivationSink;
|
|
848
|
+
/** The chain of nodes currently being re-completed — the recursion STACK.
|
|
849
|
+
* MEMBERSHIP is the cycle guard ({@link recompleteNode} refuses a node
|
|
850
|
+
* already open on this chain), which is what lets a completion recurse as
|
|
851
|
+
* deep as the graph licenses while the work stays the answer's: the
|
|
852
|
+
* recursion only advances through an ACCEPTED completion, every produced
|
|
853
|
+
* form is decomposed into its own kids, and the memo re-covers each node at
|
|
854
|
+
* most once per cover. A Set, not a flag, because it states WHICH node is
|
|
855
|
+
* open — the invariant a reader needs to check the guard. */
|
|
813
856
|
recompleteOpen = new Set();
|
|
814
857
|
/** out(i,j,bytes,…): index it for the binary rules, then offer splicing a
|
|
815
858
|
* learnt connector (the in-search bridge), splitting (at a sub-leaf form
|
package/dist/src/mind/mind.js
CHANGED
|
@@ -143,10 +143,24 @@ export class Mind {
|
|
|
143
143
|
const { store: optsStore, mechanisms: userMechs, mechanismFactories: userFacts, canon: optsCanon, profile: optsProfile, ...rest } = (optsOrCfg ?? {});
|
|
144
144
|
this._canonOpt = optsCanon ?? null;
|
|
145
145
|
this._profile = optsProfile === true;
|
|
146
|
+
// `explicitSeed` is read BEFORE resolveConfig folds the default in, so
|
|
147
|
+
// the store can be consulted only when the caller did not choose.
|
|
148
|
+
const explicitSeed = rest.seed;
|
|
146
149
|
this.cfg = resolveConfig(rest);
|
|
147
150
|
this.store = optsStore ?? new SQliteStore({
|
|
148
151
|
maxGroup: this.cfg.geometry.maxGroup,
|
|
149
152
|
});
|
|
153
|
+
// THE ARTIFACT'S SEED GOVERNS. `train.seed` is recovered by the store at
|
|
154
|
+
// open, exactly like `train.D` and `geometry.maxGroup`. The seed feeds
|
|
155
|
+
// `makeKeyring`, `Space.rand` and the `Alphabet` below, so folding a
|
|
156
|
+
// query under config.ts's default (42) against a store trained with
|
|
157
|
+
// another seed (e.g. 7) lands in a DIFFERENT vector space than the one
|
|
158
|
+
// the artifact's nodes were folded into: recognition and resonance then
|
|
159
|
+
// read the wrong space and every answer degrades silently. An explicit
|
|
160
|
+
// caller seed still wins — this only replaces the unconfigured default.
|
|
161
|
+
if (explicitSeed === undefined && this.store.trainSeed !== null) {
|
|
162
|
+
this.cfg.seed = this.store.trainSeed;
|
|
163
|
+
}
|
|
150
164
|
userMechanisms = userMechs ?? [];
|
|
151
165
|
userFactories = userFacts ?? [];
|
|
152
166
|
}
|
package/dist/src/store-sqlite.js
CHANGED
|
@@ -360,6 +360,23 @@ export class SQliteStore extends AbstractStore {
|
|
|
360
360
|
this._maxGroup = g;
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
|
+
// Recover the TRAINING seed exactly as D and maxGroup are recovered. The
|
|
364
|
+
// seed seeds the alphabet and the seat keyring (mind.ts), so a Mind that
|
|
365
|
+
// folds a query under any other seed lands in a different vector space
|
|
366
|
+
// than the one this artifact's nodes were folded into — recognition,
|
|
367
|
+
// resonance and every mechanism downstream then read the wrong space. The
|
|
368
|
+
// trainer persists `train.seed` and refuses to resume against a store
|
|
369
|
+
// trained with a different one (example/train_base/main.ts), so the value
|
|
370
|
+
// is authoritative for this artifact. Absent on a store that was never
|
|
371
|
+
// trained, where the caller's configured seed stands.
|
|
372
|
+
{
|
|
373
|
+
const row = this.sqlite.prepare("SELECT val FROM meta WHERE key = 'train.seed'").get();
|
|
374
|
+
if (row) {
|
|
375
|
+
const s = Number(row.val);
|
|
376
|
+
if (Number.isInteger(s) && s >= 0)
|
|
377
|
+
this._trainSeed = s;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
363
380
|
// Persist maxGroup to meta when opening a FRESH store (no rows yet) so
|
|
364
381
|
// indexSubtree always sees the training-time value even when the store is
|
|
365
382
|
// accessed without a Mind / full snapshot.
|
package/dist/src/store.d.ts
CHANGED
|
@@ -114,6 +114,16 @@ export declare class BoundedMap<K, V> {
|
|
|
114
114
|
}
|
|
115
115
|
export interface Store {
|
|
116
116
|
readonly D: number;
|
|
117
|
+
/** The seed the artifact was TRAINED with, recovered from the store's own
|
|
118
|
+
* `train.seed` metadata, or null for a store that was never trained.
|
|
119
|
+
*
|
|
120
|
+
* This is not decoration: the seed feeds the alphabet and the seat keyring
|
|
121
|
+
* (see the Mind constructor), so folding a query under any other seed lands
|
|
122
|
+
* in a different vector space than the one the artifact's nodes were folded
|
|
123
|
+
* into. A Mind opening a trained store MUST adopt this seed unless the
|
|
124
|
+
* caller explicitly overrides it — the same discipline that recovers
|
|
125
|
+
* `train.D` and `geometry.maxGroup` from the metadata. */
|
|
126
|
+
readonly trainSeed: number | null;
|
|
117
127
|
/** The work accumulator for the inference call in flight, or null. The
|
|
118
128
|
* Mind attaches one per profiled response and detaches it after (see
|
|
119
129
|
* src/meter.ts). A store MUST only ever write to it — no read may reach
|
|
@@ -475,6 +485,10 @@ export declare abstract class AbstractStore implements Store {
|
|
|
475
485
|
protected efFor(clusterCount: number): number;
|
|
476
486
|
protected _D: number;
|
|
477
487
|
protected _maxGroup: number;
|
|
488
|
+
/** `train.seed` recovered by the backend at open, or null when the store was
|
|
489
|
+
* never trained. A backend that omits it simply reports null, which leaves
|
|
490
|
+
* the caller's configured seed in force. */
|
|
491
|
+
protected _trainSeed: number | null;
|
|
478
492
|
protected readonly minHaloMass: number;
|
|
479
493
|
protected readonly efSearch: number;
|
|
480
494
|
protected readonly overfetch: number;
|
|
@@ -563,6 +577,10 @@ export declare abstract class AbstractStore implements Store {
|
|
|
563
577
|
protected _edgeSrcCount: number;
|
|
564
578
|
constructor(config: StoreConfig, D: number, maxGroup: number);
|
|
565
579
|
get D(): number;
|
|
580
|
+
/** The seed the artifact was trained with, recovered from `train.seed` at
|
|
581
|
+
* open. Null for a store that was never trained. See
|
|
582
|
+
* {@link Store.trainSeed} for why this must govern inference. */
|
|
583
|
+
get trainSeed(): number | null;
|
|
566
584
|
/** Await the async initialisation performed by the concrete constructor. */
|
|
567
585
|
protected _ensureReady(): Promise<void>;
|
|
568
586
|
has(id: NodeId): boolean;
|
package/dist/src/store.js
CHANGED
|
@@ -434,6 +434,10 @@ export class AbstractStore {
|
|
|
434
434
|
// ── Config ─────────────────────────────────────────────────────────────
|
|
435
435
|
_D;
|
|
436
436
|
_maxGroup;
|
|
437
|
+
/** `train.seed` recovered by the backend at open, or null when the store was
|
|
438
|
+
* never trained. A backend that omits it simply reports null, which leaves
|
|
439
|
+
* the caller's configured seed in force. */
|
|
440
|
+
_trainSeed = null;
|
|
437
441
|
minHaloMass;
|
|
438
442
|
efSearch;
|
|
439
443
|
overfetch;
|
|
@@ -552,6 +556,12 @@ export class AbstractStore {
|
|
|
552
556
|
get D() {
|
|
553
557
|
return this._D;
|
|
554
558
|
}
|
|
559
|
+
/** The seed the artifact was trained with, recovered from `train.seed` at
|
|
560
|
+
* open. Null for a store that was never trained. See
|
|
561
|
+
* {@link Store.trainSeed} for why this must govern inference. */
|
|
562
|
+
get trainSeed() {
|
|
563
|
+
return this._trainSeed;
|
|
564
|
+
}
|
|
555
565
|
/** Await the async initialisation performed by the concrete constructor. */
|
|
556
566
|
async _ensureReady() {
|
|
557
567
|
if (!this._ready)
|
package/docs/INDEX.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Sema Documentation Index
|
|
2
|
+
|
|
3
|
+
Sema is a single system stated three ways: the law lives in `docs/architecture/`
|
|
4
|
+
(what holds), the prescription in `AGENTS.md` bootloader (what to do and where),
|
|
5
|
+
and the proof in `test/` (pins that fail when the law is broken).
|
|
6
|
+
|
|
7
|
+
## Routing — what to read for each task
|
|
8
|
+
|
|
9
|
+
| Task | Read | Why |
|
|
10
|
+
| --------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
11
|
+
| Add a mechanism | `docs/architecture/mechanism-market.md` + `docs/mechanisms/*.md` | Market contract: decoupled, declared competence, visible budget, evidence travels |
|
|
12
|
+
| Add a threshold | `docs/architecture/thresholds.md` | All cutoffs are formulas over D/W/N in `geometry.ts`; `config.ts` holds only budgets |
|
|
13
|
+
| Debug an answer | `docs/architecture/cost-model.md` + `src/meter.ts` | One cost ladder (`MICRO`/`STEP`/`CONCEPT`/`PASS`) decides every grounding choice |
|
|
14
|
+
| Understand the fold | `docs/architecture/fold-contract.md` | Deposit and inference must compute the same tree; boundaries are not turn metadata |
|
|
15
|
+
| Add a store backend | `docs/architecture/store.md` + `docs/architecture/bounded-reads.md` | `AbstractStore` owns domain logic; backends are thin wrappers with capped reads |
|
|
16
|
+
| Add an ALU operation | `src/alu/README.md` | One `registry.derive` per op composing existing ops; no new `derive` needed |
|
|
17
|
+
| Add a matcher or projection | `docs/architecture/match-project.md` | Mechanisms are `(matcher, direction, gate)` configs over the shared `match.ts` family |
|
|
18
|
+
| Add a deduction rule | `docs/architecture/cost-model.md` + `docs/architecture/determinism.md` | Place cost on the ladder, keep heuristic admissible, extend `classifyMove` |
|
|
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
|
+
| 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
|
+
|
|
22
|
+
## Architecture laws (13)
|
|
23
|
+
|
|
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; five 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 and investment discipline | `test/01`, `test/04` |
|
|
32
|
+
| 7 | `docs/architecture/commonality.md` | Two populations: corpus-global (`reachOf`+`dominates`) vs weave-local (`depth[]`) | `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; `contentLevels` is single boundary rule; no W/offset dependence | `test/59`, `test/63` |
|
|
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 deterministic, phases nest | `test/55` |
|
|
39
|
+
|
|
40
|
+
## Mechanisms (8)
|
|
41
|
+
|
|
42
|
+
| Mechanism | File | Role |
|
|
43
|
+
| ----------------- | -------------------------------------- | --------------------------------------------------------- |
|
|
44
|
+
| cover | `docs/mechanisms/cover.md` | Exact/computed-span covering via `GraphSearch` |
|
|
45
|
+
| cast | `docs/mechanisms/cast.md` | Weave-local analogy via `depth[]` frame gate |
|
|
46
|
+
| confluence | `docs/mechanisms/confluence.md` | Corpus-global filler/scaffolding gate over climb |
|
|
47
|
+
| extraction | `docs/mechanisms/extraction.md` | Located-frame read-out with anchored span accounting |
|
|
48
|
+
| reference | `docs/mechanisms/reference.md` | Slot-bound voicing of asker-supplied referents |
|
|
49
|
+
| recall | `docs/mechanisms/recall.md` | Nearest stored form; echo tier via substitution bridge |
|
|
50
|
+
| prefix-completion | `docs/mechanisms/prefix-completion.md` | Literal prefix of exactly one trained form |
|
|
51
|
+
| alu | `docs/mechanisms/alu.md` | Authoritative computed spans (`parse` → `aluToMechanism`) |
|
|
52
|
+
|
|
53
|
+
## Supporting docs
|
|
54
|
+
|
|
55
|
+
| Doc | Role |
|
|
56
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------- |
|
|
57
|
+
| `docs/architecture/caches.md` | Every acceleration is a `BoundedMap`; miss re-derives; budgets in `StoreConfig` |
|
|
58
|
+
| `docs/architecture/halo-sketch.md` | Halo & sketch — distributional memory, quantization, bottom-k profiles |
|
|
59
|
+
| `docs/architecture/factored-machinery.md` | Single-definition contracts table — one owner per shared symbol |
|
|
60
|
+
|
|
61
|
+
## Cross-cutting
|
|
62
|
+
|
|
63
|
+
- `docs/INVARIANTS.md` — the five invariants (determinism, derived thresholds,
|
|
64
|
+
exact-decides, one cost currency, bounded reads) with file-level routing.
|
|
65
|
+
- `docs/failures/tempting-but-wrong.md` — refuted simplifications that passed
|
|
66
|
+
review but failed pins (e.g. reordering ladders, flattening attention
|
|
67
|
+
asymmetries, one-cone-exhausted stop).
|
|
68
|
+
- `docs/harness/gates.md` — how `AGENTS.md` recipes,
|
|
69
|
+
`bench/profile-inference.mjs`, and `test/*.test.mjs` enforce the laws.
|
|
70
|
+
- `docs/architecture/` — full per-law derivation (each file states why the law
|
|
71
|
+
is this way; no separate HOW).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# INVARIANTS — Laws, Proofs, Derivations
|
|
2
|
+
|
|
3
|
+
> Law in `docs/architecture/*.md`, proof in `test/*.test.mjs`.
|
|
4
|
+
|
|
5
|
+
| # | Law | Where defined (src symbol) | Pins (test/N) | Doc (docs/architecture/*.md) |
|
|
6
|
+
| -- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------------- |
|
|
7
|
+
| 1 | Determinism | `src/config.ts:seed` `src/alphabet.ts:Alphabet` `src/mind/traverse.ts:guidedFirst` | `test/20` `test/42` | `determinism.md` |
|
|
8
|
+
| 2 | Derived thresholds | `src/geometry.ts:mergeThreshold,identityBar,reachThreshold,significanceBar,consensusFloor,dominates` | `test/64` `test/40` | `thresholds.md` |
|
|
9
|
+
| 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` |
|
|
10
|
+
| 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` |
|
|
11
|
+
| 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` |
|
|
12
|
+
| 6 | Fold contract | `src/geometry.ts:contentLevels` `src/mind/canonical.ts:canonicalWindows,chainReach` `src/canon.ts:canonicalizer` | `test/59` `test/63` | `fold-contract.md` |
|
|
13
|
+
| 7 | Mechanism market | `src/mind/pipeline-mechanism.ts:PipelineMechanism,Precomputed` `src/mind/pipeline.ts:think,worthRunning` | `test/01` `test/04` | `mechanism-market.md` |
|
|
14
|
+
| 8 | Two commonality measures | `src/mind/traverse.ts:reachOf,dominates,corpusN` (global) `src/mind/match.ts:depth[],MIN_WEAVE` (weave-local) | `test/17` `test/34` | `commonality.md` |
|
|
15
|
+
| 9 | Memoization idempotence | `src/mind/pipeline-mechanism.ts:Precomputed` `src/mind/mind.ts:beginResponse,endResponse,_resolvedSubtrees` | `test/42` | `memoization.md` |
|
|
16
|
+
| 10 | Caches as budgets | `src/store.ts:BoundedMap` `src/config.ts:StoreConfig:bytesCacheMax,recCacheBytes,haloCacheBytes` | `test/96` `test/91` | `caches.md` |
|
|
17
|
+
| 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` |
|
|
18
|
+
| 12 | Meter contracts | `src/meter.ts:Meter,PhaseCost,time` `src/mind/pipeline-mechanism.ts:Precomputed.shared` | `test/55` | `meter.md` |
|
|
19
|
+
| 13 | Saturation | `src/mind/traverse.ts:edgeAncestors:SaturationReason` `src/mind/junction.ts:junctionContainersFrom` `src/mind/resonance.ts:pivotInto` | `test/27` `test/16` | `saturation.md` |
|