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,190 @@
1
+ """Layer 2 Storage Substrate (Sprint 387).
2
+
3
+ Canonical home for the Tri-Database persistence engine, connection pools,
4
+ CRUD repositories, bitemporal timelines, and hyperlink graph queries.
5
+ Relocated from Layer 0 to preserve Kenotic Microkernel Purity (INV-01, INV-09, INV-10).
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+ from clm.cross_cutting.clm_logger import get_logger
12
+ from clm.layer0.relations import _scan_cards_containing
13
+ from clm.layer0.relations import query_card_relations as _qcr
14
+ from clm.layer0.utils import clm_hash_string
15
+
16
+ from .db_bitemporal import BitemporalMixin
17
+ from .db_driver import _ENGINES, CLMDB, reset_shared_socket
18
+ from .db_hyperlinks import HyperlinkMixin
19
+ from .db_pool import ClmDbPool
20
+ from .db_repository import RepositoryBase
21
+ from .savepoint import DisposableList, savepoint
22
+ from .sqlite_wal import SqliteWalPool, TriDatabasePoolRegistry, get_tridb_pools
23
+
24
+ _logger = get_logger("clm.layer2.storage")
25
+
26
+
27
+ class CLMRepository(BitemporalMixin, HyperlinkMixin, RepositoryBase):
28
+ """DAO/Repository for the MCard three-table schema across the Sovereign Triad.
29
+
30
+ Sprint 387: Relocated to Layer 2 Storage. Assembled from RepositoryBase (CRUD),
31
+ BitemporalMixin (time-travel queries), and HyperlinkMixin (graph traversal).
32
+ Administrative CLI flows (init_tridb, memorize_mcard) are delegated to
33
+ clm.layer4.cli.tridb_admin.
34
+ """
35
+
36
+ def init_tridb(self) -> int:
37
+ """CLI flow: initialize all 3 pillar databases (delegated to Layer 4 CLI)."""
38
+ target = "clm.layer4.cli.tridb_admin"
39
+ mod = sys.modules.get(target)
40
+ if mod is None:
41
+ __import__(target)
42
+ mod = sys.modules.get(target)
43
+ return mod.init_tridb_method(self)
44
+
45
+ def memorize_mcard(self, mcard_file: str, pillar: str = "auto") -> int:
46
+ """CLI flow: memorize an MCard file (delegated to Layer 4 CLI)."""
47
+ target = "clm.layer4.cli.tridb_admin"
48
+ mod = sys.modules.get(target)
49
+ if mod is None:
50
+ __import__(target)
51
+ mod = sys.modules.get(target)
52
+ return mod.memorize_mcard_method(self, mcard_file, pillar)
53
+
54
+ def query_card_relations(self, card_hash: str, pillar: str = "execution_log") -> dict[str, list]:
55
+ """Resolve related cards via SQLite relational calculus."""
56
+ return _qcr(self, card_hash, pillar)
57
+
58
+ def _scan_cards_containing(self, pillar: str, needle: str) -> list:
59
+ """Scan all cards in a pillar and return parsed Cards whose content mentions needle."""
60
+ return _scan_cards_containing(self, pillar, needle)
61
+
62
+
63
+ def parse_yaml_mcard(mcard_content_str: str):
64
+ """Extract content_hash, g_time, source filename from MCard YAML."""
65
+ from clm.cross_cutting.mcard import parse_yaml_mcard as _impl
66
+ return _impl(mcard_content_str)
67
+
68
+
69
+ # Module-level convenience functions
70
+
71
+ def init_tridb(clm_root: str) -> int:
72
+ db = CLMDB(clm_root)
73
+ return CLMRepository(db, clm_root).init_tridb()
74
+
75
+
76
+ def memorize_mcard(clm_root: str, mcard_file: str, pillar: str = "auto") -> int:
77
+ db = CLMDB(clm_root)
78
+ return CLMRepository(db, clm_root).memorize_mcard(mcard_file, pillar)
79
+
80
+
81
+ def resolve_handle_hash(clm_root: str, handle_name: str, pillar: str = "content_memory") -> str:
82
+ db = CLMDB(clm_root)
83
+ return CLMRepository(db, clm_root).resolve_handle_hash(handle_name, pillar)
84
+
85
+
86
+ def resolve_handle_content(clm_root: str, handle_name: str, pillar: str = "content_memory") -> str:
87
+ db = CLMDB(clm_root)
88
+ return CLMRepository(db, clm_root).resolve_handle_content(handle_name, pillar)
89
+
90
+
91
+ def register_handle(clm_root: str, handle_name: str, content_source: str) -> int:
92
+ db = CLMDB(clm_root)
93
+ return CLMRepository(db, clm_root).register_handle(handle_name, content_source)
94
+
95
+
96
+ def resolve_handle_hash_at(clm_root: str, handle_name: str, query_time: str, pillar: str = "content_memory") -> str | None:
97
+ db = CLMDB(clm_root)
98
+ return CLMRepository(db, clm_root).resolve_handle_hash_at(handle_name, query_time, pillar)
99
+
100
+
101
+ def resolve_handle_content_at(clm_root: str, handle_name: str, query_time: str, pillar: str = "content_memory") -> str | None:
102
+ db = CLMDB(clm_root)
103
+ return CLMRepository(db, clm_root).resolve_handle_content_at(handle_name, query_time, pillar)
104
+
105
+
106
+ class CardCollection:
107
+ """Compatibility facade over CLMRepository providing MCard-TDD CardCollection surface.
108
+
109
+ Adheres to INV-REF-03 (G-Set Immortality): hard deletes are mathematically forbidden.
110
+ Calling delete(handle) emits a DeprecationWarning and delegates to tombstone_handle.
111
+ """
112
+
113
+ def __init__(self, repo: CLMRepository | None = None, clm_root: str = "."):
114
+ if repo is None:
115
+ db = CLMDB(clm_root)
116
+ self.repo = CLMRepository(db, clm_root)
117
+ else:
118
+ self.repo = repo
119
+
120
+ def add(self, card_or_content, pillar: str = "content_memory") -> str:
121
+ if hasattr(card_or_content, "hash") and hasattr(card_or_content, "content"):
122
+ h = str(card_or_content.hash)
123
+ c = card_or_content.content
124
+ if isinstance(c, bytes):
125
+ c = c.decode("utf-8", errors="replace")
126
+ self.repo.save_card(h, c, pillar)
127
+ return h
128
+
129
+ content_str = str(card_or_content)
130
+ h = clm_hash_string(content_str)
131
+ self.repo.save_card(h, content_str, pillar)
132
+ return h
133
+
134
+ def get(self, hash_val: str, pillar: str = "content_memory") -> str | None:
135
+ return self.repo.get_card_content(hash_val, pillar)
136
+
137
+ def delete(self, handle: str, pillar: str = "content_memory") -> bool:
138
+ import warnings
139
+
140
+ warnings.warn(
141
+ "Hard deletion violates G-Set CRDT invariants. Tombstoning handle in handle_history.",
142
+ DeprecationWarning,
143
+ stacklevel=2,
144
+ )
145
+ return self.tombstone_handle(handle, pillar)
146
+
147
+ def tombstone_handle(self, handle: str, pillar: str = "content_memory") -> bool:
148
+ return self.repo.tombstone_handle(handle, pillar)
149
+
150
+ def clear(self, pillar: str = "content_memory") -> None:
151
+ import warnings
152
+
153
+ warnings.warn(
154
+ "clear() violates G-Set CRDT invariants. Operation deprecated on CLMRepository.",
155
+ DeprecationWarning,
156
+ stacklevel=2,
157
+ )
158
+
159
+
160
+ def __getattr__(name):
161
+ if name == "_SHARED_SOCKET":
162
+ from . import db_driver
163
+ return db_driver._SHARED_SOCKET
164
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
165
+
166
+
167
+ __all__ = [
168
+ "CLMDB",
169
+ "CLMRepository",
170
+ "CardCollection",
171
+ "ClmDbPool",
172
+ "RepositoryBase",
173
+ "BitemporalMixin",
174
+ "HyperlinkMixin",
175
+ "savepoint",
176
+ "DisposableList",
177
+ "reset_shared_socket",
178
+ "init_tridb",
179
+ "memorize_mcard",
180
+ "resolve_handle_hash",
181
+ "resolve_handle_content",
182
+ "register_handle",
183
+ "resolve_handle_hash_at",
184
+ "resolve_handle_content_at",
185
+ "parse_yaml_mcard",
186
+ "_ENGINES",
187
+ "SqliteWalPool",
188
+ "TriDatabasePoolRegistry",
189
+ "get_tridb_pools",
190
+ ]
@@ -0,0 +1,141 @@
1
+ """Bitemporal handle queries (Sprint 133.1 / Sprint 387).
2
+
3
+ Extracted from ``db.py``. Time-travel queries over the handle_history table:
4
+ resolve handle content/hash at a specific point in time, list all versions,
5
+ and diff two versions.
6
+
7
+ Canonical Path: clm.layer2.storage.db_bitemporal
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import difflib
13
+
14
+ from clm.layer0.exceptions import CLMError, DatabaseError
15
+
16
+
17
+ class BitemporalMixin:
18
+ """Bitemporal query methods for handle version history.
19
+
20
+ Mixed into ``CLMRepository`` to provide time-travel queries over the
21
+ ``handle_history`` table (the η-conversion witness).
22
+ """
23
+
24
+ def get_handle_versions(self, handle_name: str, pillar: str = "content_memory") -> list[dict]:
25
+ """Returns the chronological list of all active versions of this handle."""
26
+ sql_registry = "SELECT current_hash, created_at, updated_at FROM handle_registry WHERE handle = ?;"
27
+ try:
28
+ res_reg = self.db.execute(pillar, sql_registry, [handle_name])
29
+ if not res_reg or not res_reg.strip():
30
+ return []
31
+ parts = res_reg.strip().split("|")
32
+ if len(parts) < 3:
33
+ return []
34
+ current_hash = parts[0]
35
+ created_at = parts[1]
36
+ updated_at = parts[2]
37
+ except Exception as e:
38
+ raise DatabaseError(f"Failed to query registry for {handle_name}: {e}") from e
39
+
40
+ sql_history = "SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY changed_at ASC;"
41
+ try:
42
+ res_hist = self.db.execute(pillar, sql_history, [handle_name])
43
+ hist_lines = [line.strip() for line in res_hist.strip().split("\n") if line.strip()] if res_hist else []
44
+ except Exception as e:
45
+ raise DatabaseError(f"Failed to query history for {handle_name}: {e}") from e
46
+
47
+ versions = []
48
+
49
+ if not hist_lines:
50
+ versions.append({
51
+ "version_index": 0,
52
+ "hash": current_hash,
53
+ "active_from": created_at,
54
+ "active_to": None
55
+ })
56
+ return versions
57
+
58
+ last_time = created_at
59
+ idx = 0
60
+ for line in hist_lines:
61
+ h_parts = line.split("|")
62
+ if len(h_parts) >= 2:
63
+ prev_hash = h_parts[0]
64
+ changed_at = h_parts[1]
65
+ versions.append({
66
+ "version_index": idx,
67
+ "hash": prev_hash,
68
+ "active_from": last_time,
69
+ "active_to": changed_at
70
+ })
71
+ last_time = changed_at
72
+ idx += 1
73
+
74
+ versions.append({
75
+ "version_index": idx,
76
+ "hash": current_hash,
77
+ "active_from": updated_at,
78
+ "active_to": None
79
+ })
80
+
81
+ return versions
82
+
83
+ def resolve_handle_hash_at(self, handle_name: str, query_time: str, pillar: str = "content_memory") -> str | None:
84
+ """Resolves the hash of a handle as it existed at a specific ISO-8601 query_time."""
85
+ versions = self.get_handle_versions(handle_name, pillar)
86
+ if not versions:
87
+ return None
88
+
89
+ q_time = query_time.strip()
90
+ for ver in versions:
91
+ active_from = ver["active_from"].strip()
92
+ active_to = ver["active_to"]
93
+ if q_time >= active_from and (active_to is None or q_time < active_to.strip()):
94
+ return ver["hash"]
95
+ return None
96
+
97
+ def resolve_handle_content_at(self, handle_name: str, query_time: str, pillar: str = "content_memory") -> str | None:
98
+ """Resolves the content of a handle as it existed at a specific ISO-8601 query_time."""
99
+ hash_val = self.resolve_handle_hash_at(handle_name, query_time, pillar)
100
+ if not hash_val:
101
+ return None
102
+
103
+ sql = "SELECT content FROM card WHERE hash = ?;"
104
+ try:
105
+ res = self.db.execute(pillar, sql, [hash_val])
106
+ return res.strip() if res else None
107
+ except Exception as e:
108
+ raise DatabaseError(f"Failed to resolve content for hash {hash_val} in {pillar}: {e}") from e
109
+
110
+ def diff_handle_versions(self, handle_name: str, version_idx1: int, version_idx2: int, pillar: str = "content_memory") -> str:
111
+ """Compares two versions of a handle's content by chronological indices and returns a diff string."""
112
+ versions = self.get_handle_versions(handle_name, pillar)
113
+ if not versions:
114
+ raise CLMError(f"No versions found for handle '{handle_name}'")
115
+
116
+ v1_hash, v2_hash = None, None
117
+ for ver in versions:
118
+ if ver["version_index"] == version_idx1:
119
+ v1_hash = ver["hash"]
120
+ if ver["version_index"] == version_idx2:
121
+ v2_hash = ver["hash"]
122
+
123
+ if not v1_hash:
124
+ raise CLMError(f"Version index {version_idx1} not found for handle '{handle_name}'")
125
+ if not v2_hash:
126
+ raise CLMError(f"Version index {version_idx2} not found for handle '{handle_name}'")
127
+
128
+ sql = "SELECT content FROM card WHERE hash = ?;"
129
+ c1 = self.db.execute(pillar, sql, [v1_hash])
130
+ c2 = self.db.execute(pillar, sql, [v2_hash])
131
+
132
+ content1 = c1.strip() if c1 else ""
133
+ content2 = c2.strip() if c2 else ""
134
+
135
+ diff = difflib.unified_diff(
136
+ content1.splitlines(keepends=True),
137
+ content2.splitlines(keepends=True),
138
+ fromfile=f"{handle_name} (v{version_idx1}: {v1_hash[:8]})",
139
+ tofile=f"{handle_name} (v{version_idx2}: {v2_hash[:8]})"
140
+ )
141
+ return "".join(diff)
@@ -0,0 +1,331 @@
1
+ """CLMDB — Low-level database driver for the Sovereign Triad (Sprint 133.1 / Sprint 387).
2
+
3
+ Provides unified SQL execution across the three pillar databases
4
+ (agent_identities, content_memory, execution_log). Supports file-based
5
+ SQLite (via SQLAlchemy / ClmDbPool) or in-memory broker mode
6
+ (CLM_ZERO_FS=true) communicating via TCP socket to clm_memdb_broker.py.
7
+
8
+ Canonical Path: clm.layer2.storage.db_driver
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import contextlib
14
+ import json
15
+ import os
16
+ import socket
17
+ import sqlite3
18
+ import subprocess
19
+ import sys
20
+ import time
21
+ from typing import Any
22
+
23
+ from sqlalchemy import create_engine, text
24
+ from sqlalchemy.pool import NullPool
25
+
26
+ from clm.cross_cutting.clm_logger import get_logger
27
+ from clm.cross_cutting.resources import get_schema_content
28
+
29
+ _logger = get_logger("clm.db")
30
+
31
+ _SHARED_SOCKET = None
32
+ _ENGINES: dict[str, Any] = {}
33
+
34
+
35
+ def reset_shared_socket() -> None:
36
+ """Reset the cached shared broker socket (used by tests between runs)."""
37
+ global _SHARED_SOCKET
38
+ _SHARED_SOCKET = None
39
+
40
+
41
+ class CLMDB:
42
+ """Low-level database driver for the Sovereign Triad.
43
+
44
+ Provides unified SQL execution across the three pillar databases
45
+ (agent_identities, content_memory, execution_log). Supports two
46
+ modes: file-based SQLite (via SQLAlchemy) for persistent operation,
47
+ or in-memory broker mode (CLM_ZERO_FS=true) communicating via TCP
48
+ socket to clm_memdb_broker.py.
49
+
50
+ Each pillar database contains the same three-table MCard Schema,
51
+ implementing the Three Angles of data identity:
52
+ card -> Content addressing (beta / Exponent / CLM C-axis)
53
+ handle_registry -> Semantic naming (alpha / Sum / CLM A-axis)
54
+ handle_history -> Temporal lineage (eta / Product / CLM B-axis)
55
+ """
56
+
57
+ def __init__(self, clm_root: str | None = None) -> None:
58
+ self.clm_root = clm_root or os.environ.get("CLM_ROOT") or os.getcwd()
59
+ self.zero_fs = os.environ.get("CLM_ZERO_FS", "").lower() == "true"
60
+ self.port = int(os.environ.get("CLM_MEMBROKER_PORT", "43740"))
61
+ self.db_dir = os.path.join(self.clm_root, "db")
62
+
63
+ def _interpolate_sql(self, sql: str, params: Any) -> str:
64
+ if not params:
65
+ return sql
66
+ idx = 0
67
+ result = []
68
+ in_str = False
69
+ str_char = None
70
+ i = 0
71
+ n = len(sql)
72
+ while i < n:
73
+ c = sql[i]
74
+ if c in ("'", '"'):
75
+ if not in_str:
76
+ in_str = True
77
+ str_char = c
78
+ elif str_char == c:
79
+ if i + 1 < n and sql[i + 1] == c:
80
+ result.append(c * 2)
81
+ i += 2
82
+ continue
83
+ else:
84
+ in_str = False
85
+ result.append(c)
86
+ elif c == "?" and not in_str:
87
+ if idx >= len(params):
88
+ raise ValueError("Too few parameters for SQL query")
89
+ val = params[idx]
90
+ idx += 1
91
+ if val is None:
92
+ result.append("NULL")
93
+ elif isinstance(val, int | float):
94
+ result.append(str(val))
95
+ elif isinstance(val, bytes):
96
+ result.append(f"X'{val.hex()}'")
97
+ else:
98
+ escaped = str(val).replace("'", "''")
99
+ result.append(f"'{escaped}'")
100
+ else:
101
+ result.append(c)
102
+ i += 1
103
+ if idx < len(params):
104
+ raise ValueError("Too many parameters for SQL query")
105
+ return "".join(result)
106
+
107
+ def _send_to_broker(self, pillar: str, sql: str) -> str:
108
+ global _SHARED_SOCKET
109
+ sql_bytes = sql.encode("utf-8")
110
+ req_header = f"CLMQ\t{pillar}\t{len(sql_bytes)}\t\n".encode()
111
+
112
+ for attempt in range(2):
113
+ s = _SHARED_SOCKET
114
+ if s is None:
115
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116
+ s.settimeout(10.0)
117
+ try:
118
+ s.connect(("127.0.0.1", self.port))
119
+ _SHARED_SOCKET = s
120
+ except ConnectionRefusedError:
121
+ is_default_port = self.port == 43740
122
+ is_externally_managed = "CLM_MEMBROKER_NO_DISK" in os.environ
123
+ if self.zero_fs and is_default_port and not is_externally_managed:
124
+ broker_script = os.path.join(self.clm_root, "scripts", "bootstrap", "layer0_core", "clm_memdb_broker.py")
125
+ if not os.path.exists(broker_script):
126
+ broker_script = os.path.join(self.clm_root, "scripts", "layer0_microkernel", "db_core", "clm_memdb_broker.py")
127
+ if os.path.exists(broker_script):
128
+ subprocess.Popen([sys.executable, broker_script], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
129
+
130
+ retries = 0
131
+ connected = False
132
+ while retries < 30:
133
+ try:
134
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
135
+ s.settimeout(10.0)
136
+ s.connect(("127.0.0.1", self.port))
137
+ connected = True
138
+ _SHARED_SOCKET = s
139
+ break
140
+ except Exception:
141
+ time.sleep(0.1)
142
+ retries += 1
143
+ if not connected:
144
+ raise Exception("Failed to lazily start clm_memdb_broker.py") from None
145
+
146
+ try:
147
+ schema_sql = get_schema_content("mcard_schema.sql")
148
+ except Exception:
149
+ schema_sql = ""
150
+ if schema_sql:
151
+ for p in ("agent_identities", "content_memory", "execution_log"):
152
+ s_schema = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
153
+ s_schema.settimeout(10.0)
154
+ s_schema.connect(("127.0.0.1", self.port))
155
+ s_bytes = schema_sql.encode("utf-8")
156
+ r_header = f"CLMQ\t{p}\t{len(s_bytes)}\t\n".encode()
157
+ s_schema.sendall(r_header + s_bytes)
158
+ resp_header = b""
159
+ while b"\n" not in resp_header:
160
+ chunk = s_schema.recv(1)
161
+ if not chunk:
162
+ break
163
+ resp_header += chunk
164
+ if resp_header:
165
+ parts = resp_header.decode("utf-8").strip().split("\t")
166
+ if len(parts) >= 3:
167
+ rlen = int(parts[2])
168
+ body = b""
169
+ while len(body) < rlen:
170
+ chunk = s_schema.recv(min(4096, rlen - len(body)))
171
+ if not chunk:
172
+ break
173
+ body += chunk
174
+ s_schema.close()
175
+ else:
176
+ raise Exception(f"Broker script not found: {broker_script}") from None
177
+ else:
178
+ raise
179
+
180
+ try:
181
+ s.sendall(req_header + sql_bytes)
182
+
183
+ header_data = b""
184
+ while b"\n" not in header_data:
185
+ chunk = s.recv(1)
186
+ if not chunk:
187
+ break
188
+ header_data += chunk
189
+
190
+ if not header_data:
191
+ raise Exception("Connection closed without response")
192
+
193
+ header_line = header_data.decode("utf-8").strip()
194
+ parts = header_line.split("\t")
195
+ if len(parts) < 3 or parts[0] != "CLMR":
196
+ raise Exception(f"Invalid response header: {header_line}")
197
+
198
+ status, rlen_str = parts[1], parts[2]
199
+ rlen = int(rlen_str)
200
+
201
+ body_bytes = b""
202
+ while len(body_bytes) < rlen:
203
+ chunk = s.recv(min(4096, rlen - len(body_bytes)))
204
+ if not chunk:
205
+ break
206
+ body_bytes += chunk
207
+
208
+ if status == "ERR":
209
+ raise Exception(body_bytes.decode("utf-8"))
210
+
211
+ return body_bytes.decode("utf-8")
212
+
213
+ except Exception as e:
214
+ with contextlib.suppress(Exception):
215
+ s.close()
216
+ _SHARED_SOCKET = None
217
+
218
+ if attempt == 1:
219
+ raise e
220
+ return ""
221
+
222
+ def execute(self, pillar: str, sql: str, params: Any = None, as_json: bool = False) -> str:
223
+ if pillar in ("execution_logs", "exec"):
224
+ pillar = "execution_log"
225
+ elif pillar in ("agent_identity", "id"):
226
+ pillar = "agent_identities"
227
+ elif pillar in ("content_memory", "mem"):
228
+ pillar = "content_memory"
229
+
230
+ if self.zero_fs:
231
+ interpolated_sql = self._interpolate_sql(sql, params)
232
+ resp = self._send_to_broker(pillar, interpolated_sql)
233
+ if as_json:
234
+ snap_dir = os.environ.get("CLM_MEMBROKER_SNAP_DIR", "/tmp")
235
+ db_path = os.path.join(snap_dir, f"{pillar}.db")
236
+ columns = []
237
+ if os.path.exists(db_path):
238
+ try:
239
+ conn = sqlite3.connect(db_path)
240
+ cursor = conn.cursor()
241
+ if "select" in sql.lower():
242
+ cursor.execute(sql, params or [])
243
+ columns = [col[0] for col in cursor.description] if cursor.description else []
244
+ cursor.close()
245
+ conn.close()
246
+ except Exception:
247
+ pass
248
+
249
+ rows = []
250
+ if resp.strip():
251
+ for line in resp.strip().split("\n"):
252
+ if not line:
253
+ continue
254
+ vals = line.split("|")
255
+ if columns and len(vals) == len(columns):
256
+ rows.append(dict(zip(columns, vals, strict=False)))
257
+ else:
258
+ rows.append({f"col_{i}": val for i, val in enumerate(vals)})
259
+ return json.dumps(rows)
260
+ else:
261
+ return resp
262
+ else:
263
+ db_path = os.path.join(self.db_dir, f"{pillar}.db")
264
+ engine = _ENGINES.get(db_path)
265
+ if not engine:
266
+ engine = create_engine(f"sqlite:///{db_path}", poolclass=NullPool)
267
+ _ENGINES[db_path] = engine
268
+
269
+ # Ensure schema exists
270
+ with engine.begin() as conn:
271
+ res = conn.execute(text("SELECT 1 FROM sqlite_master WHERE type='table' AND name='card';")).fetchone()
272
+ if not res:
273
+ try:
274
+ schema_content = get_schema_content("mcard_schema.sql")
275
+ except Exception:
276
+ schema_content = ""
277
+ if schema_content:
278
+ dbapi_conn = conn.connection
279
+ if hasattr(dbapi_conn, "executescript"):
280
+ dbapi_conn.executescript(schema_content)
281
+ elif hasattr(dbapi_conn, "dbapi_connection") and hasattr(dbapi_conn.dbapi_connection, "executescript"):
282
+ dbapi_conn.dbapi_connection.executescript(schema_content)
283
+ else:
284
+ temp_conn = sqlite3.connect(db_path)
285
+ temp_conn.executescript(schema_content)
286
+ temp_conn.commit()
287
+ temp_conn.close()
288
+
289
+ with engine.begin() as conn:
290
+ if params:
291
+ if isinstance(params, dict):
292
+ result = conn.exec_driver_sql(sql, params)
293
+ else:
294
+ result = conn.exec_driver_sql(sql, tuple(params))
295
+ else:
296
+ if "BEGIN;" in sql or ";" in sql[:-1]:
297
+ dbapi_conn = conn.connection
298
+ if hasattr(dbapi_conn, "executescript"):
299
+ dbapi_conn.executescript(sql)
300
+ elif hasattr(dbapi_conn, "dbapi_connection") and hasattr(dbapi_conn.dbapi_connection, "executescript"):
301
+ dbapi_conn.dbapi_connection.executescript(sql)
302
+ else:
303
+ temp_conn = sqlite3.connect(db_path)
304
+ temp_conn.executescript(sql)
305
+ temp_conn.commit()
306
+ temp_conn.close()
307
+ result = None
308
+ else:
309
+ result = conn.execute(text(sql))
310
+
311
+ rows = []
312
+ columns = []
313
+ if result is not None:
314
+ if result.returns_rows:
315
+ rows = result.fetchall()
316
+ columns = list(result.keys())
317
+ else:
318
+ pass
319
+
320
+ if as_json:
321
+ res_list = []
322
+ for row in rows:
323
+ res_list.append(dict(zip(columns, row, strict=False)))
324
+ result_str = json.dumps(res_list)
325
+ else:
326
+ output = []
327
+ for row in rows:
328
+ output.append("|".join("" if val is None else str(val) for val in row))
329
+ result_str = "\n".join(output) + "\n" if output else ""
330
+
331
+ return result_str