@hviana/sema 0.3.0 → 0.4.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.
Files changed (79) hide show
  1. package/AGENTS.md +71 -5
  2. package/CONTRIBUTING.md +92 -10
  3. package/LICENSE.md +2 -2
  4. package/dist/src/derive/src/deduction.d.ts +12 -1
  5. package/dist/src/derive/src/deduction.js +5 -1
  6. package/dist/src/derive/src/index.d.ts +1 -0
  7. package/dist/src/geometry.d.ts +3 -30
  8. package/dist/src/geometry.js +330 -82
  9. package/dist/src/index.d.ts +1 -0
  10. package/dist/src/index.js +1 -0
  11. package/dist/src/meter.d.ts +171 -0
  12. package/dist/src/meter.js +269 -0
  13. package/dist/src/mind/attention.d.ts +0 -4
  14. package/dist/src/mind/attention.js +251 -23
  15. package/dist/src/mind/bridge.d.ts +10 -1
  16. package/dist/src/mind/bridge.js +179 -10
  17. package/dist/src/mind/canonical.d.ts +6 -1
  18. package/dist/src/mind/canonical.js +6 -1
  19. package/dist/src/mind/graph-search.d.ts +9 -0
  20. package/dist/src/mind/graph-search.js +59 -19
  21. package/dist/src/mind/junction.d.ts +10 -0
  22. package/dist/src/mind/junction.js +14 -0
  23. package/dist/src/mind/match.d.ts +40 -0
  24. package/dist/src/mind/match.js +125 -1
  25. package/dist/src/mind/mechanisms/cast.js +63 -6
  26. package/dist/src/mind/mechanisms/extraction.d.ts +0 -34
  27. package/dist/src/mind/mechanisms/extraction.js +1 -88
  28. package/dist/src/mind/mechanisms/recall.d.ts +3 -0
  29. package/dist/src/mind/mechanisms/recall.js +77 -14
  30. package/dist/src/mind/mind.d.ts +55 -4
  31. package/dist/src/mind/mind.js +110 -91
  32. package/dist/src/mind/pipeline-mechanism.d.ts +33 -3
  33. package/dist/src/mind/pipeline-mechanism.js +179 -10
  34. package/dist/src/mind/pipeline.js +52 -10
  35. package/dist/src/mind/primitives.d.ts +11 -15
  36. package/dist/src/mind/primitives.js +47 -28
  37. package/dist/src/mind/reasoning.d.ts +7 -1
  38. package/dist/src/mind/reasoning.js +40 -8
  39. package/dist/src/mind/recognition.js +93 -20
  40. package/dist/src/mind/traverse.d.ts +11 -0
  41. package/dist/src/mind/traverse.js +58 -5
  42. package/dist/src/mind/types.d.ts +28 -5
  43. package/dist/src/store.d.ts +15 -0
  44. package/dist/src/store.js +91 -6
  45. package/package.json +1 -1
  46. package/src/derive/src/deduction.ts +15 -0
  47. package/src/derive/src/index.ts +1 -0
  48. package/src/geometry.ts +350 -122
  49. package/src/index.ts +1 -0
  50. package/src/meter.ts +333 -0
  51. package/src/mind/attention.ts +268 -31
  52. package/src/mind/bridge.ts +187 -10
  53. package/src/mind/canonical.ts +6 -1
  54. package/src/mind/graph-search.ts +60 -21
  55. package/src/mind/junction.ts +12 -0
  56. package/src/mind/match.ts +146 -1
  57. package/src/mind/mechanisms/cast.ts +62 -6
  58. package/src/mind/mechanisms/extraction.ts +2 -103
  59. package/src/mind/mechanisms/recall.ts +84 -17
  60. package/src/mind/mind.ts +139 -98
  61. package/src/mind/pipeline-mechanism.ts +203 -13
  62. package/src/mind/pipeline.ts +65 -8
  63. package/src/mind/primitives.ts +49 -33
  64. package/src/mind/reasoning.ts +39 -7
  65. package/src/mind/recognition.ts +89 -19
  66. package/src/mind/traverse.ts +59 -5
  67. package/src/mind/types.ts +28 -5
  68. package/src/store.ts +75 -6
  69. package/test/14-scaling.test.mjs +17 -7
  70. package/test/31-audit.test.mjs +4 -1
  71. package/test/33-multi-candidate.test.mjs +13 -1
  72. package/test/36-already-answered-fusion.test.mjs +10 -3
  73. package/test/46-recognise-multibyte-edge.test.mjs +3 -3
  74. package/test/53-cross-region-probe-instrumentation.test.mjs +36 -6
  75. package/test/55-cost-meter.test.mjs +284 -0
  76. package/test/56-bridge-identity-admission.test.mjs +209 -0
  77. package/test/57-fusion-order.test.mjs +104 -0
  78. package/test/58-subquantum-sites.test.mjs +112 -0
  79. package/test/59-fold-invariance.test.mjs +226 -0
package/AGENTS.md CHANGED
@@ -145,6 +145,14 @@ matcher or projection, add it **to the shared family** with a derived gate
145
145
  aligning, edge-following to a fixpoint, predecessor-picking, or fan-out capping
146
146
  is reintroducing duplication that was deliberately removed.
147
147
 
148
+ A shared analysis must not live inside a mechanism. If `pipeline-mechanism.ts`
149
+ (the shared contract and `Precomputed`) or a post-grounding stage has to import
150
+ _out of_ `mechanisms/`, the dependency is inverted and the market's decoupling
151
+ (2.6) is broken — deleting that mechanism would break the shared container. The
152
+ span-shape family (`isSpanShaped` / `containsSpan` / `skillExemplar`) was
153
+ exactly this and now lives in `match.ts`, where its two consumers can reach it
154
+ without knowing extraction exists.
155
+
148
156
  Related single-definition contracts (define once, import everywhere):
149
157
 
150
158
  - `canonical.ts` — the write/read contract for canonical segmentation
@@ -327,7 +335,49 @@ _Follow it:_ when your code can degrade, emit a counter or trace step. And mind
327
335
  the classic trap: **empty bytes are truthy** — `Uint8Array(0)` passes
328
336
  `if (answer)`; always test `.length`.
329
337
 
330
- ### 2.14 Comment style
338
+ ### 2.14 Measured, not guessed — the work meter
339
+
340
+ `src/meter.ts` is the one computational-usage accounting surface: a `Meter`
341
+ counts the WORK one inference call performs at every layer (store reads by kind
342
+ and by VOLUME, ANN queries and vectors scanned, perceptions, recognitions,
343
+ climbs and ancestor visits, alignment cells, chart pops, mechanism floors/runs)
344
+ and times named PHASES. Off by default and free when off;
345
+ `new Mind({ profile:
346
+ true })` attaches one per response and leaves a
347
+ `CostReport` on `mind.lastCost`. `bench/profile-inference.mjs` is the reference
348
+ harness.
349
+
350
+ It is the profiling counterpart of the rationale — the rationale says why an
351
+ answer was chosen, the meter says what it cost. Four contracts:
352
+
353
+ 1. **Never read by inference.** A counter that reached a decision would end
354
+ determinism (2.1). Write-only from the engine's side.
355
+ 2. **Counts are the product; times are the hint.** Counters are deterministic,
356
+ so two runs are diffable and a work regression is visible without a
357
+ stopwatch. Only `elapsedMs` and the phase millisecond totals are not.
358
+ 3. **Phases nest, and carry their own counter deltas.** `think` ⊃ `<mech>.run` ⊃
359
+ `substitutionBridge` ⊃ `recall.exhaustiveResonate`. Inclusive, never summed —
360
+ but each phase reports the work done inside it (`PhaseCost.counters`), which
361
+ is what makes "which phase did those byte reads?" answerable at all.
362
+ 4. **Count a logical operation once.** A recursive read (`bytesPrefix`
363
+ descending a branch) is charged at the public entry point only — the private
364
+ `_prefix` body is uncharged. Counting the recursion made one read of an
365
+ N-byte branch report as N reads, so the counter measured tree size instead of
366
+ read requests.
367
+ 5. **Shared analyses are charged to themselves.** `attention`, `weave`,
368
+ `spanShaped` and `substitutionBridge` bill their own phase, not the mechanism
369
+ that happened to first-touch them — otherwise the profile blames whoever paid
370
+ on everyone's behalf (it once read "cast.floor costs 2.9 s" when 2.7 s of
371
+ that was the consensus climb).
372
+
373
+ _Follow it:_ a new layer that wants to be visible bumps a field in `meter.ts` —
374
+ never a private counter. (`danglingReads`/`compactFailures` in `store.ts` stay:
375
+ those are session-lifetime HEALTH counters, not per-response work.) Add the
376
+ counter to the report the same way, and remember the classic trap: **profile
377
+ without a trace attached** — a trace bypasses the memos (2.11) and measures a
378
+ different machine.
379
+
380
+ ### 2.15 Comment style
331
381
 
332
382
  Comments state _constraints and failure modes_ — "this guard exists because X
333
383
  breaks without it", often naming the test that pins the behaviour — never
@@ -360,6 +410,7 @@ story of the fix.
360
410
  | Learning / ingestion / training cache | `src/mind/learning.ts`, `src/ingest-cache.ts` |
361
411
  | Post-grounding (reason, fuse, articulate) | `src/mind/reasoning.ts`, `src/mind/articulation.ts` |
362
412
  | Rationale / trace | `src/mind/rationale.ts`, `src/mind/trace.ts` |
413
+ | Computational-usage meter | `src/meter.ts` (harness: `bench/profile-inference.mjs`) |
363
414
  | Extension host types | `src/extension.ts` |
364
415
  | Sublibraries (own READMEs, own tests) | `src/derive/`, `src/alu/`, `src/rabitq-ivf/` |
365
416
 
@@ -427,6 +478,19 @@ recognition found, how the climb voted, which edges were followed
427
478
  (`disambiguate` steps carry the evidence). `recall-echo` means "nearest stored
428
479
  form, not a derived fact". Remember traced responses bypass memos (2.11).
429
480
 
481
+ ### Profile an answer
482
+
483
+ ```ts
484
+ const mind = new Mind({ store, profile: true }); // no trace — see 2.14
485
+ await mind.respondText(query);
486
+ console.log(formatReport(mind.lastCost!)); // counters by layer + nested phases
487
+ ```
488
+
489
+ `bench/profile-inference.mjs` runs a battery this way and prints per-query and
490
+ aggregate reports; `sumReports` aggregates a run. Diff the COUNTERS between two
491
+ runs to catch a work regression — they are deterministic; read the PHASES to see
492
+ where the wall clock went.
493
+
430
494
  ### Train at scale
431
495
 
432
496
  Use `CachedIngest` (`ingest-cache.ts`) as a drop-in for `mind.ingest` — it
@@ -450,10 +514,12 @@ against the built `dist/` (`npm test`; one suite:
450
514
  - New behaviour ⇒ a test in the matching numbered suite, or a new numbered
451
515
  suite.
452
516
  - Many tests pin **contracts that look like implementation details** (the bridge
453
- tier order, extraction's two span-shape readings, determinism, honest
454
- silence). A "simplification" that fails an existing test is wrong until you
455
- can argue the _test_ is wrong — several guards exist precisely because a
456
- plausible simplification once failed a dozen suites.
517
+ tier order, the bridge's identity admission vs. its prefix trap and the
518
+ scaffolding reading behind it, the two span-shape readings (`match.ts`),
519
+ `MechanismResult.complete`, determinism, honest silence). A "simplification"
520
+ that fails an existing test is wrong until you can argue the _test_ is wrong —
521
+ several guards exist precisely because a plausible simplification once failed
522
+ a dozen suites.
457
523
  - Sublibraries test themselves (`src/{alu,derive,rabitq-ivf}/test/`) with zero
458
524
  Sema dependency. Keep it so.
459
525
  - Performance claims are tested (the rabitq-ivf benchmark asserts sub-linear
package/CONTRIBUTING.md CHANGED
@@ -1,15 +1,97 @@
1
- # Contributing
1
+ # Contributing to Sema
2
2
 
3
- By contributing to Sema, you agree that:
3
+ Thank you for your interest in contributing to Sema.
4
4
 
5
- - you have the legal right to submit your contribution;
6
- - your contribution does not violate third-party rights;
7
- - your contribution will be licensed under the PolyForm Noncommercial License
8
- 1.0.0;
9
- - this project is source-available, not formally open source;
10
- - commercial use requires a separate paid license;
11
- - you will not submit secrets, credentials, private keys, or confidential data.
5
+ Sema is source-available software distributed publicly under the PolyForm
6
+ Noncommercial License 1.0.0. Commercial use requires a separate commercial
7
+ license from the Sema licensor.
12
8
 
13
- All commits should be signed off:
9
+ ## Contributor representations
14
10
 
11
+ By submitting a contribution to Sema, you represent that:
12
+
13
+ - you created the contribution or otherwise have the legal right to submit it
14
+ under these terms;
15
+ - the contribution does not knowingly violate any copyright, patent, trade
16
+ secret, contractual obligation, or other third-party right;
17
+ - you have obtained any authorization required from your employer or another
18
+ rights holder;
19
+ - you have clearly identified any third-party material included in the
20
+ contribution and provided its applicable license and copyright notices;
21
+ - the contribution does not contain secrets, credentials, private keys,
22
+ confidential information, or unlawfully obtained material;
23
+ - if the contribution was created with the assistance of an artificial
24
+ intelligence system, you have reviewed it and remain responsible for its
25
+ origin, legality, correctness, and compliance with these terms.
26
+
27
+ ## Contribution license grant
28
+
29
+ You retain ownership of the copyright in your contribution.
30
+
31
+ By intentionally submitting a contribution for inclusion in Sema, you grant
32
+ Henrique Viana, as the author, copyright holder, and licensor of Sema, and his
33
+ successors and assigns, a worldwide, perpetual, irrevocable, non-exclusive,
34
+ royalty-free, transferable, and sublicensable license to:
35
+
36
+ - use, reproduce, modify, adapt, and prepare derivative works from the
37
+ contribution;
38
+ - combine the contribution with Sema or other materials;
39
+ - publicly display, publicly perform, distribute, make available, host, and
40
+ otherwise exploit the contribution;
41
+ - offer, license, and sublicense the contribution as part of Sema under the
42
+ PolyForm Noncommercial License 1.0.0;
43
+ - offer, license, and sublicense the contribution as part of commercial,
44
+ proprietary, source-available, or other versions of Sema;
45
+ - relicense the contribution as part of Sema under current or future licensing
46
+ terms.
47
+
48
+ This license grant does not transfer ownership of your contribution to Henrique
49
+ Viana. You remain free to use and license your original contribution
50
+ independently, provided that doing so does not violate rights belonging to Sema
51
+ or to third parties.
52
+
53
+ ## Patent license
54
+
55
+ To the extent that you own or control patent claims necessarily infringed by
56
+ your contribution, you grant Henrique Viana, his successors and assigns, and
57
+ recipients of Sema a worldwide, perpetual, irrevocable, non-exclusive,
58
+ royalty-free, transferable, and sublicensable patent license to make, use, sell,
59
+ offer for sale, import, and otherwise exploit your contribution as part of Sema.
60
+
61
+ This patent license terminates for any party that initiates patent litigation
62
+ alleging that Sema or a contribution incorporated into Sema infringes a patent.
63
+
64
+ ## No obligation to accept contributions
65
+
66
+ Submitting a contribution does not guarantee that it will be accepted, merged,
67
+ published, maintained, or included in any public or commercial version of Sema.
68
+
69
+ The project maintainer may modify, reject, remove, or replace an accepted
70
+ contribution.
71
+
72
+ ## Developer Certificate of Origin
73
+
74
+ All commits must include a Developer Certificate of Origin sign-off:
75
+
76
+ ```bash
15
77
  git commit -s
78
+ ```
79
+
80
+ The sign-off must use your real name and an email address you are authorized to
81
+ use:
82
+
83
+ ```text
84
+ Signed-off-by: Your Name <your.email@example.com>
85
+ ```
86
+
87
+ The sign-off certifies the origin and authorization of each commit. It does not
88
+ replace the contribution license grant described in this document.
89
+
90
+ ## Acceptance
91
+
92
+ By opening or submitting a pull request, patch, or other contribution intended
93
+ for inclusion in Sema, you confirm that you have read and agree to these
94
+ contribution terms.
95
+
96
+ Contributions submitted on behalf of a company or another organization must be
97
+ authorized by the applicable rights holder.
package/LICENSE.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  <https://polyformproject.org/licenses/noncommercial/1.0.0>
4
4
 
5
- > Required Notice: Copyright Sema Authors — Henrique Viana, Marcelo Oliveira dos
6
- > Reis, Rogerio Nascimento (hv5088@gmail.com)
5
+ > Required Notice: Copyright © Henrique Viana (author). Marcelo Oliveira dos
6
+ > Reis and Rogerio Nascimento (supporters).
7
7
 
8
8
  ## Acceptance
9
9
 
@@ -118,8 +118,19 @@ export interface Derivation<I> {
118
118
  /** Derivations of the rule's premises (empty for an axiom). */
119
119
  premises: Array<Derivation<I>>;
120
120
  }
121
+ /** Optional search-effort read-out, filled in place while the search runs.
122
+ * A plain structural record so the sublibrary stays firewalled (it imports
123
+ * nothing from Sema, and Sema copies these into its own accounting). Purely
124
+ * observational: the search never reads it back. */
125
+ export interface SearchStats {
126
+ /** Agenda entries popped, stale lazy-deletion entries included — the chart
127
+ * work actually done. */
128
+ pops: number;
129
+ /** Agenda entries pushed by a successful relaxation. */
130
+ pushes: number;
131
+ }
121
132
  /**
122
133
  * Find a lightest derivation of a goal item, or `null` if none exists.
123
134
  * `cost` on the returned root is the total derivation cost.
124
135
  */
125
- export declare function lightestDerivation<I>(system: DeductionSystem<I>): Derivation<I> | null;
136
+ export declare function lightestDerivation<I>(system: DeductionSystem<I>, stats?: SearchStats): Derivation<I> | null;
@@ -43,7 +43,7 @@ import { MinHeap } from "./priority-queue.js";
43
43
  * Find a lightest derivation of a goal item, or `null` if none exists.
44
44
  * `cost` on the returned root is the total derivation cost.
45
45
  */
46
- export function lightestDerivation(system) {
46
+ export function lightestDerivation(system, stats) {
47
47
  const g = new Map(); // best known cost per item
48
48
  const proof = new Map(); // producing rule per item
49
49
  const items = new Map(); // key → the item it stands for
@@ -82,6 +82,8 @@ export function lightestDerivation(system) {
82
82
  g.set(key, cost);
83
83
  proof.set(key, rule);
84
84
  items.set(key, item);
85
+ if (stats)
86
+ stats.pushes++;
85
87
  agenda.push(cost + h(item, key), { key, g: cost });
86
88
  }
87
89
  };
@@ -89,6 +91,8 @@ export function lightestDerivation(system) {
89
91
  relax(item, cost, null);
90
92
  while (agenda.size > 0) {
91
93
  const { value } = agenda.pop();
94
+ if (stats)
95
+ stats.pops++;
92
96
  const key = value.key;
93
97
  // Lazy deletion: an entry is stale if a cheaper derivation has since been
94
98
  // recorded for the same item.
@@ -1,4 +1,5 @@
1
1
  export { lightestDerivation } from "./deduction.js";
2
+ export type { SearchStats } from "./deduction.js";
2
3
  export type { DeductionSystem, Derivation, PooledConclusion, PooledContribution, Rule, } from "./deduction.js";
3
4
  export { coverSequence } from "./rewrite.js";
4
5
  export type { CandidateSpan, Cover } from "./rewrite.js";
@@ -105,6 +105,7 @@ export interface Grid {
105
105
  data: Uint8Array;
106
106
  dims?: number[];
107
107
  }
108
+ export declare function contentBoundaries(space: Space, bytes: Uint8Array): number[];
108
109
  /** Find the longest prefix of `bytes` whose leaf-id signature matches a
109
110
  * known branch via `lookup`. Returns the byte-length of that prefix, or 0. */
110
111
  export declare function knownPrefixLength(bytes: Uint8Array, leafAt: (i: number) => number | null, lookup: (leafIds: number[]) => number | null): number;
@@ -129,9 +130,8 @@ export declare function bytesToTree(space: Space, alphabet: Alphabet, bytes: Uin
129
130
  * grown stream whose boundary set EXTENDS a previous fold's reuses every
130
131
  * matching segment's Folded unchanged (segments fold independently by
131
132
  * construction, so reuse is bit-identical to refolding) and folds only the
132
- * new right-edge segment — O(turn) per extension, the stable-prefix
133
- * counterpart of {@link FoldPyramid}. Purely a cache: the produced tree
134
- * never depends on cache state. */
133
+ * new right-edge segment — O(turn) per extension. Purely a cache: the
134
+ * produced tree never depends on cache state. */
135
135
  export interface StableFold {
136
136
  edges: number[];
137
137
  segs: Folded[];
@@ -170,33 +170,6 @@ export interface StructuralPart {
170
170
  * concatenated byte string, and never interns or stores a new node: the
171
171
  * result is an opaque, ungrounded Vec for an ANN probe only. */
172
172
  export declare function composeStructuralGist(space: Space, parts: readonly StructuralPart[]): Vec;
173
- /** The PLAIN fold's full level pyramid — every level's item list, bottom
174
- * (leaves) to top (root). Left-grouped folding is RADIX-ALIGNED: the item
175
- * at level L, index i, covers exactly bytes [i·mg^L, (i+1)·mg^L) whenever
176
- * it is a FULL block, and a full block folds bit-identically in ANY byte
177
- * string that contains it at that offset. So a string extended by a
178
- * suffix (a conversation's accumulated context) reuses every full block of
179
- * its prefix's pyramid and refolds only the right edge of each level —
180
- * O(suffix + depth·mg) per extension instead of O(whole), with the
181
- * produced tree BIT-IDENTICAL to a from-scratch plain fold (same nodes,
182
- * same FP ops; reused subtrees are shared objects, and Sema nodes are
183
- * never mutated). Purely an implementation cache: structure and numerics
184
- * never depend on whether a pyramid was available. */
185
- export interface FoldPyramid {
186
- levels: Array<Array<{
187
- tree: Sema;
188
- len: number;
189
- }>>;
190
- bytes: number;
191
- }
192
- /** Plain bytes→tree (identical to capability-less {@link bytesToTree}) that
193
- * also RETURNS its pyramid, reusing `prev` — the pyramid of a PROPER
194
- * prefix of `bytes` (caller guarantees content match and
195
- * prev.bytes < bytes.length). */
196
- export declare function bytesToTreePyramid(space: Space, alphabet: Alphabet, bytes: Uint8Array, prev?: FoldPyramid): {
197
- tree: Sema;
198
- pyramid: FoldPyramid;
199
- };
200
173
  export declare function hilbertBytes(grid: Grid): Uint8Array;
201
174
  export declare function gridToTree(space: Space, alphabet: Alphabet, grid: Grid): Sema;
202
175
  export declare function stackGrids(frames: Grid[]): Grid;