skynet-graph 1.2.0

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 (213) hide show
  1. package/CONTRIBUTING.md +22 -0
  2. package/LICENSE +661 -0
  3. package/README.md +245 -0
  4. package/bin/sg +20 -0
  5. package/concepts/README.md +23 -0
  6. package/concepts/_substrate/Claim/Confidence/Trusted.json +6 -0
  7. package/concepts/_substrate/Claim/Confidence.json +10 -0
  8. package/concepts/_substrate/Claim/Freshness/Stale.json +7 -0
  9. package/concepts/_substrate/Claim/Freshness.json +5 -0
  10. package/concepts/_substrate/Claim/Verification/Refuted.json +6 -0
  11. package/concepts/_substrate/Claim/Verification.json +6 -0
  12. package/concepts/_substrate/Claim.json +4 -0
  13. package/concepts/_substrate/Frontier/InBeam.json +6 -0
  14. package/concepts/_substrate/Frontier/Stuck.json +6 -0
  15. package/concepts/_substrate/Frontier.json +5 -0
  16. package/concepts/_substrate/Intake/ToTask.json +22 -0
  17. package/concepts/_substrate/Intake/Typed.json +10 -0
  18. package/concepts/_substrate/Intake.json +27 -0
  19. package/concepts/_substrate/Task/Answer.json +12 -0
  20. package/concepts/_substrate/Task/Complexity/Atomic.json +5 -0
  21. package/concepts/_substrate/Task/Complexity/Compound/Expansion.json +7 -0
  22. package/concepts/_substrate/Task/Complexity/Compound.json +6 -0
  23. package/concepts/_substrate/Task/Complexity.json +6 -0
  24. package/concepts/_substrate/Task/EvalComplexity.json +13 -0
  25. package/concepts/_substrate/Task/ReportUp.json +6 -0
  26. package/concepts/_substrate/Task/Rollup.json +10 -0
  27. package/concepts/_substrate/Task.json +5 -0
  28. package/concepts/clinical/Diagnosis/Medication.json +6 -0
  29. package/concepts/clinical/Diagnosis.json +15 -0
  30. package/concepts/clinical/Observation/LabValue/OutOfRange.json +6 -0
  31. package/concepts/clinical/Observation/LabValue.json +5 -0
  32. package/concepts/clinical/Observation.json +6 -0
  33. package/concepts/common/Document/pathBasket.json +6 -0
  34. package/concepts/common/Document.json +3 -0
  35. package/concepts/common/Edge/Distance.json +13 -0
  36. package/concepts/common/Edge/Stay/LongStay.json +8 -0
  37. package/concepts/common/Edge/Stay.json +8 -0
  38. package/concepts/common/Edge/Travel/LongTravel.json +16 -0
  39. package/concepts/common/Edge/Travel/ShortTravel.json +11 -0
  40. package/concepts/common/Edge/Travel/targetNode.json +7 -0
  41. package/concepts/common/Edge/Travel.json +7 -0
  42. package/concepts/common/Edge.json +3 -0
  43. package/concepts/common/Vertice.json +3 -0
  44. package/concepts/supply/Fulfillment.json +10 -0
  45. package/concepts/supply/Inventory/Reorder.json +7 -0
  46. package/concepts/supply/Inventory.json +5 -0
  47. package/concepts/supply/Procurement/SupplierConfirm.json +6 -0
  48. package/concepts/supply/Procurement.json +8 -0
  49. package/concepts/supply/Transport.json +5 -0
  50. package/doc/API.md +556 -0
  51. package/doc/CAPABILITIES.md +373 -0
  52. package/doc/MODELISATION.md +511 -0
  53. package/doc/architecture.md +267 -0
  54. package/doc/concept-as-graph.md +379 -0
  55. package/doc/concept-learning.md +168 -0
  56. package/doc/creative-loop-map.md +202 -0
  57. package/doc/original-2016-doc.md +148 -0
  58. package/doc/usage.md +501 -0
  59. package/lib/authoring/abstract.js +401 -0
  60. package/lib/authoring/abstraction.js +190 -0
  61. package/lib/authoring/adapt.js +393 -0
  62. package/lib/authoring/ancestry.js +216 -0
  63. package/lib/authoring/author.js +147 -0
  64. package/lib/authoring/bounded-merge.js +57 -0
  65. package/lib/authoring/canon.js +158 -0
  66. package/lib/authoring/clock.js +123 -0
  67. package/lib/authoring/combinator.js +92 -0
  68. package/lib/authoring/compete.js +76 -0
  69. package/lib/authoring/compose-hotspot.js +314 -0
  70. package/lib/authoring/compress.js +279 -0
  71. package/lib/authoring/concept-net.js +254 -0
  72. package/lib/authoring/concepts.js +66 -0
  73. package/lib/authoring/context-project.js +277 -0
  74. package/lib/authoring/contract.js +476 -0
  75. package/lib/authoring/corpus-pack.js +125 -0
  76. package/lib/authoring/cost-probe.js +86 -0
  77. package/lib/authoring/crystallize.js +555 -0
  78. package/lib/authoring/dag-decompose.js +256 -0
  79. package/lib/authoring/dataset-adapter.js +253 -0
  80. package/lib/authoring/debug-provider.js +92 -0
  81. package/lib/authoring/decompose.js +343 -0
  82. package/lib/authoring/emittability.js +236 -0
  83. package/lib/authoring/equilibrium.js +160 -0
  84. package/lib/authoring/extract.js +153 -0
  85. package/lib/authoring/forest.js +55 -0
  86. package/lib/authoring/forge-fallback.js +116 -0
  87. package/lib/authoring/givens.js +121 -0
  88. package/lib/authoring/glossary.js +177 -0
  89. package/lib/authoring/grammar-graph.js +191 -0
  90. package/lib/authoring/granularity.js +80 -0
  91. package/lib/authoring/graph-net.js +108 -0
  92. package/lib/authoring/ground.js +148 -0
  93. package/lib/authoring/higher-order.js +85 -0
  94. package/lib/authoring/hotspot.js +132 -0
  95. package/lib/authoring/hysteresis.js +85 -0
  96. package/lib/authoring/index.js +106 -0
  97. package/lib/authoring/lattice-morphism.js +61 -0
  98. package/lib/authoring/lattice-pack.js +148 -0
  99. package/lib/authoring/leaf-io.js +77 -0
  100. package/lib/authoring/library.js +156 -0
  101. package/lib/authoring/lifecycle.js +92 -0
  102. package/lib/authoring/loop.js +193 -0
  103. package/lib/authoring/master-loop.js +132 -0
  104. package/lib/authoring/mdl.js +209 -0
  105. package/lib/authoring/memo-stability.js +122 -0
  106. package/lib/authoring/method-explorer.js +138 -0
  107. package/lib/authoring/method-pack.js +181 -0
  108. package/lib/authoring/method.js +202 -0
  109. package/lib/authoring/mine.js +510 -0
  110. package/lib/authoring/mount.js +119 -0
  111. package/lib/authoring/negotiate.js +100 -0
  112. package/lib/authoring/parametric.js +139 -0
  113. package/lib/authoring/reaggregate.js +75 -0
  114. package/lib/authoring/rebalance.js +157 -0
  115. package/lib/authoring/recall.js +135 -0
  116. package/lib/authoring/registry.js +350 -0
  117. package/lib/authoring/relearn.js +147 -0
  118. package/lib/authoring/retention.js +89 -0
  119. package/lib/authoring/segment-proxy.js +114 -0
  120. package/lib/authoring/serve-leaf.js +64 -0
  121. package/lib/authoring/slot-aware-serve.js +77 -0
  122. package/lib/authoring/sound-invoke.js +59 -0
  123. package/lib/authoring/split-serve.js +74 -0
  124. package/lib/authoring/ste.js +239 -0
  125. package/lib/authoring/stock.js +101 -0
  126. package/lib/authoring/store.js +94 -0
  127. package/lib/authoring/supervise.js +138 -0
  128. package/lib/authoring/support.js +131 -0
  129. package/lib/authoring/task-mirror.js +51 -0
  130. package/lib/authoring/typed-loop.js +160 -0
  131. package/lib/authoring/validate.js +569 -0
  132. package/lib/authoring/widen.js +88 -0
  133. package/lib/combos/appliance.js +189 -0
  134. package/lib/combos/critique.js +377 -0
  135. package/lib/combos/defaults.js +86 -0
  136. package/lib/combos/durable-runner.js +115 -0
  137. package/lib/combos/forge.js +209 -0
  138. package/lib/combos/index.js +86 -0
  139. package/lib/combos/learning-library.js +253 -0
  140. package/lib/combos/mixture-serve.js +193 -0
  141. package/lib/combos/plan-loop.js +192 -0
  142. package/lib/combos/proxy-cache.js +272 -0
  143. package/lib/combos/self-mod.js +94 -0
  144. package/lib/durable/audit.js +102 -0
  145. package/lib/durable/checkpoint-store.js +550 -0
  146. package/lib/durable/fold.js +62 -0
  147. package/lib/durable/interpreter.js +242 -0
  148. package/lib/durable/xlate.js +185 -0
  149. package/lib/graph/Graph.js +2486 -0
  150. package/lib/graph/expr.js +307 -0
  151. package/lib/graph/index.js +26 -0
  152. package/lib/graph/log.js +134 -0
  153. package/lib/graph/objects/Concept.js +382 -0
  154. package/lib/graph/objects/Entity.js +503 -0
  155. package/lib/graph/objects/Node.js +57 -0
  156. package/lib/graph/objects/PathMap.js +557 -0
  157. package/lib/graph/objects/Segment.js +100 -0
  158. package/lib/graph/tasks/stabilize.js +59 -0
  159. package/lib/graph/tasks/taskflow.js +247 -0
  160. package/lib/index.js +122 -0
  161. package/lib/load.js +132 -0
  162. package/lib/providers/backends.js +117 -0
  163. package/lib/providers/borderline.js +129 -0
  164. package/lib/providers/cache.js +226 -0
  165. package/lib/providers/canonicalize.js +198 -0
  166. package/lib/providers/constat.js +107 -0
  167. package/lib/providers/geo.js +53 -0
  168. package/lib/providers/index.js +116 -0
  169. package/lib/providers/intake.js +263 -0
  170. package/lib/providers/llm-local.js +88 -0
  171. package/lib/providers/llm.js +236 -0
  172. package/lib/providers/local-host.js +197 -0
  173. package/lib/providers/merge-consistency.js +162 -0
  174. package/lib/providers/nogood.js +149 -0
  175. package/lib/providers/reason-loop.js +223 -0
  176. package/lib/providers/semiring.js +285 -0
  177. package/lib/providers/solver-fork.js +163 -0
  178. package/lib/providers/stats.js +228 -0
  179. package/lib/providers/verify.js +120 -0
  180. package/lib/runtime/index.js +135 -0
  181. package/lib/runtime/invoke-pool.js +92 -0
  182. package/lib/runtime/protocol.js +210 -0
  183. package/lib/runtime/transport-socket.js +77 -0
  184. package/lib/runtime/worker-entry.js +31 -0
  185. package/lib/sg/cli.js +601 -0
  186. package/lib/sg/log-sinks.js +214 -0
  187. package/lib/sg/mcp.js +395 -0
  188. package/lib/sg/proxy-run.js +66 -0
  189. package/lib/sg/serve-demo.js +185 -0
  190. package/lib/sg/serve.js +205 -0
  191. package/lib/sg/trace.js +118 -0
  192. package/lib/studio/protocol.js +45 -0
  193. package/lib/studio/public/app.js +310 -0
  194. package/lib/studio/public/components/ConceptEditor.js +43 -0
  195. package/lib/studio/public/components/ConceptTree.js +43 -0
  196. package/lib/studio/public/components/CorpusPanel.js +68 -0
  197. package/lib/studio/public/components/ForkTree.js +36 -0
  198. package/lib/studio/public/components/GrammarGraph.js +99 -0
  199. package/lib/studio/public/components/GraphCanvas.js +156 -0
  200. package/lib/studio/public/components/Inspector.js +48 -0
  201. package/lib/studio/public/components/LearningPanel.js +85 -0
  202. package/lib/studio/public/components/PromptConsole.js +46 -0
  203. package/lib/studio/public/components/ProviderTrace.js +39 -0
  204. package/lib/studio/public/components/SessionSplit.js +47 -0
  205. package/lib/studio/public/components/TilingOverlay.js +40 -0
  206. package/lib/studio/public/components/Timeline.js +57 -0
  207. package/lib/studio/public/index.html +26 -0
  208. package/lib/studio/public/styles.css +185 -0
  209. package/lib/studio/public/ws.js +58 -0
  210. package/lib/studio/server.js +130 -0
  211. package/lib/studio/session.js +397 -0
  212. package/lib/studio/studio.js +149 -0
  213. package/package.json +42 -0
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * AGPL-3.0-or-later. See <https://www.gnu.org/licenses/>.
6
+ */
7
+ 'use strict';
8
+ /**
9
+ * ground.js — GOLD-MINED GROUNDING RINGS (the promoted core of the 2026-07-06 ring-grounding cells).
10
+ *
11
+ * The grounding problem: which declared vocab unit does a surface form (a question token, a model's raw
12
+ * extraction) refer to? Lexical matching cannot learn it (paraphrases/synonyms miss) and a model's holistic
13
+ * judgment errs — measured on two domains (entity→table: every deterministic variant ≤ the model's 0.764;
14
+ * attribute vocab: a loose-prompt extractor grounds 0/18 hard paraphrases). The lattice's answer is the
15
+ * defeasible SYNONYM RING grown from ORACLE-LABELED traffic behind the one admission gate — this module is
16
+ * that miner, promoted after two cells exercised the identical shape (tables: joins 0.764→0.800, the first
17
+ * lever to beat the model; attributes: 0→11/18 at 0 served-wrong, traps refused).
18
+ *
19
+ * Episodes → per-(alias, member) evidence → `registry.js#decideRingAdmission` (the per-unit counterfactual
20
+ * gate) → `registry.js#mergeRingProposals` (confluence + provenance, retractable). THE GRAINS THAT MATTER
21
+ * (each measured against its own failed variant):
22
+ * • support ≥ minSupport (default 3) — the df-2 fluke door under a noisy oracle (Rule-of-Three);
23
+ * • 0 counters, with an optional per-episode `verdict` able to return 'neutral' — the middle bucket
24
+ * (member ∈ gold but the grounded set over-shoots) must stay NEUTRAL: counting it against kills true
25
+ * aliases through lexical-base noise (0.800→0.733 measured);
26
+ * • vacuity BOTH by base (the episode's `base` set already grounds the member without rings) AND by prior
27
+ * (baseRate > maxBaseRate: a member the oracle nearly always contains is grounded by the prior alone —
28
+ * and a degenerate/constant oracle makes EVERYTHING vacuous → 0 admissions);
29
+ * • the DOCTRINE (the registry's own): zero-under-noise is NOT the claim at finite support — the claim is
30
+ * a BOUNDED admission rate under a structureless oracle plus RECOVERABILITY (provenance `via`, one
31
+ * retractRingAlias per bad alias, 10/10 localized flips measured). Alias grain: prefer STABLE surface
32
+ * tokens over model raw extractions (24 vs 9 aliases, serve 11 vs 2 — the same-pattern raw varies).
33
+ *
34
+ * @param episodes [{ aliases:[string…], gold:Set|Array|string|null, base?:Set|Array }] — one oracle-labeled
35
+ * occurrence: the surface forms seen, the oracle's member(s) (null = out-of-vocabulary), and
36
+ * optionally the members already grounded WITHOUT rings (the lexical/enum base).
37
+ * @param opts.registry a registry to grow ({keys:{[key]:{enum,…}}}), or use opts.key+opts.enum to build one.
38
+ * @param opts.key the registry key the rings live under (default 'unit').
39
+ * @param opts.enum the closed member vocabulary (required unless opts.registry carries it).
40
+ * @param opts.minSupport gate threshold (default 3).
41
+ * @param opts.maxBaseRate vacuity-by-prior threshold (default 0.8).
42
+ * @param opts.verdict optional (episode, member) => 'support'|'counter'|'neutral' — defaults to
43
+ * member∈gold → 'support' else 'counter'. (The subset/overshoot rule of the table cell
44
+ * is a custom verdict; 'neutral' is the load-bearing middle bucket.)
45
+ * @param opts.via provenance tag (default 'gold-mined').
46
+ * @returns { registry, admitted, rejected, stats:{ candidates, admitted, vacuous, countered, confluenceRejected } }
47
+ */
48
+
49
+ var reg = require('./registry.js');
50
+
51
+ function toSet( v ) {
52
+ if ( v == null ) return new Set();
53
+ if ( v instanceof Set ) return v;
54
+ if ( Array.isArray(v) ) return new Set(v);
55
+ return new Set([v]);
56
+ }
57
+
58
+ function mineGroundingRings( episodes, opts ) {
59
+ opts = opts || {};
60
+ var registry = opts.registry;
61
+ if ( !registry ) {
62
+ if ( !Array.isArray(opts.enum) ) throw new Error('mineGroundingRings needs opts.registry or opts.enum (the closed member vocabulary)');
63
+ registry = { keys: {}, version: 'v1' };
64
+ registry.keys[opts.key || 'unit'] = { enum: opts.enum.slice() };
65
+ }
66
+ var key = opts.key || Object.keys(registry.keys)[0] || 'unit';
67
+ var entry = registry.keys[key];
68
+ if ( !entry || !Array.isArray(entry.enum) ) throw new Error('mineGroundingRings: registry key "' + key + '" has no enum');
69
+ var members = entry.enum;
70
+ var minSupport = opts.minSupport == null ? 3 : opts.minSupport;
71
+ var maxBaseRate = opts.maxBaseRate == null ? 0.8 : opts.maxBaseRate;
72
+ var verdict = opts.verdict || function ( ep, member ) { return toSet(ep.gold).has(member) ? 'support' : 'counter'; };
73
+
74
+ // vacuity-by-prior: a member the oracle nearly always names is grounded by the prior alone.
75
+ var eps = (episodes || []).map(function ( ep ) { return { aliases: [...new Set(ep.aliases || [])], gold: toSet(ep.gold), base: toSet(ep.base) }; });
76
+ var baseRate = {};
77
+ for ( var mi = 0; mi < members.length; mi++ ) {
78
+ var m = members[mi];
79
+ var n = 0;
80
+ for ( var e = 0; e < eps.length; e++ ) if ( eps[e].gold.has(m) ) n++;
81
+ baseRate[m] = eps.length ? n / eps.length : 0;
82
+ }
83
+
84
+ // per-(alias, member) evidence over the episodes.
85
+ var stats = {};
86
+ for ( var i = 0; i < eps.length; i++ ) {
87
+ var ep = eps[i];
88
+ for ( var a = 0; a < ep.aliases.length; a++ ) {
89
+ var alias = String(ep.aliases[a]);
90
+ if ( !alias ) continue;
91
+ for ( var j = 0; j < members.length; j++ ) {
92
+ var member = members[j];
93
+ var v = verdict(ep, member);
94
+ if ( v !== 'support' && v !== 'counter' ) continue; // 'neutral' — the middle bucket
95
+ var k = alias.toLowerCase() + '::' + member;
96
+ var s = stats[k] || (stats[k] = { alias: alias, member: member, support: 0, counter: 0, nonVacuous: 0 });
97
+ if ( v === 'support' ) { s.support++; if ( !ep.base.has(member) ) s.nonVacuous++; }
98
+ else s.counter++;
99
+ }
100
+ }
101
+ }
102
+
103
+ // the gate, per candidate — then the confluence-checked merge (provenance, retractable).
104
+ var proposals = [];
105
+ var vacuous = 0, countered = 0, candidates = 0;
106
+ for ( var sk in stats ) {
107
+ var c = stats[sk];
108
+ if ( c.support + c.counter < Math.min(2, minSupport) ) continue; // below any evidence bar — not a candidate
109
+ candidates++;
110
+ var d = reg.decideRingAdmission({
111
+ member : c.member,
112
+ withAlias : c.support >= minSupport && c.counter === 0,
113
+ withoutAlias: c.nonVacuous === 0 || baseRate[c.member] > maxBaseRate
114
+ });
115
+ if ( d.reason === 'vacuous' ) { vacuous++; continue; }
116
+ if ( !d.admit ) { countered++; continue; }
117
+ proposals.push({ key: key, alias: c.alias, member: c.member, via: opts.via || 'gold-mined' });
118
+ }
119
+ var merged = reg.mergeRingProposals(registry, proposals);
120
+ return {
121
+ registry: merged.registry,
122
+ admitted: merged.admitted,
123
+ rejected: merged.rejected,
124
+ stats: { candidates: candidates, admitted: merged.admitted.length, vacuous: vacuous, countered: countered, confluenceRejected: merged.rejected.length }
125
+ };
126
+ }
127
+
128
+ /**
129
+ * ringTouch — the serve-side half: which members do these surface forms ground to, through the grown ring?
130
+ * (Both cells' serve loop.) Ambiguity is the CALLER's policy — the sound default is "unique member or refuse"
131
+ * (the hybrid rescue of the attribute cell served 11/18 at 0 wrong with exactly that rule).
132
+ * @returns Set<member>
133
+ */
134
+ function ringTouch( aliases, registry, key ) {
135
+ var entry = registry && registry.keys && registry.keys[key || Object.keys(registry.keys)[0]];
136
+ var syn = (entry && entry.synonyms) || {};
137
+ var norm = {};
138
+ for ( var i = 0; i < (aliases || []).length; i++ ) norm[String(aliases[i]).toLowerCase().trim()] = true;
139
+ var touched = new Set();
140
+ for ( var m in syn ) {
141
+ var ring = syn[m] || [];
142
+ for ( var a = 0; a < ring.length; a++ )
143
+ if ( norm[String(ring[a]).toLowerCase().trim()] ) { touched.add(m); break; }
144
+ }
145
+ return touched;
146
+ }
147
+
148
+ module.exports = { mineGroundingRings: mineGroundingRings, ringTouch: ringTouch };
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * AGPL-3.0-or-later. See <https://www.gnu.org/licenses/>.
6
+ */
7
+ 'use strict';
8
+ /**
9
+ * higher-order — THE METHOD-SLOT / higher-order `need` (roadmap 2026-07-09 §5(a); ZERO-CORE). Until now a part
10
+ * declares only DATA-needs (it needs a VALUE). §5(a) adds the BEHAVIOURAL hole: a "loop" method declares
11
+ * *"I need a SUB-METHOD in this slot"* (the loop body, the stop predicate) = a **method-slot** (`kind:'subgraph'`),
12
+ * filled by DISPATCH (P2/serveLeaf) — a slot filled by a dispatched method, not hardcoded. Swapping the dispatched
13
+ * body changes the loop's behaviour: that IS the loop-in-loop. Soundness across the extra hop is KG-PROXY-2 (GO).
14
+ *
15
+ * `makeHigherOrderServe({ loops, bodies, pool })` → a projection `serve(leaf)`:
16
+ * • DISPATCH the body sub-method for the leaf's method-slot (`loop.bodyKeyOf(leaf)` → a libraryKey);
17
+ * • APPLY it (map/all/any/fold) over the leaf's items — each application is a MOUNTED, gated invoke (P1/P3/P4)
18
+ * of the dispatched body → the loop's behavioural hole is filled at runtime by a dispatched method.
19
+ * The body sub-methods reuse `makeMethodServe` (P6), so each body application is itself dispatch + mount + gate.
20
+ *
21
+ * NESTING (map-of-maps): when a body key names ANOTHER loop (not a plain method), the application RECURSES — the
22
+ * inner loop's items are the current item. Bounded by construction (finite items × finite inner items = a finite
23
+ * tree) + a CYCLE guard (a loop that transitively names itself REFUSES, the control-structure analog of G3). The
24
+ * soundness of the stack of nested hops = KG-PROXY-2 (each hop bounded + gated).
25
+ */
26
+ const { makeMethodServe } = require('./serve-leaf.js');
27
+
28
+ /**
29
+ * @param spec.bodies { <libraryKey>: <makeMethodServe method spec> } the candidate SLOT FILLERS (dispatched bodies).
30
+ * each body's buildSeed receives a leaf carrying `.item` (the current element).
31
+ * @param spec.loops { <libraryKey>: { bodyKeyOf(leaf)->libraryKey, items(leaf)->[..], combinator?, fold?, init? } }
32
+ * the higher-order "loop" methods. combinator: 'map'(default) | 'all' | 'any' | 'fold'.
33
+ * @param spec.keyOf (leaf) => loop libraryKey (default: leaf.produces || leaf.id).
34
+ * @param spec.pool a shared P3 invoke-pool (default: own).
35
+ * @returns serve async (leaf, ctx) => value (+ serve.pool, serve.close()).
36
+ */
37
+ function makeHigherOrderServe( spec ) {
38
+ spec = spec || {};
39
+ const pool = spec.pool || require('../index.js').createInvokePool();
40
+ const loops = spec.loops || {};
41
+ const keyOf = spec.keyOf || (( leaf ) => leaf.produces || leaf.id);
42
+ // the SLOT FILLERS are served exactly like any leaf method (P6) — dispatch + mount + gate — keyed by the body key.
43
+ const bodyServe = makeMethodServe({ methods: spec.bodies || {}, keyOf: ( bl ) => bl.__key, pool });
44
+
45
+ // apply the body over ONE item: a body key that names a nested LOOP → RECURSE (the inner loop's items = this item);
46
+ // else dispatch+mount+gate a PLAIN method (P6). `chain` carries the loop-key path for the cycle guard.
47
+ async function applyBody( bodyKey, item, id, ctx, chain ) {
48
+ if ( loops[bodyKey] ) // NESTED loop → recurse (map-of-maps)
49
+ return runLoop(bodyKey, { id: id, produces: bodyKey, item: item, inputs: {} }, ctx, chain);
50
+ return bodyServe({ __key: bodyKey, id: id, produces: bodyKey, needs: [], inputs: {}, item: item }, ctx);
51
+ }
52
+
53
+ async function runLoop( loopKey, leaf, ctx, chain ) {
54
+ chain = chain || [];
55
+ if ( chain.indexOf(loopKey) !== -1 ) // CYCLE guard: a loop that transitively names itself → REFUSE (control-structure G3)
56
+ throw new Error('higher-order serve: cyclic nested loop "' + loopKey + '" (chain ' + chain.concat(loopKey).join('→') + ')');
57
+ const next = chain.concat(loopKey);
58
+ const loop = loops[loopKey];
59
+ const bodyKey = loop.bodyKeyOf(leaf); // DISPATCH the body into the method-slot
60
+ const items = loop.items(leaf); // items = the RESOLVED collection (never coerce undefined→[])
61
+ if ( !Array.isArray(items) ) // fail-closed: an UNRESOLVED slot is a refusal, NOT a vacuous empty loop (all([])===true)
62
+ throw new Error('higher-order serve: items(leaf) for "' + loopKey + '" is not an array (unresolved slot?) — leaf ' + leaf.id);
63
+ const results = [];
64
+ for ( let i = 0; i < items.length; i++ ) // APPLY the body (plain OR nested loop) over the items
65
+ results.push(await applyBody(bodyKey, items[i], leaf.id + ':body' + i, ctx, next));
66
+
67
+ switch ( loop.combinator || 'map' ) {
68
+ case 'all': return results.every(Boolean);
69
+ case 'any': return results.some(Boolean);
70
+ case 'fold': return results.reduce(loop.fold, loop.init);
71
+ default: return results; // 'map'
72
+ }
73
+ }
74
+
75
+ async function serve( leaf, ctx ) {
76
+ const key = keyOf(leaf);
77
+ if ( !loops[key] ) throw new Error('higher-order serve: no loop method for libraryKey "' + key + '" (leaf ' + leaf.id + ')');
78
+ return runLoop(key, leaf, ctx, []);
79
+ }
80
+ serve.pool = pool;
81
+ serve.close = () => spec.pool ? Promise.resolve() : pool.close();
82
+ return serve;
83
+ }
84
+
85
+ module.exports = { makeHigherOrderServe };
@@ -0,0 +1,132 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ 'use strict';
19
+ /**
20
+ * hotspot — the distill go/no-go DETECTOR (the §4.2-B / Proxy-KD plan-B pre-test). The cheap,
21
+ * standing question that GATES the entire distilled-NN-concept build (which otherwise stays filed).
22
+ *
23
+ * Why a pre-test at all: the fidelity-gated cache (`cache.js`) ALREADY serves an exact-key hot
24
+ * slice at 0 calls. So a parametric net buys NOTHING on an exactly-recurrent slice — it only
25
+ * earns its place where the exact-key memo keeps MISSING yet the mapping is learnable. That is a
26
+ * THREE-WAY condition, and proving/refuting it cheaply IS the deliverable:
27
+ * (1) FREQUENT — enough volume to be worth distilling (calls ≥ minCalls)
28
+ * (2) CACHE-MISSING — many distinct-but-related keys the exact-key (hitRate ≤ maxHitRate
29
+ * memo can't cover (high cardinality) ⇔ high key cardinality)
30
+ * (3) K1-SUFFICIENT — the typed surface predicts the verdict; if (fidMean ≥ minFidelity)
31
+ * even the repeated keys don't reproduce, the
32
+ * LLM reads something off-surface → unlearnable
33
+ * Only all-three ⇒ `distill-candidate` (a Phase-1 generalization measurement then confirms it).
34
+ * The honest, expected outcome on today's workloads is NO qualifying slice ⇒ the net stays filed —
35
+ * proven cheaply rather than on faith.
36
+ *
37
+ * const t = trackCache();
38
+ * const cache = createProviderCache({ ..., onHit: t.onHit, onMiss: t.onMiss, fidelity: {...} });
39
+ * // ...run the workload through the cache...
40
+ * const report = hotspots(t, cache.fid, { minCalls: 20, maxHitRate: 0.5, minFidelity: 0.8 });
41
+ */
42
+
43
+ /**
44
+ * A per-concept tally you wire into a provider cache's `onHit`/`onMiss`. `onHit` = a served
45
+ * cache hit (0 model calls); `onMiss` = a cold (uncached) derivation (a model call). Each
46
+ * distinct key cold-misses exactly once (its first sight, before it is stored), so the cold-miss
47
+ * key set is the cardinality estimate; the served hits are the amortization.
48
+ */
49
+ function trackCache() {
50
+ const perConcept = {}; // name -> { hits, misses, hitKeys:Set, missKeys:Set }
51
+ function slot( concept ) {
52
+ const n = (concept && concept._name) || String(concept);
53
+ return perConcept[n] || (perConcept[n] = { hits: 0, misses: 0, hitKeys: new Set(), missKeys: new Set() });
54
+ }
55
+ return {
56
+ perConcept,
57
+ onHit: function ( key, concept ) { const s = slot(concept); s.hits++; if ( key != null ) s.hitKeys.add(key); },
58
+ onMiss: function ( key, concept ) { const s = slot(concept); s.misses++; if ( key != null ) s.missKeys.add(key); },
59
+ reset: function () { for ( const k of Object.keys(perConcept) ) delete perConcept[k]; },
60
+ };
61
+ }
62
+
63
+ // the mean exact-key reproduce rate over a concept's fidStore entries (the keys it actually
64
+ // touched). null when no key has fidelity data yet (e.g. a pure-singleton high-cardinality
65
+ // slice — repeats are what populate `fid`, so K1-sufficiency is then UNCONFIRMED, not refuted).
66
+ function fidelityFor( keys, fidStore ) {
67
+ if ( !fidStore || !keys || !keys.size ) return { mean: null, n: 0 };
68
+ let sum = 0, n = 0;
69
+ for ( const k of keys ) {
70
+ const fs = fidStore.get(k);
71
+ if ( fs && fs.total > 0 ) { sum += fs.ok / fs.total; n++; }
72
+ }
73
+ return { mean: n ? sum / n : null, n: n };
74
+ }
75
+
76
+ /**
77
+ * Classify every tracked concept into the four buckets. Pure (no boot, no model calls).
78
+ * @param tracker a `trackCache()` collector (or `{ perConcept }`)
79
+ * @param fidStore the cache's `fid` Map (key -> { ok, total }); omit → fidelity unconfirmed
80
+ * @param opts.minCalls volume floor (default 20)
81
+ * @param opts.maxHitRate above this the exact-key cache already wins (default 0.5)
82
+ * @param opts.minFidelity K1-sufficiency threshold on the reproduce rate (default 0.8)
83
+ * @returns [{ concept, calls, hits, misses, distinctKeys, hitRate, keyCardinality, fidMean, fidN, verdict, reason }]
84
+ * sorted distill-candidates first, then by volume. verdict ∈
85
+ * { 'distill-candidate', 'cache-already-wins', 'unlearnable', 'too-rare' }.
86
+ */
87
+ function hotspots( tracker, fidStore, opts ) {
88
+ opts = opts || {};
89
+ const minCalls = opts.minCalls != null ? opts.minCalls : 20;
90
+ const maxHitRate = opts.maxHitRate != null ? opts.maxHitRate : 0.5;
91
+ const minFidelity = opts.minFidelity != null ? opts.minFidelity : 0.8;
92
+ const per = (tracker && tracker.perConcept) || tracker || {};
93
+
94
+ const rows = Object.keys(per).map(function ( name ) {
95
+ const s = per[name];
96
+ const hits = s.hits || 0, misses = s.misses || 0;
97
+ const calls = hits + misses; // served + cold (verify/escalate uncounted — a floor)
98
+ const keys = new Set([...(s.missKeys || []), ...(s.hitKeys || [])]);
99
+ const distinctKeys = keys.size;
100
+ const hitRate = calls ? hits / calls : 0;
101
+ const keyCardinality = calls ? distinctKeys / calls : 0; // ~1 = every call a new key (cache can't cover)
102
+ const fid = fidelityFor(keys, fidStore);
103
+
104
+ let verdict, reason;
105
+ if ( calls < minCalls ) {
106
+ verdict = 'too-rare'; reason = 'calls ' + calls + ' < minCalls ' + minCalls;
107
+ } else if ( hitRate > maxHitRate ) {
108
+ verdict = 'cache-already-wins'; reason = 'hitRate ' + hitRate.toFixed(2) + ' > ' + maxHitRate + ' — the exact-key memo already serves it';
109
+ } else if ( fid.mean != null && fid.mean < minFidelity ) {
110
+ verdict = 'unlearnable'; reason = 'fidMean ' + fid.mean.toFixed(2) + ' < ' + minFidelity + ' — the verdict is not a function of the typed key';
111
+ } else {
112
+ verdict = 'distill-candidate';
113
+ reason = fid.mean != null
114
+ ? 'frequent ∧ cache-missing ∧ K1-sufficient (fidMean ' + fid.mean.toFixed(2) + ') — Phase-1 generalization check next'
115
+ : 'frequent ∧ cache-missing; fidelity UNCONFIRMED (no repeats yet) — Phase-1 must confirm K1-sufficiency';
116
+ }
117
+ return {
118
+ concept: name, calls: calls, hits: hits, misses: misses, distinctKeys: distinctKeys,
119
+ hitRate: hitRate, keyCardinality: keyCardinality, fidMean: fid.mean, fidN: fid.n,
120
+ verdict: verdict, reason: reason,
121
+ };
122
+ });
123
+
124
+ const rank = { 'distill-candidate': 0, 'unlearnable': 1, 'cache-already-wins': 2, 'too-rare': 3 };
125
+ rows.sort(function ( a, b ) { return (rank[a.verdict] - rank[b.verdict]) || (b.calls - a.calls); });
126
+ return rows;
127
+ }
128
+
129
+ // convenience: are there any distill-candidates? (the go/no-go boolean).
130
+ function anyCandidate( rows ) { return (rows || []).some(function ( r ) { return r.verdict === 'distill-candidate'; }); }
131
+
132
+ module.exports = { trackCache: trackCache, hotspots: hotspots, fidelityFor: fidelityFor, anyCandidate: anyCandidate };
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ 'use strict';
19
+ /**
20
+ * Hysteresis dead-band for the grain split/merge loop (doc/WIP/HANDOFF.md §7 Tier 3 P3;
21
+ * experiment C). Zero core change — a host-side decision helper for a supervisor's injected
22
+ * `evaluate` hook (lib/authoring/supervise.js).
23
+ *
24
+ * The problem (C probe): adopting a finer grain when a hold-out score improves, and reverting
25
+ * when it regresses, OSCILLATES across episodes if the keep/revert decision uses a SINGLE
26
+ * fixed margin and the re-evaluation variance exceeds it (the probe flipped 31/60 with a fixed
27
+ * margin < noise). What terminates is a HYSTERESIS dead-band: a high bar to ADOPT the finer
28
+ * grain (`keepThreshold`) and a separate bar to REVERT it (`mergeThreshold`), with the band
29
+ * between them sized to ≥ ~3σ of the re-eval variance — plus an idempotent "grain in force"
30
+ * marker so a settled grain stops re-triggering. Then steady-state flips → 0.
31
+ *
32
+ * HONEST: this is a statistical contraction, NOT a formal termination proof (an adversarial
33
+ * hold-out with unbounded between-episode variance can still cross any finite band). It is the
34
+ * available zero-core mitigation; a formal cross-episode guarantee is open R&D.
35
+ *
36
+ * const { makeHysteresis, bandFromSigma } = require('./hysteresis');
37
+ * const h = makeHysteresis({ keepThreshold: bandFromSigma(reEvalSigma), betterIsLower: true });
38
+ * const action = h.decide(candidateScore, baselineScore, currentRegime); // 'adopt' | 'revert' | 'hold'
39
+ */
40
+
41
+ /** A ≥k·σ dead-band from an estimate of the re-evaluation std-dev (k defaults to 3, per C). */
42
+ function bandFromSigma( sigma, k ) {
43
+ return (k == null ? 3 : k) * (sigma || 0);
44
+ }
45
+
46
+ /**
47
+ * Build a hysteresis decision function.
48
+ * @param opts.keepThreshold the minimum GAIN to ADOPT the finer grain from 'coarse' (default 1).
49
+ * @param opts.mergeThreshold the minimum REGRESSION to REVERT from 'fine' (default = keepThreshold;
50
+ * keeping it ≥ keepThreshold makes the dead-band ≥ keepThreshold wide).
51
+ * @param opts.betterIsLower true (default) when a LOWER score is better (deviance/MSE/loss);
52
+ * false for higher-is-better (accuracy/reward).
53
+ * @returns { decide(candidate, baseline, regime), gain(candidate, baseline), keepThreshold, mergeThreshold }
54
+ */
55
+ function makeHysteresis( opts ) {
56
+ opts = opts || {};
57
+ var keep = opts.keepThreshold == null ? 1 : opts.keepThreshold;
58
+ var merge = opts.mergeThreshold == null ? keep : opts.mergeThreshold;
59
+ var lower = opts.betterIsLower !== false;
60
+
61
+ // how much BETTER candidate is than baseline (positive = better), in either polarity
62
+ function gain( candidate, baseline ) {
63
+ return lower ? (baseline - candidate) : (candidate - baseline);
64
+ }
65
+
66
+ return {
67
+ keepThreshold: keep,
68
+ mergeThreshold: merge,
69
+ gain: gain,
70
+ /**
71
+ * Decide from the CURRENT regime:
72
+ * 'coarse' — considering adopting the finer grain: 'adopt' iff gain ≥ keepThreshold, else 'hold'.
73
+ * 'fine' — considering reverting: 'revert' iff the finer grain REGRESSED by ≥ mergeThreshold
74
+ * (gain ≤ −mergeThreshold), else 'hold'.
75
+ * The gap (−mergeThreshold, keepThreshold) is the dead-band where nothing changes.
76
+ */
77
+ decide: function ( candidate, baseline, regime ) {
78
+ var g = gain(candidate, baseline);
79
+ if ( regime === 'fine' ) return g <= -merge ? 'revert' : 'hold';
80
+ return g >= keep ? 'adopt' : 'hold';
81
+ }
82
+ };
83
+ }
84
+
85
+ module.exports = { makeHysteresis: makeHysteresis, bandFromSigma: bandFromSigma };
@@ -0,0 +1,106 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU Affero General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU Affero General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ 'use strict';
19
+ /**
20
+ * Authoring & R&D toolkit — packaged so the substrate tools are usable "à nu"
21
+ * without deep-path requires.
22
+ *
23
+ * const Graph = require('skynet-graph');
24
+ * const { contract, method, abstract } = Graph.authoring; // exposed as a static, like Graph.providers
25
+ * contract.checkCompose(m1, m2);
26
+ *
27
+ * Unlike the providers barrel (which flattens function names), this barrel is
28
+ * NAMESPACED — one key per module — because authoring modules legitimately share
29
+ * export names (e.g. `validate` in both validate.js and bounded-merge.js,
30
+ * `verify` in recall.js, `evaluate` in abstraction.js). Each module also stays
31
+ * importable on its own (`require('skynet-graph/lib/authoring/contract')`) — the
32
+ * barrel is a convenience, not a gate.
33
+ */
34
+
35
+ module.exports = {
36
+ // ── Concept authoring & validation ───────────────────────────────────────
37
+ concepts : require('./concepts'), // buildConceptTree(dir) — JSONC concept tree
38
+ validate : require('./validate'), // validateConceptTree — author-time structural validator (K1 barrier)
39
+ author : require('./author'), // authorConcept — declarative AI-authoring + CEGIS loop
40
+ contract : require('./contract'), // the defeasible separation-triple checker (checkCompose/assertPost/satisfies/reviseOnBlame)
41
+ grammarGraph : require('./grammar-graph'), // conceptFactGraph — the concept↔fact grammar graph
42
+
43
+ // ── Concept-as-graph methods (build / select / mount / abstract) ──────────
44
+ method : require('./method'), // applySubgraphArg/mapSubgraph/lintMethod/selectCluster
45
+ mount : require('./mount'), // tiered mount controller (instance/inline/frozen)
46
+ abstract : require('./abstract'), // F6 relativize/instantiate/antiUnify/methodTransform (structural transfer) + generalizeContent/fillContentHoles (LGG content-hole discovery for the adapt operator)
47
+ abstraction : require('./abstraction'), // the abstraction-gate currency (applies→LLM-cost)
48
+ mdl : require('./mdl'), // static bits-based MDL ranker/pre-filter in front of abstraction.evaluate (#12)
49
+ decompose : require('./decompose'), // plan decomposition / forkPlan auto-tiling
50
+ registry : require('./registry'), // G-1 the interface-alphabet REGISTRY (Σ_sep): curated+versioned vocab CATALOG — deriveRegistry/freezeRegistry/specForKey (rings live here)/mergeRingProposals (borderline proposals, confluence re-checked)/checkTreeAgainstRegistry (enforce the frozen canon)
51
+ extract : require('./extract'), // bounded subgraph EXTRACTION for fork / multi-process ship (program slicing: segment-closed 1-hop ball + frozen frontier; mergeSlice = single-writer + assumption-recheck; the fork/ship lever, ZERO-CORE)
52
+ boundedMerge : require('./bounded-merge'), // bounded N→1 projection (assume-guarantee merge interface)
53
+ reaggregate : require('./reaggregate'), // defeasibleAggregate (fold-back / merge)
54
+ memoStability: require('./memo-stability'), // memoSnapshot/memoDiff — the canonicalization-stability discipline (F4 adjudicator)
55
+ support : require('./support'), // support grammar (problem-solving scaffold)
56
+
57
+ // ── Library learning / crystallization / packaging ───────────────────────
58
+ crystallize : require('./crystallize'), // distil recurrent methods from traces — provider-fusion + crystallizeStructural (re-mountable defeasible method, DECLARED frontier + reified FrontierSignature + libraryKey dispatch + lintFrontier) + synthesizeContract (CLS capstone)
59
+ mine : require('./mine'), // traceMiner/methodTrace — mine producer→consumer chains + recurrent STRUCTURAL methods (mineMethods: antiUnify + K1-ceiling guard; declaredCtx = DECLARED frontier; emitEquivalence)
60
+ compress : require('./compress'), // the DIGRAM-grain affinity miner (2026-07-03 GO kill-gate): adjacent-pair support + dispatchable sub-expansion index (K1 rule, kind|ctx grains) + mdl.js-ΔL fold + typed-loop-parity subpath patches + the method-FOREST skeleton
61
+ library : require('./library'), // the method-library index + dispatch (the structuring↔concept-DLL juncture): O(1) libraryKey bucket → refine by app-conditions → ranked candidates
62
+ combinator : require('./combinator'), // the dispatch→MOUNT bridge (P2.5): a higher-order concept fills its behavioral hole with a DISPATCHED library fragment (recombination at 0 calls; durable guard, not the self-flag)
63
+ adapt : require('./adapt'), // the adapt-or-forge CONTROLLER (the creative loop's drive): retrieve(hit,0-call) → forge/adapt via the model (reuse neighbours) → verifier-gate (contract) → index back (amortise). antiUnifyAdapt = the principled CONTENT-FORGE adapt (LGG auto-discovers content holes, forge only those, reuse skeleton; opts.adaptContent)
64
+ hotspot : require('./hotspot'), // distill go/no-go detector (frequent ∧ cache-missing ∧ K1-sufficient slice) (#§4.2-B)
65
+ composeHotspot: require('./compose-hotspot'),// compress.js kill-gate, STRUCTURAL half: does a data-flow composite RECUR cross-distinct-task? (interleave-robust provenance chains + RE-PAIR; G-a poly ceiling)
66
+ costProbe : require('./cost-probe'), // compress.js kill-gate, COST half: does a composite-memo elide FORGE calls the leaf floor can't? (the thin canonicalization-in-context band — the axis that actually decides the build)
67
+ recall : require('./recall'), // recallAndVerify index (retrieve-or-forge)
68
+ methodPack : require('./method-pack'), // portable LEARNED-method package (.sgc sibling)
69
+ corpusPack : require('./corpus-pack'), // .sgc corpus exchange + derived manifest
70
+ latticePack : require('./lattice-pack'), // portable TYPED-LATTICE package (.sgc sibling — registry: isa vocab + grown rings)
71
+ methodExplorer: require('./method-explorer'), // list+judge a concept-method population (title/category/description + coverage/openness)
72
+ retention : require('./retention'), // usage-tracked + self-evicting store (the reuse/keep-or-evict axis; sibling of lifecycle's plasticity axis)
73
+ store : require('./store'), // createFileStore (durable cross-restart library)
74
+ stock : require('./stock'), // gold-gated concept-method STOCK building (goldGate + consistencyVote + packStock) — the reusable core of the dataset-stock pilot
75
+ ground : require('./ground'), // gold-mined GROUNDING RINGS (mineGroundingRings + ringTouch) — surface form → vocab unit through the ring gate (the 2026-07-06 cells' core)
76
+ glossary : require('./glossary'), // P2 the cross-round terminology REFERENCE: createGlossary = a persistent lattice-backed store of canonical GROUNDED notions (harvest = witness-overlap SELECTS the member + mergeRingProposals ADMITS the alias under confluence, audited/versioned/retractable; reconcile = JTMS entry-retract with cascade; inject = citable-vocabulary block). The managed reference the re-split needs — ZERO-CORE (assembles registry.js + canonicalize.js)
77
+ granularity : require('./granularity'), // P2 the STRUCTURAL granularity ARBITER (the sonde's grounded dimensions): clusterByGrounding = connected components on the witness co-citation graph; arbitrate = the lazy 2-régime verdict (coherent | MIXED = the re-plan `frame: TOO-NARROW` grounded signal | unstructured = escalate to Q2). No prose parsing — dimensions come from grounding, never fabricated. ZERO-CORE
78
+ datasetAdapter: require('./dataset-adapter'), // labelled query dataset → {query,context,klass,goldShape} (WikiSQL built-in; register more) — "plusieurs datasets" one registry away
79
+
80
+ // ── Supervision / lifecycle / runtime regime ─────────────────────────────
81
+ supervise : require('./supervise'), // reactive supervisor (Stuck→hypothesize→evaluate→revert)
82
+ relearn : require('./relearn'), // standing autonomous un-learn loop (blame→revise→patch as reactive concepts)
83
+ loop : require('./loop'), // autonomous episode loop
84
+ typedLoop : require('./typed-loop'), // the fused RECURSIVE TYPED decompose operator (loop.js emergent depth × canon-snapped stepKind children, prose untracked → the decompose trace is K1-crystallizable)
85
+ rebalance : require('./rebalance'), // R1 the DEFEASIBLE REBALANCING FIXPOINT (E2∘E1∘E3∘E4 under a lexicographic measure; kill-gated KG-R1b) — drives a degenerate plan to a balanced fixpoint; usable à nu
86
+ dagDecompose : require('./dag-decompose'), // model-driven TYPED DAG DECOMPOSE (study 2026-07-08 §6.1): decompose PROMPT + grammar-constrained decoding → a needs/produces DAG (stepKind on a closed enum = the K1 barrier); makeDagDecompose = plan-loop's decompose seam feeding the projection
87
+ contextProject: require('./context-project'),// R1 §2 GRAPH-NATIVE CONTEXT PROJECTION (pool ref-parent + counter gate + recursive down-projection/remontée) — each node completes its BOUNDED context from structure; guardPlan (recursive deadlock guard) + createContextProjection; the real projection wired into plan-loop
88
+ soundInvoke : require('./sound-invoke'), // P4 the CONSTAT operationalized: soundInvokeMerge = assertPost (G1 frame via the P1 write-footprint + post-holds + G2 oracle) → mergeSlice (assumption-recheck + single-writer) → sequenced commit; a violation REFUSES (blame for reviseOnBlame) so composé-cross-instance ≡ plat, *sainement*
89
+ segmentProxy : require('./segment-proxy'), // P2 the reactive SEGMENT-PROXY: makeSegmentProxy = a concept-method that casts on its contract's conditions → delegates via the P1 invoke → gates via P4 assertPost → posts summaryFacts JTMS-visible (the good C.8: an interface). Cast = the cost gradient: delegate → gate → LAST-RESORT forge fallback (reconstructStack up the parentSeg chain, bounded; forge re-enters the gate) — the §5 plasticity hook
90
+ serveLeaf : require('./serve-leaf'), // P6 the UNIFICATION: makeMethodServe = a projection serve(leaf) that DISPATCHES (libraryKey) + MOUNTS a concept-method (invoked on a P3 shared instance, gated by P4) — a leaf IS a mounted method, not an opaque value; folds projection + runtime + library + method into one structure
91
+ higherOrder : require('./higher-order'), // §5(a) the METHOD-SLOT / higher-order need: makeHigherOrderServe = a "loop" method whose behavioural hole (the body / stop predicate) is filled by a DISPATCHED sub-method (P2), applied (map/all/any/fold) over items — swap the dispatched body → different behaviour = the loop-in-loop (soundness across the hop = KG-PROXY-2 GO)
92
+ forgeFallback: require('./forge-fallback'), // §5(b) the last-resort LEARNING fallback wired for P2's forge hook: reconstructStack → stackToPrompt (the LEVER) → forge (the model) → RE-ENTER the gate (assertPost DISPOSES a bad forge) → INDEX-BACK (amortise; next matching case = a dispatch hit). `maxRounds>1` = the BOUNDED BLAME-DRIVEN RETRY (residue USE-2(d), the OPEN-regime DUAL of negotiate): on a refusal, fold the violated atoms + rejected-history into the next prompt and revise, bounded → typed refusal. LIVE-PROVEN 17/24→24/24 at 0-false. Default maxRounds 1 = one-shot, byte-identical
93
+ forest : require('./forest'), // §5(c) the MULTI-PATH generalization: makeForestServe = a concept-method as a LIBRARY of alternative sub-paths; the FIRST candidate that dispatches+mounts+GATES sound is SELECTED (one stays active — the confluence guarantee that dodges G3: SELECT one path, never COMPOSE coupled retractable methods → no oscillation). Forest exhausted → the §5(b) forge
94
+ splitServe : require('./split-serve'), // the RECURSIVE composite path (KG-SPLIT GO 2026-07-10): kind-route → NL SPLIT into standalone sub-questions → plain-decompose each → assemble operand|op. Single-shot composed emission refuted (0-14%); the split reaches 55% strict / 64% relaxed / 0% plain-FP live — makes the COMPOSED certified vocabulary dispatchable (the trusted-tier growth lever). Fail-closed: malformed/conflicting/throwing split → plain fallback on the original query
95
+ negotiate : require('./negotiate'), // the bounded LLM↔GRAPH dialogue (CLOSED-domain): model PROPOSES a typed candidate → graph GATES (assertPost) → on mismatch, blame + ENUMERATED admissible options (tested through the gate, not guessed) → revision prompt → revise, bounded K. 0-false · honest refusal (empty options, never forced) · termination. The closed-domain sibling of forge-fallback's open-regime blame-retry
96
+ masterLoop : require('./master-loop'), // always-on master-loop (retrieve-or-forge→switch→bounded-context)
97
+ lifecycle : require('./lifecycle'), // onCast/teardown lifecycle helpers
98
+ clock : require('./clock'), // makeReaper / TTL clock (live regime)
99
+ hysteresis : require('./hysteresis'), // makeHysteresis — band stabilization
100
+
101
+ // ── Experimental / shelved (probabilistic concept-nets — see studies) ─────
102
+ conceptNet : require('./concept-net'), // (shelved) concept-net populations at the fixpoint
103
+ graphNet : require('./graph-net'), // (shelved) graph-net variant
104
+ equilibrium : require('./equilibrium'), // (shelved) implicit-equilibrium solver
105
+ ste : require('./ste') // (shelved) straight-through soft-train/hard-infer
106
+ };
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Copyright 2026 Nathanael Braun
3
+ * @author : Nathanael BRAUN <pp9ping@gmail.com>
4
+ *
5
+ * AGPL-3.0-or-later. See <https://www.gnu.org/licenses/>.
6
+ */
7
+ 'use strict';
8
+ /**
9
+ * lattice-morphism — STRUCTURAL reconciliation between two CARVINGS of a shared grounded domain (owner 2026-07-09;
10
+ * ZERO-CORE). The LEXICAL case (same classes, different labels: `high` vs `elevated`) is handled by the synonym ring
11
+ * (`canonicalize.js`). The STRUCTURAL case — two DIFFERENT partitions of the same ordered base ({low,mid,high} vs
12
+ * {cold,cool,warm,hot}) — is NOT a synonym: it is a MORPHISM derived from INTERVAL OVERLAP on the shared base. It is
13
+ * • GROUNDED — computed from the intervals, not guessed;
14
+ * • DETERMINISTIC on a known VALUE (both carvings classify the same v);
15
+ * • SET-VALUED on a coarse MEMBER (a member that spans several target members = the coarsening loss);
16
+ * • DEFEASIBLE at boundaries (a value within `eps` of a disputed cut is flagged `ambiguous` → escalate, never a
17
+ * forced map) and FAIL-CLOSED off the base (a value outside every interval → null, never a forced nearest).
18
+ *
19
+ * REGIME (the honest ceiling): this works when both carvings anchor to a SHARED, GROUNDED base (a numeric grain, a
20
+ * measurable). Two UNGROUNDED carvings (no shared measurable) have no base to factor through → the morphism must be
21
+ * LEARNED from co-assignment on shared instances (paired data) — the harder residual, out of scope here.
22
+ *
23
+ * const m = memberMorphism(A, B); m.reconcile('high') // -> ['warm','hot'] (A-high is coarser)
24
+ * classifyValue(B, 70) // -> { member:'warm' } (a known value reconciles exactly)
25
+ *
26
+ * A carving = [{ member, lo, hi }] over a shared numeric base; intervals are [lo, hi) with the TOP interval closed.
27
+ */
28
+
29
+ // do two intervals overlap on the shared base?
30
+ function overlaps( a, b ) { return a.lo < b.hi && b.lo < a.hi; }
31
+
32
+ /**
33
+ * classifyValue(carving, v, opts) → { member, ambiguous }
34
+ * The member whose interval contains v (deterministic). `ambiguous:true` when v is within `opts.eps` of an INTERNAL
35
+ * boundary (a disputed cut) — the defeasible signal. `member:null` when v is outside every interval (fail-closed).
36
+ */
37
+ function classifyValue( carving, v, opts ) {
38
+ opts = opts || {};
39
+ const eps = opts.eps || 0;
40
+ const his = carving.map(( c ) => c.hi ), los = carving.map(( c ) => c.lo );
41
+ const maxHi = Math.max.apply(null, his), minLo = Math.min.apply(null, los);
42
+ let member = null;
43
+ for ( const c of carving ) if ( v >= c.lo && (v < c.hi || (c.hi === maxHi && v <= c.hi)) ) { member = c.member; break; }
44
+ let ambiguous = false;
45
+ for ( const c of carving ) if ( c.lo !== minLo && Math.abs(v - c.lo) <= eps ) ambiguous = true; // near an internal cut
46
+ return { member: member, ambiguous: ambiguous };
47
+ }
48
+
49
+ /**
50
+ * memberMorphism(from, to) → { map, reconcile(member) }
51
+ * The member→member morphism by interval overlap. `map[m]` / `reconcile(m)` = the target members `m` overlaps: a
52
+ * SINGLETON = clean (from ⊆ to at that region), MULTI = `m` is COARSER (coarsening loss), EMPTY = disjoint (no
53
+ * fabricated overlap). Symmetric — call both directions for the full picture.
54
+ */
55
+ function memberMorphism( from, to ) {
56
+ const map = Object.create(null);
57
+ for ( const f of (from || []) ) map[f.member] = (to || []).filter(( t ) => overlaps(f, t) ).map(( t ) => t.member );
58
+ return { map: map, reconcile: ( m ) => map[m] || [] };
59
+ }
60
+
61
+ module.exports = { classifyValue, memberMorphism, overlaps };