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,125 @@
1
+ """
2
+ mcard/ontology/collection.py
3
+ MCardCollection Resolver Prototype
4
+ Implements unified management and resolution across all three MCard morphologies:
5
+ - DirectoryMCard
6
+ - FileMCard
7
+ - UriMCard
8
+ """
9
+
10
+ import hashlib
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ import yaml
17
+
18
+
19
+ class MorphologyType(str, Enum):
20
+ DIRECTORY = "directory"
21
+ FILE = "file"
22
+ URI = "uri"
23
+
24
+
25
+ @dataclass
26
+ class DirectoryMCard:
27
+ path: Path
28
+ role: str
29
+ manifest: dict[str, Any]
30
+ mcard_morphology: str = "directory"
31
+ tree_hash: str = ""
32
+
33
+ def __post_init__(self):
34
+ if not self.tree_hash:
35
+ self.tree_hash = self._compute_tree_hash()
36
+
37
+ def _compute_tree_hash(self) -> str:
38
+ # Canonical hash of manifest content
39
+ manifest_str = yaml.dump(self.manifest, sort_keys=True)
40
+ return hashlib.sha256(manifest_str.encode("utf-8")).hexdigest()
41
+
42
+
43
+ @dataclass
44
+ class FileMCard:
45
+ hash: str
46
+ content: str | dict[str, Any]
47
+ mime_type: str
48
+ g_time: int
49
+ mcard_morphology: str = "file"
50
+ tags: list[str] = field(default_factory=list)
51
+ handles: list[str] = field(default_factory=list)
52
+
53
+
54
+ @dataclass
55
+ class UriMCard:
56
+ uri: str
57
+ protocol: str
58
+ mcard_morphology: str = "uri"
59
+ digest: str = ""
60
+ entrypoint: str = ""
61
+ metadata: dict[str, Any] = field(default_factory=dict)
62
+
63
+
64
+ class MCardCollection:
65
+ """Unified collection and resolver for all three MCard morphologies."""
66
+
67
+ def __init__(self):
68
+ self._entries: dict[str, DirectoryMCard | FileMCard | UriMCard] = {}
69
+
70
+ def register_directory(self, path: str | Path) -> DirectoryMCard:
71
+ dir_path = Path(path).expanduser().resolve()
72
+ manifest_file = dir_path / "mcard.yaml"
73
+ if not manifest_file.exists():
74
+ raise FileNotFoundError(f"Missing mcard.yaml in directory: {dir_path}")
75
+
76
+ with open(manifest_file, encoding="utf-8") as f:
77
+ manifest = yaml.safe_load(f)
78
+
79
+ role = manifest.get("role", "developmental-subsystem")
80
+ dm = DirectoryMCard(path=dir_path, role=role, manifest=manifest)
81
+ self._entries[f"dir:{dir_path.name}"] = dm
82
+ return dm
83
+
84
+ def register_file(
85
+ self,
86
+ content: str | dict[str, Any],
87
+ mime_type: str = "text/plain",
88
+ g_time: int = 1,
89
+ ) -> FileMCard:
90
+ if isinstance(content, dict):
91
+ raw_bytes = yaml.dump(content, sort_keys=True).encode("utf-8")
92
+ else:
93
+ raw_bytes = content.encode("utf-8")
94
+
95
+ file_hash = hashlib.sha256(raw_bytes).hexdigest()
96
+ fm = FileMCard(
97
+ hash=file_hash, content=content, mime_type=mime_type, g_time=g_time
98
+ )
99
+ self._entries[f"file:{file_hash}"] = fm
100
+ return fm
101
+
102
+ def register_uri(
103
+ self, uri: str, protocol: str = "", entrypoint: str = ""
104
+ ) -> UriMCard:
105
+ if not protocol:
106
+ protocol = uri.split("://")[0] if "://" in uri else "uri"
107
+
108
+ um = UriMCard(uri=uri, protocol=protocol, entrypoint=entrypoint)
109
+ self._entries[f"uri:{uri}"] = um
110
+ return um
111
+
112
+ def get(self, key: str) -> DirectoryMCard | FileMCard | UriMCard | None:
113
+ return self._entries.get(key)
114
+
115
+ def list_by_morphology(
116
+ self, morphology: str
117
+ ) -> list[DirectoryMCard | FileMCard | UriMCard]:
118
+ return [
119
+ item
120
+ for item in self._entries.values()
121
+ if item.mcard_morphology == morphology
122
+ ]
123
+
124
+ def count(self) -> int:
125
+ return len(self._entries)
clm/layer0/parser.py ADDED
@@ -0,0 +1,429 @@
1
+ """Unified Two-Pass CLM Parser (DV-374-01).
2
+
3
+ Implements the unified CLM interpretation pipeline:
4
+ - Pass 1 (Structural - ⊤): Path reference interception, CAS file wrapping into MCards,
5
+ environment variable substitution, and dimension extraction. Fully subsumes legacy ClmRoot.
6
+ - Pass 2 (Semantic - Lattice Descent): Triadic normalization ({abstract, concrete, balanced, meta}),
7
+ compilation of CEL pre/postcondition invariants into VCard meet gates, and compilation of
8
+ Baldwin modularity operators into Coloured Petri Net (CPN) subgraphs.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import re
15
+ from dataclasses import dataclass, field
16
+ from pathlib import Path
17
+ from typing import Any, Callable
18
+
19
+ import yaml
20
+
21
+ from clm.layer0.cel_eval import CelEvaluator
22
+ from clm.layer0.model.card import MCard
23
+ from clm.layer0.model.dictionary import ClmReservedWords, DataSemanticType
24
+ from clm.layer0.model.pcard import PCard
25
+ from clm.layer0.model.vcard import VCard
26
+
27
+ _ENV_VAR_PATTERN = re.compile(r"\$\{([A-Za-z0-9_]+)(?::-([^}]*))?\}|\$([A-Za-z0-9_]+)")
28
+
29
+ # Compiler hook for Baldwin Modularity Operators (Dependency Inversion: Layer 1 registers compiler)
30
+ BaldwinCompilerFn = Callable[[list[dict[str, Any]]], dict[str, Any]]
31
+ _baldwin_compiler: BaldwinCompilerFn | None = None
32
+
33
+
34
+ def register_baldwin_compiler(compiler: BaldwinCompilerFn) -> None:
35
+ """Register a Baldwin CPN compiler from Layer 1."""
36
+ global _baldwin_compiler
37
+ _baldwin_compiler = compiler
38
+
39
+
40
+ def substitute_env_vars(text: str, env: dict[str, str] | None = None) -> str:
41
+ """Substitutes ${VAR:-default} and $VAR occurrences from env or os.environ."""
42
+ source_env = os.environ if env is None else env
43
+
44
+ def _replace(match: re.Match) -> str:
45
+ var_name = match.group(1) or match.group(3)
46
+ default_val = match.group(2) if match.group(2) is not None else ""
47
+ return source_env.get(var_name, default_val)
48
+
49
+ return _ENV_VAR_PATTERN.sub(_replace, text)
50
+
51
+
52
+ @dataclass
53
+ class ParsedClmAst:
54
+ """Canonical representation of a parsed CLM specification."""
55
+
56
+ raw: dict[str, Any]
57
+ abstract: dict[str, Any]
58
+ concrete: dict[str, Any]
59
+ balanced: dict[str, Any]
60
+ meta: dict[str, Any]
61
+ preconditions: list[str] = field(default_factory=list)
62
+ postconditions: list[str] = field(default_factory=list)
63
+ cel_evaluators: dict[str, CelEvaluator] = field(default_factory=dict)
64
+ baldwin_nets: dict[str, Any] = field(default_factory=dict)
65
+ cards: tuple[MCard, MCard, PCard, VCard, list[MCard]] | None = None
66
+ chapter: dict[str, Any] | None = None
67
+ chapter_name: str | None = None
68
+ chapter_number: int | None = None
69
+ narrative: str | None = None
70
+
71
+ def to_dict(self) -> dict[str, Any]:
72
+ """Serializes the AST into a structural dictionary for AST parity validation."""
73
+ data = {
74
+ "abstract": self.abstract,
75
+ "concrete": self.concrete,
76
+ "balanced": self.balanced,
77
+ "meta": self.meta,
78
+ "preconditions": self.preconditions,
79
+ "postconditions": self.postconditions,
80
+ "baldwin_operator_count": len(self.baldwin_nets),
81
+ }
82
+ if self.chapter_name is not None:
83
+ data["chapter_name"] = self.chapter_name
84
+ if self.chapter_number is not None:
85
+ data["chapter_number"] = self.chapter_number
86
+ if self.narrative is not None:
87
+ data["narrative"] = self.narrative
88
+ return data
89
+
90
+
91
+ class UnifiedClmParser:
92
+ """Unified Two-Pass CLM Parser subsuming legacy ClmRoot and implementing multi-pass semantic descent."""
93
+
94
+ # ── Pass 1: Structural Resolution & Path Interception ───────────────────
95
+
96
+ @classmethod
97
+ def extract_dimension(cls, val: Any, keys: list[str]) -> Any:
98
+ """Recursively searches through a YAML dict tree to find dimension keys."""
99
+ if isinstance(val, dict):
100
+ for key_str in keys:
101
+ if key_str in val:
102
+ return val[key_str]
103
+ for child_val in val.values():
104
+ found = cls.extract_dimension(child_val, keys)
105
+ if found is not None:
106
+ return found
107
+ return None
108
+
109
+ @classmethod
110
+ def traverse_and_intercept(
111
+ cls,
112
+ value: Any,
113
+ dictionary: ClmReservedWords | None,
114
+ g_time: str,
115
+ extra_cards: list[MCard],
116
+ clm_dir: Path | None = None,
117
+ ) -> None:
118
+ """Mutably traverses the YAML tree, wrapping physical files into CAS MCards and replacing paths with content hashes."""
119
+ if isinstance(value, dict):
120
+ changes = {}
121
+ for k, v in value.items():
122
+ is_path_ref = False
123
+ if dictionary and isinstance(k, str):
124
+ semantic_type = dictionary.lookup(k)
125
+ if semantic_type == DataSemanticType.PathReference:
126
+ is_path_ref = True
127
+ elif isinstance(k, str) and k in {
128
+ "file",
129
+ "file_path",
130
+ "path",
131
+ "source_path",
132
+ "script_path",
133
+ "content_path",
134
+ }:
135
+ is_path_ref = True
136
+
137
+ if is_path_ref and isinstance(v, str):
138
+ target_path = (clm_dir / v) if clm_dir else Path(v)
139
+ if target_path.exists() and target_path.is_file():
140
+ try:
141
+ bytes_content = target_path.read_bytes()
142
+ new_card = MCard(content=bytes_content)
143
+ changes[k] = str(new_card.hash)
144
+ extra_cards.append(new_card)
145
+ except Exception:
146
+ pass
147
+
148
+ cls.traverse_and_intercept(v, dictionary, g_time, extra_cards, clm_dir)
149
+
150
+ for k_str, new_val in changes.items():
151
+ value[k_str] = new_val
152
+
153
+ elif isinstance(value, list):
154
+ for element in value:
155
+ cls.traverse_and_intercept(
156
+ element, dictionary, g_time, extra_cards, clm_dir
157
+ )
158
+
159
+ @classmethod
160
+ def map_to_cards(
161
+ cls,
162
+ yaml_bytes: bytes,
163
+ g_time: str,
164
+ dictionary: ClmReservedWords | None = None,
165
+ clm_dir: Path | None = None,
166
+ ) -> tuple[MCard, MCard, PCard, VCard, list[MCard]]:
167
+ """Maps raw YAML bytes into Card topology (root, abstract, concrete, balanced, extra)."""
168
+ yaml_str = yaml_bytes.decode("utf-8")
169
+ extra_cards: list[MCard] = []
170
+
171
+ root_mcard = MCard(content=yaml_bytes)
172
+ parsed = yaml.safe_load(yaml_str) or {}
173
+
174
+ if dictionary or clm_dir:
175
+ cls.traverse_and_intercept(parsed, dictionary, g_time, extra_cards, clm_dir)
176
+
177
+ # 2. Abstract Section -> MCard
178
+ abstract_dim = cls.extract_dimension(parsed, ["abstract", "abstract_spec", "A"])
179
+ abstract_bytes = (
180
+ yaml.safe_dump(abstract_dim).encode("utf-8")
181
+ if abstract_dim is not None
182
+ else b"null\n"
183
+ )
184
+ abstract_mcard = MCard(content=abstract_bytes)
185
+
186
+ # 3. Concrete Section -> PCard
187
+ concrete_dim = cls.extract_dimension(parsed, ["concrete", "concrete_impl", "C"])
188
+ concrete_bytes = (
189
+ yaml.safe_dump(concrete_dim).encode("utf-8")
190
+ if concrete_dim is not None
191
+ else b"{}\n"
192
+ )
193
+ pcard = PCard(concrete_bytes)
194
+
195
+ # 4. Balanced Section -> VCard
196
+ balanced_dim = cls.extract_dimension(parsed, ["balanced", "balanced_exp", "B"])
197
+ balanced_bytes = (
198
+ yaml.safe_dump(balanced_dim).encode("utf-8")
199
+ if balanced_dim is not None
200
+ else b"{}\n"
201
+ )
202
+ vcard = VCard(balanced_bytes)
203
+
204
+ return (root_mcard, abstract_mcard, pcard, vcard, extra_cards)
205
+
206
+ # ── Pass 2: Semantic Analysis & Lattice Descent ─────────────────────────
207
+
208
+ @classmethod
209
+ def parse(
210
+ cls,
211
+ input_data: str | bytes | dict[str, Any],
212
+ clm_dir: Path | None = None,
213
+ env: dict[str, str] | None = None,
214
+ g_time: str = "1970-01-01T00:00:00Z",
215
+ dictionary: ClmReservedWords | None = None,
216
+ ) -> ParsedClmAst:
217
+ """Full Two-Pass CLM Parse: Structural Ingestion + Semantic Descent."""
218
+ # Normalize input to string and raw dict
219
+ if isinstance(input_data, bytes):
220
+ raw_text = input_data.decode("utf-8")
221
+ raw_text = substitute_env_vars(raw_text, env)
222
+ raw_dict = yaml.safe_load(raw_text) or {}
223
+ source_bytes = input_data
224
+ elif isinstance(input_data, str):
225
+ raw_text = substitute_env_vars(input_data, env)
226
+ raw_dict = yaml.safe_load(raw_text) or {}
227
+ source_bytes = raw_text.encode("utf-8")
228
+ elif isinstance(input_data, dict):
229
+ raw_dict = input_data
230
+ source_bytes = yaml.safe_dump(raw_dict).encode("utf-8")
231
+ else:
232
+ raise TypeError(f"Unsupported input type for CLM parser: {type(input_data)}")
233
+
234
+ # Pass 1: Card mapping & path interception
235
+ cards = cls.map_to_cards(
236
+ yaml_bytes=source_bytes,
237
+ g_time=g_time,
238
+ dictionary=dictionary,
239
+ clm_dir=clm_dir,
240
+ )
241
+
242
+ # Pass 2: Triadic Normalization & Dialect Detection
243
+ clm_root = raw_dict.get("clm", raw_dict) if isinstance(raw_dict, dict) else {}
244
+ if not isinstance(clm_root, dict):
245
+ clm_root = {}
246
+
247
+ abstract = (
248
+ clm_root.get("abstract")
249
+ or clm_root.get("abstract_spec")
250
+ or clm_root.get("A")
251
+ or {}
252
+ )
253
+ concrete = (
254
+ clm_root.get("concrete")
255
+ or clm_root.get("concrete_impl")
256
+ or clm_root.get("C")
257
+ or {}
258
+ )
259
+ balanced = (
260
+ clm_root.get("balanced")
261
+ or clm_root.get("balanced_exp")
262
+ or clm_root.get("B")
263
+ or {}
264
+ )
265
+ meta = (
266
+ clm_root.get("_meta")
267
+ or clm_root.get("meta")
268
+ or raw_dict.get("_meta")
269
+ or raw_dict.get("meta")
270
+ or {}
271
+ )
272
+
273
+ # Dialect B Synthesis: Formal CPN Net Specification
274
+ places = raw_dict.get("places") or clm_root.get("places")
275
+ transitions = raw_dict.get("transitions") or clm_root.get("transitions")
276
+ if places or transitions:
277
+ if not abstract:
278
+ abstract = {"places": places or [], "transitions": transitions or []}
279
+ if not concrete:
280
+ concrete = {"transitions": transitions or [], "arrows": raw_dict.get("arrows", [])}
281
+ if not balanced:
282
+ balanced = {"guards": raw_dict.get("guards", []), "invariants": raw_dict.get("invariants", [])}
283
+
284
+ # Chapter & Narrative Metadata Extraction
285
+ chapter_data = raw_dict.get("chapter") or clm_root.get("chapter")
286
+ chapter_dict: dict[str, Any] | None = chapter_data if isinstance(chapter_data, dict) else None
287
+ chapter_name: str | None = None
288
+ chapter_number: int | None = None
289
+ narrative: str | None = None
290
+
291
+ if chapter_dict:
292
+ chapter_name = chapter_dict.get("title") or chapter_dict.get("name")
293
+ num_val = chapter_dict.get("id") or chapter_dict.get("number")
294
+ if num_val is not None:
295
+ try:
296
+ chapter_number = int(num_val)
297
+ except (ValueError, TypeError):
298
+ chapter_number = None
299
+ narrative = chapter_dict.get("narrative") or chapter_dict.get("description")
300
+ else:
301
+ narrative = raw_dict.get("narrative") or clm_root.get("narrative")
302
+
303
+ # Precondition / Postcondition Extraction
304
+ preconditions: list[str] = []
305
+ postconditions: list[str] = []
306
+
307
+ if isinstance(concrete, dict):
308
+ pre = concrete.get("precondition") or concrete.get("vcard_pre")
309
+ if isinstance(pre, str):
310
+ preconditions.append(pre)
311
+ elif isinstance(pre, dict):
312
+ vpre = pre.get("vcard_pre") or pre.get("expr")
313
+ if isinstance(vpre, str):
314
+ preconditions.append(vpre)
315
+
316
+ post = concrete.get("postcondition") or concrete.get("vcard_post")
317
+ if isinstance(post, str):
318
+ postconditions.append(post)
319
+ elif isinstance(post, dict):
320
+ vpost = post.get("vcard_post") or post.get("expr")
321
+ if isinstance(vpost, str):
322
+ postconditions.append(vpost)
323
+
324
+ # Compile CEL Invariant Gates
325
+ cel_evaluators: dict[str, CelEvaluator] = {}
326
+ for idx, cond in enumerate(preconditions):
327
+ cel_evaluators[f"pre_{idx}"] = CelEvaluator(cond)
328
+ for idx, cond in enumerate(postconditions):
329
+ cel_evaluators[f"post_{idx}"] = CelEvaluator(cond)
330
+
331
+ # Compile Baldwin Modularity Operators (Dependency Inversion for INV-383-02)
332
+ baldwin_nets: dict[str, Any] = {}
333
+ operators = (
334
+ raw_dict.get("operators")
335
+ or clm_root.get("operators")
336
+ or (concrete.get("operators") if isinstance(concrete, dict) else None)
337
+ )
338
+ if isinstance(operators, list):
339
+ if _baldwin_compiler is not None:
340
+ try:
341
+ baldwin_nets = _baldwin_compiler(operators)
342
+ except Exception:
343
+ pass
344
+ else:
345
+ import sys
346
+
347
+ gen_mod = sys.modules.get("clm.layer1.algebra_baldwin_generators")
348
+ arrow_mod = sys.modules.get("clm.layer1.arrow_compiler")
349
+ if gen_mod and arrow_mod:
350
+ try:
351
+ Arrow = getattr(arrow_mod, "Arrow", None)
352
+ BaldwinCpnGenerators = getattr(gen_mod, "BaldwinCpnGenerators", None)
353
+ if Arrow and BaldwinCpnGenerators:
354
+ for op_def in operators:
355
+ if not isinstance(op_def, dict):
356
+ continue
357
+ op_name = op_def.get("name")
358
+ if not op_name:
359
+ continue
360
+ if op_name == "split":
361
+ base_arr = Arrow.arr(lambda x: x, name="base")
362
+ net = BaldwinCpnGenerators.split(
363
+ base_arr,
364
+ branch_a_fn=lambda x: x,
365
+ branch_b_fn=lambda x: x,
366
+ name_prefix=f"ast_{op_name}",
367
+ )
368
+ baldwin_nets[op_name] = net
369
+ elif op_name == "invert":
370
+ pair_arr = Arrow.arr(lambda x: (x, x), name="pair")
371
+ net = BaldwinCpnGenerators.invert(pair_arr)
372
+ baldwin_nets[op_name] = net
373
+ except Exception:
374
+ pass
375
+
376
+ return ParsedClmAst(
377
+ raw=raw_dict,
378
+ abstract=abstract if isinstance(abstract, dict) else {"spec": abstract},
379
+ concrete=concrete if isinstance(concrete, dict) else {"impl": concrete},
380
+ balanced=balanced if isinstance(balanced, dict) else {"exp": balanced},
381
+ meta=meta if isinstance(meta, dict) else {"meta": meta},
382
+ preconditions=preconditions,
383
+ postconditions=postconditions,
384
+ cel_evaluators=cel_evaluators,
385
+ baldwin_nets=baldwin_nets,
386
+ cards=cards,
387
+ chapter=chapter_dict,
388
+ chapter_name=chapter_name,
389
+ chapter_number=chapter_number,
390
+ narrative=narrative,
391
+ )
392
+
393
+ @classmethod
394
+ def parse_file(
395
+ cls,
396
+ file_path: str | Path,
397
+ env: dict[str, str] | None = None,
398
+ g_time: str = "1970-01-01T00:00:00Z",
399
+ dictionary: ClmReservedWords | None = None,
400
+ ) -> ParsedClmAst:
401
+ """Parses a CLM manifest or chapter specification file from disk."""
402
+ path = Path(file_path).resolve()
403
+ if not path.exists():
404
+ raise FileNotFoundError(f"CLM specification file not found: {path}")
405
+
406
+ if path.is_dir():
407
+ candidates = ["manifest.yaml", "manifest.yml", "clm.yaml", "clm.yml", "index.yaml"]
408
+ for cand in candidates:
409
+ cand_path = path / cand
410
+ if cand_path.exists():
411
+ return cls.parse_file(cand_path, env=env, g_time=g_time, dictionary=dictionary)
412
+ yaml_files = sorted(list(path.glob("*.yaml")) + list(path.glob("*.yml")))
413
+ if yaml_files:
414
+ return cls.parse_file(yaml_files[0], env=env, g_time=g_time, dictionary=dictionary)
415
+ raise ValueError(f"No CLM YAML manifests found in directory: {path}")
416
+
417
+ source_bytes = path.read_bytes()
418
+ return cls.parse(
419
+ input_data=source_bytes,
420
+ clm_dir=path.parent,
421
+ env=env,
422
+ g_time=g_time,
423
+ dictionary=dictionary,
424
+ )
425
+
426
+
427
+ # Canonical alias for backward compatibility across the repository
428
+ ClmRoot = UnifiedClmParser
429
+
@@ -0,0 +1,73 @@
1
+ """Relational card queries (Sprint 133.1 + 133.6).
2
+
3
+ Extracted from ``CLMRepository`` in ``db.py``. These methods import ``Card``
4
+ from ``cross_cutting.mcard`` lazily (inside function bodies) to avoid a
5
+ ``layer0 -> cross_cutting`` import cycle. Isolating them here makes the
6
+ cycle explicit and documented rather than hidden inside a 1,100-line
7
+ god-module.
8
+
9
+ Long-term fix (Sprint 134): move ``Card`` to ``layer0/card.py`` so
10
+ ``layer0`` has no upward dependency on ``cross_cutting`` at all.
11
+ """
12
+ import contextlib
13
+ import json as _json
14
+ from typing import Any
15
+
16
+
17
+ def query_card_relations(repo: Any, card_hash: str, pillar: str = "execution_log") -> dict[str, list]:
18
+ """Resolve related cards via SQLite relational calculus (Sprint 131.2).
19
+
20
+ Returns ``{"inputs": [...], "outputs": [...], "processes": [...]}``
21
+ where each entry is a parsed ``Card``.
22
+ """
23
+ from clm.cross_cutting.mcard import Card
24
+
25
+ relations: dict[str, list] = {"inputs": [], "outputs": [], "processes": []}
26
+
27
+ content = repo.get_card_content(card_hash, pillar)
28
+ if not content:
29
+ return relations
30
+ try:
31
+ card = Card.from_yaml(content)
32
+ except Exception:
33
+ return relations
34
+
35
+ if card.token_type == "pcard":
36
+ for h in card.input_place_hashes:
37
+ c = repo.get_card_content(h, pillar)
38
+ if c:
39
+ with contextlib.suppress(Exception):
40
+ relations["inputs"].append(Card.from_yaml(c))
41
+ for cand in _scan_cards_containing(repo, pillar, card_hash):
42
+ if getattr(cand, "producer_pcard_hash", None) == card_hash:
43
+ relations["outputs"].append(cand)
44
+
45
+ elif card.token_type == "vcard":
46
+ if card.pcard_hash:
47
+ c = repo.get_card_content(card.pcard_hash, pillar)
48
+ if c:
49
+ with contextlib.suppress(Exception):
50
+ relations["processes"].append(Card.from_yaml(c))
51
+
52
+ else: # mcard or state
53
+ for cand in _scan_cards_containing(repo, pillar, card_hash):
54
+ if (cand.token_type == "pcard" and card_hash in (cand.input_place_hashes or [])) \
55
+ or getattr(cand, "producer_pcard_hash", None) == card_hash:
56
+ relations["processes"].append(cand)
57
+
58
+ return relations
59
+
60
+
61
+ def _scan_cards_containing(repo: Any, pillar: str, needle: str) -> list:
62
+ """Scan all cards in a pillar and return parsed Cards whose content
63
+ mentions ``needle``. Used as the FTS5 fallback per §6.2 risk row."""
64
+ from clm.cross_cutting.mcard import Card
65
+
66
+ rows_json = repo.db.execute(pillar, "SELECT content FROM card;", as_json=True)
67
+ out = []
68
+ for row in _json.loads(rows_json or "[]"):
69
+ content = row.get("content", "")
70
+ if needle in content:
71
+ with contextlib.suppress(Exception):
72
+ out.append(Card.from_yaml(content))
73
+ return out