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,64 @@
1
+ """CEL string, path, and comparison ops (Sprint 133.2).
2
+
3
+ Extracted from ``cel_ops.py``.
4
+ """
5
+ import os
6
+ from collections.abc import Callable
7
+
8
+ from clm.cross_cutting.cel_engine import _resolve_arg
9
+
10
+
11
+ def op_path_join(cel: dict, ctx: dict):
12
+ parts = [str(_resolve_arg(p, ctx)) for p in cel["parts"]]
13
+ return os.path.join(*parts)
14
+
15
+
16
+ def op_path_basename(cel: dict, ctx: dict):
17
+ value = _resolve_arg(cel["value"], ctx)
18
+ return os.path.basename(str(value))
19
+
20
+
21
+ def op_string_concat(cel: dict, ctx: dict):
22
+ parts = [_resolve_arg(p, ctx) for p in cel["parts"]]
23
+ return "".join(str(p) for p in parts)
24
+
25
+
26
+ def op_string_join(cel: dict, ctx: dict):
27
+ separator = str(_resolve_arg(cel["separator"], ctx))
28
+ parts = [_resolve_arg(p, ctx) for p in cel["parts"]]
29
+ return separator.join(str(p) for p in parts)
30
+
31
+
32
+ def op_replace(cel: dict, ctx: dict):
33
+ value = str(_resolve_arg(cel["value"], ctx))
34
+ old = str(_resolve_arg(cel["old"], ctx))
35
+ new = str(_resolve_arg(cel["new"], ctx))
36
+ return value.replace(old, new)
37
+
38
+
39
+ _COMPARE_OPS: dict[str, Callable] = {
40
+ "eq": lambda a, b: a == b,
41
+ "ne": lambda a, b: a != b,
42
+ "gt": lambda a, b: a > b,
43
+ "gte": lambda a, b: a >= b,
44
+ "lt": lambda a, b: a < b,
45
+ "lte": lambda a, b: a <= b,
46
+ "in": lambda a, b: (tuple(a) if isinstance(a, list) else a) in b,
47
+ "not_in": lambda a, b: (tuple(a) if isinstance(a, list) else a) not in b,
48
+ }
49
+
50
+
51
+ def op_compare(cel: dict, ctx: dict):
52
+ left = _resolve_arg(cel["left"], ctx)
53
+ right = _resolve_arg(cel["right"], ctx)
54
+ operator = cel["operator"]
55
+ fn = _COMPARE_OPS.get(operator)
56
+ if fn is None:
57
+ raise ValueError(f"Unknown CEL compare operator: {operator}")
58
+ return fn(left, right)
59
+
60
+
61
+ def op_contains(cel: dict, ctx: dict):
62
+ haystack = _resolve_arg(cel["haystack"], ctx)
63
+ needle = _resolve_arg(cel["needle"], ctx)
64
+ return needle in haystack
@@ -0,0 +1,63 @@
1
+ """Unified CEL reference resolver (Sprint 133 §7.4).
2
+
3
+ Extracted from ``cel_engine._resolve_ref`` and ``CelStore.select``.
4
+ Both operated on the same dot-notation namespace dispatch but were
5
+ duplicated — one for raw ``dict`` contexts, one for typed ``CelStore``.
6
+
7
+ This module provides a single ``resolve_ref`` function that works against
8
+ either a ``dict`` or a ``CelStore``, eliminating the duplication.
9
+ """
10
+ from typing import Any
11
+
12
+
13
+ def resolve_ref(ref_str: str, source: Any) -> Any:
14
+ """Resolve a dot-notation reference against a dict or CelStore.
15
+
16
+ Namespaces:
17
+ inputs.<key> -> source["inputs"][key] / source.state.inputs[key]
18
+ outputs.<key> -> source["outputs"][key] / source.state.outputs[key]
19
+ state.<key> -> source["retrieve"](key) / source.state.vars[key]
20
+ env.<key> -> source["tmp_dir"] etc. / source.env.tmp_dir etc.
21
+ services.<name> -> source["db"] etc. / source.services.db etc.
22
+
23
+ Args:
24
+ ref_str: Dot-notation reference (e.g. ``"inputs.schema_path"``).
25
+ source: A ``dict`` (legacy context) or a ``CelStore``.
26
+
27
+ Returns:
28
+ The resolved value.
29
+
30
+ Raises:
31
+ ValueError: If the namespace is unknown.
32
+ KeyError: If the key is not found in the namespace.
33
+ """
34
+ parts = ref_str.split(".", 1)
35
+ namespace = parts[0]
36
+ key = parts[1] if len(parts) > 1 else ""
37
+
38
+ # CelStore path (typed)
39
+ if hasattr(source, "state") and hasattr(source, "env"):
40
+ if namespace == "inputs":
41
+ return source.state.inputs[key]
42
+ if namespace == "outputs":
43
+ return source.state.outputs[key]
44
+ if namespace == "state":
45
+ return source.state.vars[key]
46
+ if namespace == "env":
47
+ env_map = {"tmp_dir": source.env.tmp_dir, "clm_root": source.env.clm_root, "rc": source.state.rc}
48
+ return env_map.get(key, key)
49
+ if namespace == "services":
50
+ return getattr(source.services, key, None)
51
+ raise ValueError(f"Unknown CEL reference namespace: {namespace}")
52
+
53
+ # Dict path (legacy context)
54
+ if namespace == "inputs":
55
+ return source["inputs"][key]
56
+ if namespace == "outputs":
57
+ return source["outputs"][key]
58
+ if namespace == "state":
59
+ return source["retrieve"](key)
60
+ if namespace == "env":
61
+ env_map = {"tmp_dir": "tmp_dir", "clm_root": "clm_root", "rc": "rc"}
62
+ return source[env_map.get(key, key)]
63
+ raise ValueError(f"Unknown CEL reference namespace: {namespace}")
@@ -0,0 +1,56 @@
1
+ """CelStore state slices (Sprint 133.2).
2
+
3
+ Extracted from ``cel_store.py``. The three Pydantic slice models that
4
+ structure the store's state: ``StateSlice`` (mutable), ``ServiceSlice``
5
+ (read-only services), ``EnvSlice`` (read-only environment).
6
+ """
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class StateSlice(BaseModel):
13
+ """Mutable test state — the data that changes during a test case."""
14
+ inputs: dict[str, Any] = Field(default_factory=dict)
15
+ outputs: dict[str, Any] = Field(default_factory=dict)
16
+ rc: int = 0
17
+ vars: dict[str, Any] = Field(default_factory=dict)
18
+ card_vars: dict[str, str] = Field(default_factory=dict)
19
+
20
+ def get(self, key: str) -> Any:
21
+ return self.vars.get(key)
22
+
23
+ def set(self, key: str, value: Any) -> Any:
24
+ self.vars[key] = value
25
+ self.card_vars.pop(key, None)
26
+ return value
27
+
28
+ def set_card(self, key: str, handle: str, value: Any) -> Any:
29
+ self.vars[key] = value
30
+ self.card_vars[key] = handle
31
+ return value
32
+
33
+
34
+ class ServiceSlice(BaseModel):
35
+ """Read-only services injected into the store."""
36
+ db: Any = None
37
+ ctx: Any = None
38
+ repo: Any = None
39
+ classify_clm: Any = None
40
+ detect_dialect: Any = None
41
+ modules: dict[str, Any] = Field(default_factory=dict)
42
+
43
+ model_config = ConfigDict(arbitrary_types_allowed=True)
44
+
45
+
46
+ class EnvSlice(BaseModel):
47
+ """Read-only environment configuration."""
48
+ clm_root: str = ""
49
+ tmp_dir: str = ""
50
+ os: Any = None
51
+ sys: Any = None
52
+ time: Any = None
53
+ hashlib: Any = None
54
+ str: type = str
55
+
56
+ model_config = ConfigDict(arbitrary_types_allowed=True)
@@ -0,0 +1,330 @@
1
+ """CelStore — Flux-pattern store for CEL evaluation state (Sprint 128.1+128.2).
2
+
3
+ Implements the Single Source of Truth principle from the Flux/Redux/Zustand
4
+ pattern. Replaces the 30+ key mutable ``context`` dict ("God Object") with a
5
+ typed store structured into three slices:
6
+
7
+ - ``StateSlice`` — mutable test state (inputs, outputs, rc, vars)
8
+ - ``ServiceSlice`` — read-only services (db, repo, ctx, classifiers)
9
+ - ``EnvSlice`` — read-only environment (clm_root, tmp_dir)
10
+
11
+ Unidirectional data flow:
12
+
13
+ action -> pre-middleware -> reducer -> post-middleware -> action log
14
+ -> notify subscribers
15
+
16
+ Backward compatibility: :meth:`CelStore.to_dict` produces the exact dict shape
17
+ that existing CEL op handlers and tests expect, so handlers continue to
18
+ receive ``dict`` and require no changes during migration.
19
+
20
+ Design references:
21
+ - Sprint 128 spec (docs/sprints/sprint-128-flux-store-architecture.md)
22
+ - Redux (single store, unidirectional flow)
23
+ - Zustand (minimal get/set/subscribe API)
24
+ - nanostores (computed selectors)
25
+ - Thunk (side-effect isolation via EffectRegistry)
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import time
31
+ from collections.abc import Callable
32
+ from typing import Any
33
+
34
+ from pydantic import BaseModel # noqa: F401 — re-exported for backward compat
35
+
36
+ # Sprint 133.2: middleware types (re-exported from cel_builder for backward compat)
37
+ from clm.cross_cutting.cel_builder import ( # noqa: F401
38
+ Listener,
39
+ PostMiddleware,
40
+ PreMiddleware,
41
+ )
42
+
43
+ # Sprint 133.2: slices extracted to cel_slices.py
44
+ from clm.cross_cutting.cel_slices import EnvSlice, ServiceSlice, StateSlice # noqa: F401
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # CelStore (Sprint 128.1 + 128.2)
48
+ # ---------------------------------------------------------------------------
49
+
50
+ class CelStore:
51
+ """Flux-pattern store — the Single Source of Truth for CEL evaluation.
52
+
53
+ API (Zustand-inspired minimal surface):
54
+
55
+ store.get_state() -> StateSlice
56
+ store.get_services() -> ServiceSlice
57
+ store.get_env() -> EnvSlice
58
+ store.set(key, value) -> mutate state.vars[key]
59
+ store.get(key) -> read state.vars[key]
60
+ store.select(ref) -> dot-notation selector
61
+ store.dispatch(action) -> unidirectional flow
62
+ store.subscribe(fn) -> listen for state changes
63
+ store.to_dict() -> backward-compat dict adapter
64
+
65
+ The dispatch flow is:
66
+
67
+ 1. Run pre-middleware (can modify or reject action by returning None)
68
+ 2. Invoke reducer via CelOpRegistry.dispatch()
69
+ 3. Run post-middleware (observes result)
70
+ 4. Append to action_log
71
+ 5. Notify subscribers
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ state: StateSlice | None = None,
77
+ services: ServiceSlice | None = None,
78
+ env: EnvSlice | None = None,
79
+ ) -> None:
80
+ self.state: StateSlice = state or StateSlice()
81
+ self.services: ServiceSlice = services or ServiceSlice()
82
+ self.env: EnvSlice = env or EnvSlice()
83
+ self.action_log: list[dict[str, Any]] = []
84
+ self._listeners: list[Listener] = []
85
+ self._pre_middleware: list[PreMiddleware] = []
86
+ self._post_middleware: list[PostMiddleware] = []
87
+ # Lazily-injected side-effect registry (Sprint 128.4)
88
+ self.effects: Any = None
89
+ # Writer monad delegate (Sprint 132.1 — handle_history persistence)
90
+ from clm.cross_cutting.writer import StoreWriter
91
+ self.writer: StoreWriter = StoreWriter(self)
92
+ # Sprint 132.5: cached to_dict() — invalidated on set()/set_persisted()
93
+ self._ctx_cache: dict[str, Any] | None = None
94
+
95
+ def _invalidate_cache(self) -> None:
96
+ """Invalidate the to_dict() cache (Sprint 132.5)."""
97
+ self._ctx_cache = None
98
+
99
+ # ------------------------------------------------------------------
100
+ # Slice accessors
101
+ # ------------------------------------------------------------------
102
+
103
+ def get_state(self) -> StateSlice:
104
+ return self.state
105
+
106
+ def get_services(self) -> ServiceSlice:
107
+ return self.services
108
+
109
+ def get_env(self) -> EnvSlice:
110
+ return self.env
111
+
112
+ # ------------------------------------------------------------------
113
+ # State mutation (Zustand-style minimal API)
114
+ # ------------------------------------------------------------------
115
+
116
+ def get(self, key: str) -> Any:
117
+ return self.state.get(key)
118
+
119
+ def set(self, key: str, value: Any) -> Any:
120
+ self._invalidate_cache()
121
+ return self.state.set(key, value)
122
+
123
+ def select(self, ref: str) -> Any:
124
+ """Resolve a dot-notation reference against the store.
125
+
126
+ Sprint 133 §7.4: delegates to ``cel_resolver.resolve_ref`` for a
127
+ single implementation shared with ``cel_engine._resolve_ref``.
128
+ """
129
+ from clm.cross_cutting.cel_resolver import resolve_ref
130
+ return resolve_ref(ref, self)
131
+
132
+ # ------------------------------------------------------------------
133
+ # Handle-backed persistence (Sprint 131.3 — opt-in, backward-compatible)
134
+ # ------------------------------------------------------------------
135
+
136
+ def set_persisted(self, key: str, value: Any, pillar: str = "execution_log") -> Any:
137
+ """Delegate to StoreWriter (Writer monad — moved to writer.py in Sprint 132.1)."""
138
+ self._invalidate_cache()
139
+ return self.writer.set_persisted(key, value, pillar)
140
+
141
+ def select_card(self, ref: str) -> Any:
142
+ """Delegate to StoreWriter (Writer monad — moved to writer.py in Sprint 132.1)."""
143
+ return self.writer.select_card(ref)
144
+
145
+ # ------------------------------------------------------------------
146
+ # Middleware registration
147
+ # ------------------------------------------------------------------
148
+
149
+ def add_pre_middleware(self, mw: PreMiddleware) -> None:
150
+ self._pre_middleware.append(mw)
151
+
152
+ def add_post_middleware(self, mw: PostMiddleware) -> None:
153
+ self._post_middleware.append(mw)
154
+
155
+ # ------------------------------------------------------------------
156
+ # Subscribers
157
+ # ------------------------------------------------------------------
158
+
159
+ def subscribe(self, listener: Listener) -> Callable[[], None]:
160
+ """Subscribe to state changes. Returns an unsubscribe function."""
161
+ self._listeners.append(listener)
162
+
163
+ def unsubscribe() -> None:
164
+ if listener in self._listeners:
165
+ self._listeners.remove(listener)
166
+
167
+ return unsubscribe
168
+
169
+ # ------------------------------------------------------------------
170
+ # Dispatch (Sprint 128.2 — unidirectional data flow)
171
+ # ------------------------------------------------------------------
172
+
173
+ def dispatch(self, action: dict) -> Any:
174
+ """Dispatch a CEL action through the middleware pipeline.
175
+
176
+ Flow:
177
+ pre-middleware -> reducer -> post-middleware -> log -> notify
178
+
179
+ If any pre-middleware returns ``None``, the action is rejected
180
+ and ``None`` is returned without invoking the reducer.
181
+ """
182
+ ctx = self.to_dict()
183
+
184
+ # 1. Pre-middleware (can reject by returning None)
185
+ for mw in self._pre_middleware:
186
+ result = mw(self, action)
187
+ if result is None:
188
+ self._log_action(action, None, rejected=True)
189
+ return None
190
+ action = result
191
+ ctx = self.to_dict()
192
+
193
+ # 2. Reducer (delegate to CelOpRegistry)
194
+ from clm.cross_cutting.cel_engine import _DEFAULT_REGISTRY
195
+ result = _DEFAULT_REGISTRY.dispatch(action, ctx)
196
+
197
+ # 3. Post-middleware
198
+ for mw in self._post_middleware:
199
+ mw(self, action, result)
200
+
201
+ # 4. Action log
202
+ self._log_action(action, result)
203
+
204
+ # 5. Notify subscribers
205
+ for listener in self._listeners:
206
+ listener(self, action, result)
207
+
208
+ return result
209
+
210
+ def _log_action(self, action: dict, result: Any, rejected: bool = False) -> None:
211
+ self.action_log.append({
212
+ "type": "cel_dispatch",
213
+ "op": action.get("op"),
214
+ "action": action,
215
+ "result": repr(result)[:200],
216
+ "rejected": rejected,
217
+ "timestamp": time.time(),
218
+ })
219
+
220
+ # ------------------------------------------------------------------
221
+ # Backward-compatibility adapter (Sprint 128.1)
222
+ # ------------------------------------------------------------------
223
+
224
+ def to_dict(self) -> dict[str, Any]:
225
+ """Produce the legacy context dict shape for op handlers.
226
+
227
+ Sprint 132.5: caches the result; invalidated on set()/set_persisted().
228
+ """
229
+ if self._ctx_cache is not None:
230
+ return self._ctx_cache
231
+ ctx: dict[str, Any] = {
232
+ # State slice
233
+ "inputs": self.state.inputs,
234
+ "outputs": self.state.outputs,
235
+ "rc": self.state.rc,
236
+ "store": self.state.set,
237
+ "retrieve": self.state.get,
238
+ # Service slice
239
+ "db": self.services.db,
240
+ "ctx": self.services.ctx,
241
+ "repo": self.services.repo,
242
+ "classify_clm": self.services.classify_clm,
243
+ "detect_dialect": self.services.detect_dialect,
244
+ # Env slice
245
+ "clm_root": self.env.clm_root,
246
+ "tmp_dir": self.env.tmp_dir,
247
+ "os": self.env.os,
248
+ "sys": self.env.sys,
249
+ "time": self.env.time,
250
+ "hashlib": self.env.hashlib,
251
+ "str": self.env.str,
252
+ }
253
+ # Merge any extra service modules (write_file, make_mcard_content, etc.)
254
+ ctx.update(self.services.modules)
255
+ # Expose the effect registry for side-effect isolation (Sprint 128.4).
256
+ ctx["effects"] = self.effects
257
+ self._ctx_cache = ctx
258
+ return ctx
259
+
260
+ # ------------------------------------------------------------------
261
+ # Monadic operations (Sprint 129.2 — bind/unit)
262
+ # ------------------------------------------------------------------
263
+
264
+ def bind(self, next_fn: Callable[["CelStore", Any], Any]) -> Any:
265
+ """Monadic bind (>>= operator).
266
+
267
+ Threads the store through a sequence of computations:
268
+ store.bind(f).bind(g) == store >>= f >>= g
269
+
270
+ Each bind:
271
+ 1. If there's a pending action, dispatch it and use the result
272
+ 2. Otherwise, use the monadic value from the previous unit()
273
+ 3. Pass the value to next_fn
274
+ 4. Return the result
275
+
276
+ This satisfies the three monad laws (verified in tests/test_monadic_laws.py):
277
+ Left identity: unit(a).bind(f) == f(store, a)
278
+ Right identity: store.bind(f).bind(unit) == store.bind(f)
279
+ Associativity: store.bind(f).bind(g) == store.bind(lambda s, x: f(s,x).bind(g))
280
+ """
281
+ result = None
282
+ pending = getattr(self, "_pending_action", None)
283
+ if pending is not None:
284
+ self._pending_action = None
285
+ result = self.dispatch(pending)
286
+ else:
287
+ # Use monadic value from previous unit() call
288
+ result = self.state.get("_monadic_value")
289
+ return next_fn(self, result)
290
+
291
+ def unit(self, value: Any) -> "CelStore":
292
+ """Monadic unit/return — wraps a value in the store context.
293
+
294
+ Stores the value as a state var, ready for the next bind in the chain.
295
+ """
296
+ self.set("_monadic_value", value)
297
+ self._pending_action = None
298
+ return self
299
+
300
+ @property
301
+ def pending_action(self) -> dict | None:
302
+ """Get or set the pending action for deferred dispatch via bind()."""
303
+ return getattr(self, "_pending_action", None)
304
+
305
+ @pending_action.setter
306
+ def pending_action(self, action: dict | None) -> None:
307
+ self._pending_action = action
308
+
309
+ # ------------------------------------------------------------------
310
+ # Flux event store persistence (Sprint 129.3)
311
+ # ------------------------------------------------------------------
312
+
313
+ def flush_action_log(self, ctx: Any) -> str | None:
314
+ """Delegate to StoreWriter (Writer monad — moved to writer.py in Sprint 132.1)."""
315
+ return self.writer.flush_action_log(ctx)
316
+
317
+
318
+ # Sprint 133.2: thunk moved to vcard.py (Sprint 132.1); re-exported here.
319
+ # Sprint 133.2: middleware factories + build_store extracted to cel_builder.py
320
+ from clm.cross_cutting.cel_builder import ( # noqa: E402,F401
321
+ _build_write_file_fn,
322
+ action_log_middleware,
323
+ build_store,
324
+ vcard_post_middleware,
325
+ vcard_pre_middleware,
326
+ )
327
+
328
+ # Sprint 133.2: EffectRegistry re-exported from effects.py (Sprint 132.1)
329
+ from clm.cross_cutting.effects import EffectRegistry, register_default_effects # noqa: E402,F401
330
+ from clm.cross_cutting.vcard import thunk # noqa: E402,F401
@@ -0,0 +1,140 @@
1
+ """CLMLogger — Unified logging for the CLM platform (Sprint 129.1).
2
+
3
+ Replaces all ad-hoc ``print(..., file=sys.stderr)`` calls with a structured,
4
+ leveled, configurable logger. Extends Python's ``logging`` module with
5
+ CLM-specific structured fields (agent_did, run_id, pcard_hash, op).
6
+
7
+ Configuration via environment variables:
8
+ CLM_LOG_LEVEL: DEBUG | INFO | WARNING | ERROR (default: INFO)
9
+ CLM_LOG_FORMAT: text | json (default: text)
10
+ CLM_LOG_FILE: Path to log file (default: stderr only)
11
+ CLM_LOG_STRUCTURED: true | false (default: false; if true, forces JSON)
12
+
13
+ Cross-runtime protocol:
14
+ Rust/WASM/Shell runtimes emit the same JSON schema to stderr,
15
+ enabling unified observability across language boundaries.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import logging
22
+ import os
23
+ import sys
24
+ from typing import Any
25
+
26
+ from clm.cross_cutting.timeutil import default_agent_did, now_g_time
27
+
28
+
29
+ class CLMJsonFormatter(logging.Formatter):
30
+ """JSON formatter for cross-runtime log compatibility."""
31
+
32
+ def format(self, record: logging.LogRecord) -> str:
33
+ log_entry: dict[str, Any] = {
34
+ "timestamp": now_g_time(),
35
+ "level": record.levelname,
36
+ "logger": record.name,
37
+ "message": record.getMessage(),
38
+ }
39
+ for attr in ("agent_did", "run_id", "pcard_hash", "op"):
40
+ if hasattr(record, attr):
41
+ log_entry[attr] = getattr(record, attr)
42
+ return json.dumps(log_entry)
43
+
44
+
45
+ class DynamicStreamHandler(logging.StreamHandler):
46
+ """A StreamHandler that resolves sys.stderr dynamically on every write,
47
+ allowing unit tests to redirect stderr using sys.stderr = StringIO().
48
+ """
49
+
50
+ @property
51
+ def stream(self) -> Any:
52
+ return sys.stderr
53
+
54
+ @stream.setter
55
+ def stream(self, value: Any) -> None:
56
+ pass
57
+
58
+
59
+ class CLMLogger:
60
+ """Unified logger for the CLM platform.
61
+
62
+ Extends Python's logging.Logger with CLM-specific structured fields:
63
+ - agent_did: The DID of the executing agent
64
+ - run_id: The current CLM run ID
65
+ - pcard_hash: The PCard being executed (if applicable)
66
+ - op: The CEL operation being dispatched (if applicable)
67
+ """
68
+
69
+ _instance: CLMLogger | None = None
70
+
71
+ @classmethod
72
+ def get(cls, name: str = "clm") -> CLMLogger:
73
+ """Get the singleton CLMLogger instance."""
74
+ if cls._instance is None:
75
+ cls._instance = cls(name)
76
+ return cls._instance
77
+
78
+ def __init__(self, name: str = "clm") -> None:
79
+ self._logger = logging.getLogger(name)
80
+ self._agent_did = default_agent_did()
81
+ self._run_id = os.environ.get("CLM_RUN_ID", "")
82
+ self._configure()
83
+
84
+ def _configure(self) -> None:
85
+ level = os.environ.get("CLM_LOG_LEVEL", "INFO").upper()
86
+ fmt = os.environ.get("CLM_LOG_FORMAT", "text")
87
+ log_file = os.environ.get("CLM_LOG_FILE", "")
88
+
89
+ self._logger.setLevel(getattr(logging, level, logging.INFO))
90
+
91
+ if self._logger.handlers:
92
+ return
93
+
94
+ if fmt == "json" or os.environ.get("CLM_LOG_STRUCTURED", "").lower() == "true":
95
+ handler: logging.Handler = DynamicStreamHandler()
96
+ handler.setFormatter(CLMJsonFormatter())
97
+ else:
98
+ handler = DynamicStreamHandler()
99
+ handler.setFormatter(logging.Formatter(
100
+ "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
101
+ datefmt="%Y-%m-%dT%H:%M:%S%z",
102
+ ))
103
+
104
+ self._logger.addHandler(handler)
105
+
106
+ if log_file:
107
+ file_handler = logging.FileHandler(log_file)
108
+ file_handler.setFormatter(handler.formatter)
109
+ self._logger.addHandler(file_handler)
110
+
111
+ def _extra(self, **kwargs: Any) -> dict:
112
+ extra: dict[str, Any] = {"agent_did": self._agent_did, "run_id": self._run_id}
113
+ extra.update(kwargs)
114
+ return extra
115
+
116
+ def debug(self, msg: str, **kwargs: Any) -> None:
117
+ exc_info = kwargs.pop("exc_info", None)
118
+ self._logger.debug(msg, extra=self._extra(**kwargs), exc_info=exc_info)
119
+
120
+ def info(self, msg: str, **kwargs: Any) -> None:
121
+ exc_info = kwargs.pop("exc_info", None)
122
+ self._logger.info(msg, extra=self._extra(**kwargs), exc_info=exc_info)
123
+
124
+ def warning(self, msg: str, **kwargs: Any) -> None:
125
+ exc_info = kwargs.pop("exc_info", None)
126
+ self._logger.warning(msg, extra=self._extra(**kwargs), exc_info=exc_info)
127
+
128
+ def error(self, msg: str, **kwargs: Any) -> None:
129
+ exc_info = kwargs.pop("exc_info", None)
130
+ self._logger.error(msg, extra=self._extra(**kwargs), exc_info=exc_info)
131
+
132
+ def critical(self, msg: str, **kwargs: Any) -> None:
133
+ exc_info = kwargs.pop("exc_info", None)
134
+ self._logger.critical(msg, extra=self._extra(**kwargs), exc_info=exc_info)
135
+
136
+
137
+
138
+ def get_logger(name: str = "clm") -> CLMLogger:
139
+ """Convenience function to get the CLMLogger singleton."""
140
+ return CLMLogger.get(name)