clm-kernel 0.0.0__py3-none-any.whl

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 (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,652 @@
1
+ """
2
+ MCard Unified Schema Loader (Singleton)
3
+
4
+ This module provides the ONLY access point to MCard database schemas.
5
+ All schemas are loaded from: schema/mcard_schema.sql
6
+
7
+ This implements a Singleton pattern to ensure:
8
+ 1. Schema file is read only once
9
+ 2. All code uses the same schema definitions
10
+ 3. NO duplicate CREATE TABLE statements exist elsewhere
11
+
12
+ IMPORTANT: The SQL file MUST exist. There are no fallbacks.
13
+ If the file is missing, an explicit error is raised.
14
+
15
+ Usage:
16
+ from clm.schemas import MCardSchema
17
+
18
+ # Get the singleton instance
19
+ schema = MCardSchema.get_instance()
20
+
21
+ # Get a specific table's CREATE statement
22
+ card_schema = schema.get_table('card')
23
+
24
+ # Initialize all tables on a connection
25
+ schema.init_all_tables(conn)
26
+
27
+ Single Source of Truth:
28
+ schema/mcard_schema.sql
29
+
30
+ See: docs/architecture/Handle_Vector_Similarity_Design.md
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import re
36
+ import sqlite3
37
+ import threading
38
+ from pathlib import Path
39
+ from typing import TYPE_CHECKING
40
+
41
+ # ─────────────────────────────────────────────────────────────────────────────
42
+ # Schema File Loading (Delegated to Core)
43
+ # ─────────────────────────────────────────────────────────────────────────────
44
+
45
+ try:
46
+ import mcard_core_py # type: ignore
47
+ except ImportError:
48
+ import logging
49
+
50
+ logging.getLogger(__name__).error(
51
+ "mcard_core_py native extension not found! Schema loading will fail."
52
+ )
53
+
54
+
55
+ def _find_project_root() -> Path:
56
+ from clm.cross_cutting.config.config_constants import _find_project_root
57
+ return _find_project_root()
58
+
59
+
60
+ def _find_schema_path() -> Path:
61
+ """Return the canonical core schema SQL path."""
62
+ local_path = Path(__file__).resolve().parent / "mcard_schema.sql"
63
+ if local_path.exists():
64
+ return local_path
65
+ schema_path = _find_project_root() / "schema" / "mcard_schema.sql"
66
+ if not schema_path.exists():
67
+ raise FileNotFoundError(f"Canonical schema file not found in {local_path} or {schema_path}")
68
+ return schema_path
69
+
70
+
71
+ def _get_core_sql() -> str:
72
+ """Retrieve the unified core schema file from the canonical SQL file on disk,
73
+ falling back to the Rust foundation if file reading fails."""
74
+ try:
75
+ path = _find_schema_path()
76
+ if path.exists():
77
+ return path.read_text(encoding="utf-8")
78
+ except Exception:
79
+ pass
80
+ return mcard_core_py.get_core_schema() # type: ignore
81
+
82
+
83
+ def _get_vector_sql() -> str:
84
+ """Retrieve the vector schema file from the canonical SQL file on disk,
85
+ falling back to the Rust foundation if file reading fails."""
86
+ try:
87
+ local_path = Path(__file__).resolve().parent / "mcard_vector_schema.sql"
88
+ if local_path.exists():
89
+ return local_path.read_text(encoding="utf-8")
90
+ schema_path = (
91
+ _find_project_root() / "schema" / "mcard_vector_schema.sql"
92
+ )
93
+ if schema_path.exists():
94
+ return schema_path.read_text(encoding="utf-8")
95
+ except Exception:
96
+ pass
97
+ return mcard_core_py.get_vector_schema() # type: ignore
98
+
99
+
100
+ class MCardSchema:
101
+ """
102
+ Singleton class for MCard database schema management.
103
+
104
+ This class ensures that ALL schema definitions come exclusively from:
105
+ schema/mcard_schema.sql
106
+
107
+ There are NO hardcoded SQL fallbacks in this class.
108
+
109
+ Thread-safe singleton implementation.
110
+
111
+ Usage:
112
+ schema = MCardSchema.get_instance()
113
+ schema.get_table('card') # Get CREATE TABLE statement
114
+ schema.init_core_tables(conn) # Initialize core tables
115
+ """
116
+
117
+ _instance: MCardSchema | None = None
118
+ _lock = threading.Lock()
119
+
120
+ # Table-to-layer mapping (defines the architecture)
121
+ TABLE_LAYERS = {
122
+ # Layer 1: Core
123
+ "card": "core",
124
+ "card_tombstone": "core",
125
+ "documents": "core", # Legacy FTS table for backward compatibility
126
+ # Layer 2: Handle System
127
+ "handle_registry": "handle",
128
+ "handle_history": "handle",
129
+ # Layer 3: CRDT Federation
130
+ "learning_event": "federation",
131
+ # Layer 4: Vector Storage
132
+ "mcard_vector_metadata": "vector",
133
+ "mcard_embeddings": "vector",
134
+ "mcard_fts": "vector",
135
+ # Layer 4: Semantic Versioning
136
+ "handle_version_vectors": "semantic",
137
+ "version_similarity_cache": "semantic",
138
+ # Layer 5: Knowledge Graph
139
+ "graph_entities": "graph",
140
+ "graph_relationships": "graph",
141
+ "graph_communities": "graph",
142
+ "graph_extractions": "graph",
143
+ # Metadata
144
+ "schema_version": "metadata",
145
+ }
146
+
147
+ def __init__(self) -> None:
148
+ """Private constructor - use get_instance() instead."""
149
+ self._schema_path: Path | None = None
150
+ self._raw_sql: str | None = None
151
+ self._statements: list[str] = []
152
+ self._tables: dict[str, str] = {}
153
+ self._indexes: dict[str, str] = {}
154
+ self._loaded: bool = False
155
+
156
+ @classmethod
157
+ def get_instance(cls) -> MCardSchema:
158
+ """
159
+ Get the singleton instance of MCardSchema.
160
+
161
+ Thread-safe lazy initialization.
162
+
163
+ Returns:
164
+ The single MCardSchema instance
165
+
166
+ Raises:
167
+ FileNotFoundError: If schema/mcard_schema.sql cannot be found
168
+ """
169
+ if cls._instance is None:
170
+ with cls._lock:
171
+ # Double-check locking
172
+ if cls._instance is None:
173
+ cls._instance = cls()
174
+ cls._instance._load()
175
+ return cls._instance
176
+
177
+ @classmethod
178
+ def reset_instance(cls) -> None:
179
+ """Reset the singleton (primarily for testing)."""
180
+ with cls._lock:
181
+ cls._instance = None
182
+
183
+ def _load(self) -> None:
184
+ """Load and parse the schema file(s)."""
185
+ if self._loaded:
186
+ return
187
+
188
+ self._schema_path = _find_schema_path()
189
+
190
+ # Fetch SQL string definitions straight from the C FFI pointer
191
+ self._raw_sql = _get_core_sql()
192
+
193
+ vector_sql = _get_vector_sql()
194
+ if vector_sql:
195
+ self._raw_sql += "\n\n" + vector_sql
196
+
197
+ self._statements = self._parse_statements(self._raw_sql)
198
+ self._tables = {}
199
+ self._indexes = {}
200
+
201
+ for stmt in self._statements:
202
+ name = self._extract_name(stmt)
203
+ if name:
204
+ if (
205
+ "CREATE TABLE" in stmt.upper()
206
+ or "CREATE VIRTUAL TABLE" in stmt.upper()
207
+ ):
208
+ self._tables[name.lower()] = stmt
209
+ elif "CREATE INDEX" in stmt.upper():
210
+ self._indexes[name.lower()] = stmt
211
+
212
+ self._loaded = True
213
+
214
+ def _parse_statements(self, sql: str) -> list[str]:
215
+ """Parse SQL file into individual statements."""
216
+ # Remove block comments
217
+ sql = re.sub(r"/\*.*?\*/", "", sql, flags=re.DOTALL)
218
+
219
+ statements = []
220
+ current = []
221
+
222
+ for line in sql.split("\n"):
223
+ # Skip line comments and empty lines
224
+ line_stripped = line.split("--")[0].strip()
225
+ if not line_stripped:
226
+ continue
227
+
228
+ current.append(line_stripped)
229
+
230
+ if line_stripped.endswith(";"):
231
+ statement = " ".join(current)
232
+ # Skip INSERT statements (version tracking)
233
+ if not statement.strip().upper().startswith("INSERT"):
234
+ statements.append(statement)
235
+ current = []
236
+
237
+ return [s.strip() for s in statements if s.strip()]
238
+
239
+ def _extract_name(self, statement: str) -> str | None:
240
+ """Extract table or index name from a CREATE statement."""
241
+ # Handle CREATE TABLE
242
+ match = re.search(
243
+ r"CREATE\s+(?:VIRTUAL\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)",
244
+ statement,
245
+ re.IGNORECASE,
246
+ )
247
+ if match:
248
+ return match.group(1)
249
+
250
+ # Handle CREATE INDEX
251
+ match = re.search(
252
+ r"CREATE\s+INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)", statement, re.IGNORECASE
253
+ )
254
+ if match:
255
+ return match.group(1)
256
+
257
+ return None
258
+
259
+ # ─────────────────────────────────────────────────────────────────────────
260
+ # Schema Access
261
+ # ─────────────────────────────────────────────────────────────────────────
262
+
263
+ @property
264
+ def schema_path(self) -> Path:
265
+ """Get the path to the schema file."""
266
+ return self._schema_path or _find_schema_path()
267
+
268
+ def get_table(self, table_name: str) -> str | None:
269
+ """
270
+ Get CREATE TABLE statement for a table.
271
+
272
+ Args:
273
+ table_name: Name of the table
274
+
275
+ Returns:
276
+ CREATE TABLE SQL statement, or None if not found
277
+ """
278
+ return self._tables.get(table_name.lower())
279
+
280
+ def get_index(self, index_name: str) -> str | None:
281
+ """
282
+ Get CREATE INDEX statement for an index.
283
+
284
+ Args:
285
+ index_name: Name of the index
286
+
287
+ Returns:
288
+ CREATE INDEX SQL statement, or None if not found
289
+ """
290
+ return self._indexes.get(index_name.lower())
291
+
292
+ def get_all_tables(self) -> dict[str, str]:
293
+ """Get all table CREATE statements."""
294
+ return dict(self._tables)
295
+
296
+ def get_all_indexes(self) -> dict[str, str]:
297
+ """Get all index CREATE statements."""
298
+ return dict(self._indexes)
299
+
300
+ def get_all_statements(self) -> list[str]:
301
+ """Get all CREATE statements."""
302
+ return list(self._statements)
303
+
304
+ def get_tables_by_layer(self, layer: str) -> list[str]:
305
+ """Get table names for a specific layer."""
306
+ return [
307
+ name
308
+ for name, layer_name in self.TABLE_LAYERS.items()
309
+ if layer_name == layer
310
+ ]
311
+
312
+ def get_layer_statements(self, layer: str) -> list[str]:
313
+ """Get all CREATE statements for a layer (tables + indexes)."""
314
+ statements = []
315
+ tables = self.get_tables_by_layer(layer)
316
+
317
+ # Add tables
318
+ for table in tables:
319
+ stmt = self.get_table(table)
320
+ if stmt:
321
+ statements.append(stmt)
322
+
323
+ # Add indexes for these tables
324
+ for _idx_name, stmt in self._indexes.items():
325
+ match = re.search(r"ON\s+(\w+)", stmt, re.IGNORECASE)
326
+ if match and match.group(1).lower() in [t.lower() for t in tables]:
327
+ statements.append(stmt)
328
+
329
+ return statements
330
+
331
+ # ─────────────────────────────────────────────────────────────────────────
332
+ # Database Initialization
333
+ # ─────────────────────────────────────────────────────────────────────────
334
+
335
+ def init_layer(self, conn: sqlite3.Connection, layer: str) -> int:
336
+ """
337
+ Initialize tables for a specific layer.
338
+
339
+ Args:
340
+ conn: SQLite connection
341
+ layer: Layer name
342
+
343
+ Returns:
344
+ Number of statements executed
345
+ """
346
+ cursor = conn.cursor()
347
+ statements = self.get_layer_statements(layer)
348
+
349
+ for stmt in statements:
350
+ cursor.execute(stmt)
351
+
352
+ conn.commit()
353
+ return len(statements)
354
+
355
+ def init_core_tables(self, conn: sqlite3.Connection) -> int:
356
+ """
357
+ Initialize Monadic Core tables (Card, Handle, Version).
358
+
359
+ This initializes both 'core' and 'handle' layers, as they constitute
360
+ the minimal functional unit of the MCard system.
361
+ """
362
+ count = self.init_layer(conn, "core")
363
+ count += self.init_layer(conn, "handle")
364
+ return count
365
+
366
+ def init_handle_tables(self, conn: sqlite3.Connection) -> int:
367
+ """Initialize handle system tables."""
368
+ return self.init_layer(conn, "handle")
369
+
370
+ def init_vector_tables(
371
+ self, conn: sqlite3.Connection, enable_fts: bool = True
372
+ ) -> int:
373
+ """Initialize vector storage tables."""
374
+ cursor = conn.cursor()
375
+ count = 0
376
+
377
+ for stmt in self.get_layer_statements("vector"):
378
+ if not enable_fts and "fts" in stmt.lower():
379
+ continue
380
+ cursor.execute(stmt)
381
+ count += 1
382
+
383
+ conn.commit()
384
+ return count
385
+
386
+ def init_semantic_tables(self, conn: sqlite3.Connection) -> int:
387
+ """Initialize semantic versioning tables."""
388
+ return self.init_layer(conn, "semantic")
389
+
390
+ def init_graph_tables(self, conn: sqlite3.Connection) -> int:
391
+ """Initialize knowledge graph tables."""
392
+ return self.init_layer(conn, "graph")
393
+
394
+ def init_all_tables(
395
+ self,
396
+ conn: sqlite3.Connection,
397
+ include_fts: bool = True,
398
+ include_graph: bool = True,
399
+ include_semantic: bool = True,
400
+ ) -> int:
401
+ """
402
+ Initialize all MCard tables on a database.
403
+
404
+ Args:
405
+ conn: SQLite connection
406
+ include_fts: Whether to include FTS tables
407
+ include_graph: Whether to include graph tables
408
+ include_semantic: Whether to include semantic versioning tables
409
+
410
+ Returns:
411
+ Number of statements executed
412
+ """
413
+ count = 0
414
+ count += self.init_core_tables(conn)
415
+ count += self.init_handle_tables(conn)
416
+ count += self.init_vector_tables(conn, enable_fts=include_fts)
417
+
418
+ if include_semantic:
419
+ count += self.init_semantic_tables(conn)
420
+
421
+ if include_graph:
422
+ count += self.init_graph_tables(conn)
423
+
424
+ return count
425
+
426
+ def init_vec0_table(self, conn: sqlite3.Connection, dimensions: int) -> None:
427
+ """
428
+ Initialize sqlite-vec virtual table.
429
+
430
+ Note: This is the ONLY dynamically generated schema because
431
+ dimensions are determined at runtime.
432
+
433
+ Args:
434
+ conn: SQLite connection
435
+ dimensions: Embedding vector dimensions
436
+ """
437
+ conn.execute(
438
+ f"""
439
+ CREATE VIRTUAL TABLE IF NOT EXISTS mcard_vec USING vec0(
440
+ metadata_id INTEGER PRIMARY KEY,
441
+ embedding float[{dimensions}]
442
+ )
443
+ """
444
+ )
445
+ conn.commit()
446
+
447
+ # ─────────────────────────────────────────────────────────────────────────
448
+ # Schema Version Management
449
+ # ─────────────────────────────────────────────────────────────────────────
450
+
451
+ def get_schema_version(self, conn: sqlite3.Connection) -> str | None:
452
+ """Get the current schema version from the database."""
453
+ try:
454
+ cursor = conn.execute(
455
+ "SELECT version FROM schema_version ORDER BY applied_at DESC LIMIT 1"
456
+ )
457
+ row = cursor.fetchone()
458
+ return row[0] if row else None
459
+ except sqlite3.OperationalError:
460
+ return None
461
+
462
+ def set_schema_version(
463
+ self, conn: sqlite3.Connection, version: str, description: str = ""
464
+ ) -> None:
465
+ """Record a schema version in the database."""
466
+ conn.execute(
467
+ """
468
+ INSERT OR REPLACE INTO schema_version (version, applied_at, description)
469
+ VALUES (?, datetime('now'), ?)
470
+ """,
471
+ (version, description),
472
+ )
473
+ conn.commit()
474
+
475
+
476
+ # ─────────────────────────────────────────────────────────────────────────────
477
+ # Convenience Functions (use singleton internally)
478
+ # ─────────────────────────────────────────────────────────────────────────────
479
+
480
+
481
+ def get_instance() -> MCardSchema:
482
+ """Get the singleton MCardSchema instance."""
483
+ return MCardSchema.get_instance()
484
+
485
+
486
+ def get_schema(table_or_index_name: str) -> str | None:
487
+ """Get CREATE statement for a table or index."""
488
+ schema = MCardSchema.get_instance()
489
+ result = schema.get_table(table_or_index_name)
490
+ if result is None:
491
+ result = schema.get_index(table_or_index_name)
492
+ return result
493
+
494
+
495
+ def get_all_statements() -> list[str]:
496
+ """Get all CREATE statements from the schema file."""
497
+ return MCardSchema.get_instance().get_all_statements()
498
+
499
+
500
+ def get_schemas_by_layer(layer: str) -> dict[str, str]:
501
+ """Get all schemas for a specific layer."""
502
+ schema = MCardSchema.get_instance()
503
+ result = {}
504
+ for table in schema.get_tables_by_layer(layer):
505
+ stmt = schema.get_table(table)
506
+ if stmt:
507
+ result[table] = stmt
508
+ return result
509
+
510
+
511
+ def get_tables_by_layer(layer: str) -> list[str]:
512
+ """Get table names for a specific layer."""
513
+ return MCardSchema.get_instance().get_tables_by_layer(layer)
514
+
515
+
516
+ def init_all_tables(
517
+ conn: sqlite3.Connection, include_fts: bool = True, include_graph: bool = True
518
+ ) -> int:
519
+ """Initialize all MCard tables."""
520
+ return MCardSchema.get_instance().init_all_tables(conn, include_fts, include_graph)
521
+
522
+
523
+ def init_core_tables(conn: sqlite3.Connection) -> int:
524
+ """Initialize core tables."""
525
+ return MCardSchema.get_instance().init_core_tables(conn)
526
+
527
+
528
+ def init_handle_tables(conn: sqlite3.Connection) -> int:
529
+ """Initialize handle tables."""
530
+ return MCardSchema.get_instance().init_handle_tables(conn)
531
+
532
+
533
+ def init_vector_tables(conn: sqlite3.Connection, enable_fts: bool = True) -> int:
534
+ """Initialize vector tables."""
535
+ return MCardSchema.get_instance().init_vector_tables(conn, enable_fts)
536
+
537
+
538
+ def init_semantic_tables(conn: sqlite3.Connection) -> int:
539
+ """Initialize semantic versioning tables."""
540
+ return MCardSchema.get_instance().init_semantic_tables(conn)
541
+
542
+
543
+ def init_graph_tables(conn: sqlite3.Connection) -> int:
544
+ """Initialize graph tables."""
545
+ return MCardSchema.get_instance().init_graph_tables(conn)
546
+
547
+
548
+ def init_vec0_table(conn: sqlite3.Connection, dimensions: int) -> None:
549
+ """Initialize sqlite-vec table."""
550
+ MCardSchema.get_instance().init_vec0_table(conn, dimensions)
551
+
552
+
553
+ def get_schema_version(conn: sqlite3.Connection) -> str | None:
554
+ """Get schema version."""
555
+ return MCardSchema.get_instance().get_schema_version(conn)
556
+
557
+
558
+ def set_schema_version(
559
+ conn: sqlite3.Connection, version: str, description: str = ""
560
+ ) -> None:
561
+ """Set schema version."""
562
+ MCardSchema.get_instance().set_schema_version(conn, version, description)
563
+
564
+
565
+ # ─────────────────────────────────────────────────────────────────────────────
566
+ # Backward Compatibility Exports
567
+ # ─────────────────────────────────────────────────────────────────────────────
568
+
569
+ # These provide lazy-loaded access to commonly used schemas for backward
570
+ # compatibility with code that imports constants like CARD_TABLE_SCHEMA.
571
+ #
572
+ # IMPORTANT: These load ONLY from the SQL file. There are NO hardcoded fallbacks.
573
+ # If the SQL file is missing, an error will be raised.
574
+
575
+ _CARD_TABLE_SCHEMA: str | None = None
576
+ _HANDLE_REGISTRY_SCHEMA: str | None = None
577
+ _HANDLE_HISTORY_SCHEMA: str | None = None
578
+ _HANDLE_INDEX_SCHEMA: str | None = None
579
+
580
+
581
+ def __getattr__(name: str):
582
+ """
583
+ Lazy attribute access for backward compatibility constants.
584
+
585
+ All schemas are loaded from schema/mcard_schema.sql.
586
+ There are NO hardcoded fallbacks - the SQL file MUST exist.
587
+ """
588
+ global _CARD_TABLE_SCHEMA, _HANDLE_REGISTRY_SCHEMA, _HANDLE_HISTORY_SCHEMA, _HANDLE_INDEX_SCHEMA
589
+
590
+ schema = MCardSchema.get_instance()
591
+
592
+ if name == "CARD_TABLE_SCHEMA":
593
+ if _CARD_TABLE_SCHEMA is None:
594
+ _CARD_TABLE_SCHEMA = schema.get_table("card")
595
+ return _CARD_TABLE_SCHEMA
596
+
597
+ elif name == "HANDLE_REGISTRY_SCHEMA":
598
+ if _HANDLE_REGISTRY_SCHEMA is None:
599
+ _HANDLE_REGISTRY_SCHEMA = schema.get_table("handle_registry")
600
+ return _HANDLE_REGISTRY_SCHEMA
601
+
602
+ elif name == "HANDLE_HISTORY_SCHEMA":
603
+ if _HANDLE_HISTORY_SCHEMA is None:
604
+ _HANDLE_HISTORY_SCHEMA = schema.get_table("handle_history")
605
+ return _HANDLE_HISTORY_SCHEMA
606
+
607
+ elif name == "HANDLE_INDEX_SCHEMA":
608
+ if _HANDLE_INDEX_SCHEMA is None:
609
+ _HANDLE_INDEX_SCHEMA = schema.get_index("idx_handle_current_hash")
610
+ return _HANDLE_INDEX_SCHEMA
611
+
612
+ elif name == "TABLE_LAYERS":
613
+ return MCardSchema.TABLE_LAYERS
614
+
615
+ raise AttributeError(f"module 'mcard.schema' has no attribute '{name}'")
616
+
617
+
618
+ if TYPE_CHECKING:
619
+ CARD_TABLE_SCHEMA: str
620
+ HANDLE_REGISTRY_SCHEMA: str
621
+ HANDLE_HISTORY_SCHEMA: str
622
+ HANDLE_INDEX_SCHEMA: str
623
+ TABLE_LAYERS: dict[str, str]
624
+
625
+ __all__ = [
626
+ # Singleton class
627
+ "MCardSchema",
628
+ "get_instance",
629
+ "_find_schema_path",
630
+ # Primary API
631
+ "get_schema",
632
+ "get_all_statements",
633
+ "get_schemas_by_layer",
634
+ "get_tables_by_layer",
635
+ # Initialization
636
+ "init_all_tables",
637
+ "init_core_tables",
638
+ "init_handle_tables",
639
+ "init_vector_tables",
640
+ "init_semantic_tables",
641
+ "init_graph_tables",
642
+ "init_vec0_table",
643
+ # Version management
644
+ "get_schema_version",
645
+ "set_schema_version",
646
+ # Backward compatibility (loaded from SQL file only)
647
+ "CARD_TABLE_SCHEMA",
648
+ "HANDLE_REGISTRY_SCHEMA",
649
+ "HANDLE_HISTORY_SCHEMA",
650
+ "HANDLE_INDEX_SCHEMA",
651
+ "TABLE_LAYERS",
652
+ ]