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,118 @@
1
+ """Supervised Subprocess Execution and Process Group Guards (Sprint 178 / Sprint 384 Quarantine).
2
+
3
+ [QUARANTINE - HOST-SIDE ADAPTER TOOLING ONLY]
4
+ ---------------------------------------------
5
+ WARNING: This module is strictly quarantined for host-side tooling, external runtime adapters,
6
+ and out-of-process subprocess execution. It MUST NOT be imported or invoked by pure Layer 0
7
+ or Layer 1 kernel state transitions (DEL-384-05). All kernel state transitions must execute
8
+ deterministically within in-memory state monads or Cordis fibers.
9
+
10
+ Eliminates unbounded blocking calls, zombie process leaks, and pipe buffer deadlocks.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import signal
17
+ import subprocess
18
+ import sys
19
+ from typing import Any, NamedTuple
20
+
21
+
22
+ class SubprocessResult(NamedTuple):
23
+ returncode: int
24
+ stdout: str
25
+ stderr: str
26
+ timed_out: bool
27
+
28
+
29
+ def safe_subprocess_run(
30
+ cmd: list[str],
31
+ *,
32
+ input_data: str | bytes | None = None,
33
+ timeout: float = 30.0,
34
+ cwd: str | None = None,
35
+ env: dict[str, str] | None = None,
36
+ ) -> SubprocessResult:
37
+ """Execute a subprocess safely with process group isolation and guaranteed cleanup.
38
+
39
+ [HOST-ADAPTER ONLY] Must not be called from Layer 0 / Layer 1 kernel transitions.
40
+ - Creates a new POSIX process session (os.setsid) so all child processes can be killed together.
41
+ - Implements non-deadlocking stream consumption via proc.communicate().
42
+ - Kills the entire process group (os.killpg) on timeout or exception.
43
+ """
44
+ is_posix = sys.platform != "win32"
45
+ preexec = os.setsid if is_posix else None
46
+
47
+ stdin_pipe = subprocess.PIPE if input_data is not None else None
48
+ text_mode = isinstance(input_data, str) or input_data is None
49
+
50
+ proc = subprocess.Popen(
51
+ cmd,
52
+ stdin=stdin_pipe,
53
+ stdout=subprocess.PIPE,
54
+ stderr=subprocess.PIPE,
55
+ text=text_mode,
56
+ cwd=cwd,
57
+ env=env or os.environ.copy(),
58
+ preexec_fn=preexec,
59
+ )
60
+
61
+ try:
62
+ stdout, stderr = proc.communicate(input=input_data, timeout=timeout)
63
+ if isinstance(stdout, bytes):
64
+ stdout = stdout.decode("utf-8", errors="replace")
65
+ if isinstance(stderr, bytes):
66
+ stderr = stderr.decode("utf-8", errors="replace")
67
+ return SubprocessResult(proc.returncode, stdout or "", stderr or "", False)
68
+ except subprocess.TimeoutExpired:
69
+ if is_posix:
70
+ try:
71
+ pgid = os.getpgid(proc.pid)
72
+ os.killpg(pgid, signal.SIGKILL)
73
+ except ProcessLookupError:
74
+ pass
75
+ else:
76
+ proc.kill()
77
+ try:
78
+ proc.communicate(timeout=2.0)
79
+ except Exception:
80
+ pass
81
+ return SubprocessResult(
82
+ -signal.SIGKILL if is_posix else -1,
83
+ "",
84
+ f"Process timed out after {timeout}s",
85
+ True,
86
+ )
87
+ except Exception:
88
+ if is_posix:
89
+ try:
90
+ pgid = os.getpgid(proc.pid)
91
+ os.killpg(pgid, signal.SIGKILL)
92
+ except ProcessLookupError:
93
+ pass
94
+ else:
95
+ proc.kill()
96
+ raise
97
+
98
+
99
+ def run_in_clm_context(
100
+ cmd: list[str],
101
+ *,
102
+ ctx: Any | None = None,
103
+ input_data: str | bytes | None = None,
104
+ timeout: float = 30.0,
105
+ cwd: str | None = None,
106
+ extra_env: dict[str, str] | None = None,
107
+ ) -> SubprocessResult:
108
+ """Execute a subprocess injecting CLMContext environment flags without mutating global os.environ.
109
+
110
+ [HOST-ADAPTER ONLY] Must not be called from Layer 0 / Layer 1 kernel transitions.
111
+ """
112
+ from clm.layer0.context import CLMContext
113
+
114
+ context = ctx or CLMContext.current()
115
+ env = context.to_env_dict()
116
+ if extra_env:
117
+ env.update(extra_env)
118
+ return safe_subprocess_run(cmd, input_data=input_data, timeout=timeout, cwd=cwd, env=env)
@@ -0,0 +1,164 @@
1
+ """Telemetry and event emission utilities for the CLM platform."""
2
+ import atexit
3
+ import json
4
+ import os
5
+ import warnings
6
+ from typing import Any
7
+
8
+ from clm.cross_cutting.clm_logger import get_logger
9
+ from clm.cross_cutting.timeutil import default_agent_did, now_g_time
10
+ from clm.layer0.utils import clm_hash_string
11
+
12
+ _logger = get_logger("clm.telemetry")
13
+
14
+ # Sprint 132.5: module-level cached file handle and CLMRepository.
15
+ _FILE_CACHE: dict[str, object] = {}
16
+ _REPO_CACHE: dict[str, Any] = {}
17
+ _LAST_RUN_ID: str | None = None
18
+
19
+
20
+ def reset_telemetry_cache() -> None:
21
+ """Flush and discard cached file handles and CLMRepository instances.
22
+
23
+ Tests call this between runs to force a clean state.
24
+ """
25
+ global _LAST_RUN_ID
26
+ for fh in _FILE_CACHE.values():
27
+ try:
28
+ fh.flush()
29
+ fh.close()
30
+ except Exception:
31
+ pass
32
+ _FILE_CACHE.clear()
33
+ _REPO_CACHE.clear()
34
+ _LAST_RUN_ID = None
35
+
36
+
37
+ @atexit.register
38
+ def _flush_on_exit() -> None:
39
+ for fh in _FILE_CACHE.values():
40
+ try:
41
+ fh.flush()
42
+ fh.close()
43
+ except Exception:
44
+ pass
45
+
46
+
47
+ def _get_cached_repo(clm_root: str) -> Any:
48
+ """Return a cached CLMRepository for ``clm_root``, creating one on first call."""
49
+ repo = _REPO_CACHE.get(clm_root)
50
+ if repo is None:
51
+ import sys
52
+ target = "clm.layer2.storage"
53
+ mod = sys.modules.get(target)
54
+ if mod is None:
55
+ __import__(target)
56
+ mod = sys.modules.get(target)
57
+ if mod is not None:
58
+ CLMDB = mod.CLMDB
59
+ CLMRepository = mod.CLMRepository
60
+ db = CLMDB(clm_root)
61
+ repo = CLMRepository(db, clm_root)
62
+ _REPO_CACHE[clm_root] = repo
63
+ return repo
64
+
65
+
66
+ def _coerce_legacy_value(v_str: str) -> Any:
67
+ if v_str == "null":
68
+ return None
69
+ if (v_str.startswith("[") and v_str.endswith("]")) or (v_str.startswith("{") and v_str.endswith("}")):
70
+ try:
71
+ return json.loads(v_str)
72
+ except Exception:
73
+ return v_str
74
+ try:
75
+ return int(v_str)
76
+ except ValueError:
77
+ return v_str
78
+
79
+
80
+ def jsonl_emit(event_type: str, data: dict[str, Any] | list[str], clm_root: str) -> str:
81
+ """Emit a structured JSONL event and optionally register it in the execution_log database.
82
+
83
+ Args:
84
+ event_type: The type of the event.
85
+ data: Either a dict (preferred) or a list of ``"k=v"`` strings (deprecated).
86
+ clm_root: The CLM workspace root.
87
+
88
+ Returns:
89
+ The emitted JSON string.
90
+ """
91
+ map_data: dict[str, Any] = {
92
+ "event": event_type,
93
+ "timestamp": now_g_time(),
94
+ "agent_did": default_agent_did(),
95
+ }
96
+
97
+ if isinstance(data, dict):
98
+ map_data.update(data)
99
+ else:
100
+ warnings.warn(
101
+ "jsonl_emit list[str] form is deprecated; pass a dict",
102
+ DeprecationWarning,
103
+ stacklevel=2,
104
+ )
105
+ for kv in data:
106
+ if "=" in kv:
107
+ k, v_str = kv.split("=", 1)
108
+ map_data[k] = _coerce_legacy_value(v_str)
109
+
110
+ json_line = json.dumps(map_data)
111
+ zero_fs = os.environ.get("CLM_ZERO_FS", "").lower() == "true"
112
+
113
+ events_dir = os.path.join(clm_root, "data", "events")
114
+ log_file_str = os.environ.get("CLM_EVENT_LOG_PATH", os.path.join(events_dir, "token_events.jsonl"))
115
+
116
+ if not zero_fs or "/tmp" in log_file_str:
117
+ os.makedirs(os.path.dirname(log_file_str), exist_ok=True)
118
+ buffered = os.environ.get("CLM_TELEMETRY_BUFFER", "").lower() == "1"
119
+ if buffered:
120
+ fh = _FILE_CACHE.get(log_file_str)
121
+ if fh is None:
122
+ fh = open(log_file_str, "a", encoding="utf-8") # noqa: SIM115 — cached for buffering
123
+ _FILE_CACHE[log_file_str] = fh
124
+ fh.write(json_line + "\n")
125
+ else:
126
+ with open(log_file_str, "a", encoding="utf-8") as f:
127
+ f.write(json_line + "\n")
128
+
129
+ run_id = os.environ.get("CLM_RUN_ID", "")
130
+ _logger.debug(f"jsonl_emit: run_id={run_id}, clm_root={clm_root}, event={event_type}")
131
+ if run_id:
132
+ global _LAST_RUN_ID
133
+ if _LAST_RUN_ID is not None and run_id != _LAST_RUN_ID:
134
+ reset_telemetry_cache()
135
+ _LAST_RUN_ID = run_id
136
+
137
+ repo = _get_cached_repo(clm_root)
138
+ handle_name = f"run:{run_id}:events"
139
+ try:
140
+ current_content = repo.resolve_handle_content(handle_name, "execution_log")
141
+ _logger.debug(f"jsonl_emit: current_content len={len(current_content) if current_content else 0}")
142
+ except Exception as e:
143
+ _logger.error(f"jsonl_emit: resolve failed: {e}")
144
+ current_content = None
145
+
146
+ new_content = f"{current_content}\n{json_line}" if current_content else json_line
147
+
148
+ new_hash = clm_hash_string(new_content)
149
+ try:
150
+ repo.save_card_and_handle(handle_name, new_hash, new_content, "execution_log", replace=True)
151
+ except Exception as e:
152
+ _logger.error(f"jsonl_emit: DB registration failed: {e}")
153
+
154
+ _logger.debug(json_line)
155
+ return json_line
156
+
157
+
158
+ def jsonl_emit_typed(event_type: str, data: dict, clm_root: str) -> str:
159
+ """Emit a structured JSONL event from a typed dict.
160
+
161
+ .. deprecated:: Sprint 132.4
162
+ Use :func:`jsonl_emit` with a ``dict`` argument directly.
163
+ """
164
+ return jsonl_emit(event_type, data, clm_root)
@@ -0,0 +1,8 @@
1
+ """
2
+ MCard Testing Subsystem (Sprint 380).
3
+ Provides test fixture loaders, dataset registries, and container execution runners.
4
+ """
5
+
6
+ from .datasets import list_catalog, load_dataset, resolve_image
7
+
8
+ __all__ = ["load_dataset", "resolve_image", "list_catalog"]
@@ -0,0 +1,187 @@
1
+ """
2
+ Runtime-to-UI Functional Equivalence Comparator Engine (Sprint 381).
3
+ Governing Invariants: INV-COORD-01, INV-COORD-02, INV-MCARD-05, INV-REF-01, INV-REF-04.
4
+
5
+ Implements:
6
+ 1. GatewayDriver: JSON-RPC 2.0 interface to headless CLM runtime.
7
+ 2. PlaywrightStateExtractor: Extracts MCard representation from UI Viewlets.
8
+ 3. StateMeetValidator: Mathematical meet computation Delta(M_runtime, M_UI).
9
+ 4. SynestheticLatencyBenchmark: High-resolution perceptual coupling timer (p95 <= 100ms).
10
+ """
11
+
12
+ import copy
13
+ import hashlib
14
+ import json
15
+ import statistics
16
+ import time
17
+ from typing import Any
18
+
19
+ try:
20
+ from clm.layer0.hash import compute_blake3
21
+ except ImportError:
22
+
23
+ def compute_blake3(data: bytes | str) -> str:
24
+ if isinstance(data, str):
25
+ data = data.encode("utf-8")
26
+ return hashlib.sha256(data).hexdigest()
27
+
28
+
29
+ class GatewayDriver:
30
+ """
31
+ JSON-RPC 2.0 client communicating with CLM runtime over Presentation Membrane SPI.
32
+ Operates in live WebSocket mode or deterministic mock headless CAS mode.
33
+ """
34
+
35
+ def __init__(self, endpoint: str = "ws://localhost:5321", mock_mode: bool = True):
36
+ self.endpoint = endpoint
37
+ self.mock_mode = mock_mode
38
+ self._mock_cas: dict[str, dict[str, Any]] = {}
39
+ self._event_listeners: list[Any] = []
40
+
41
+ def ping(self) -> dict[str, Any]:
42
+ """Performs ping-pong latency check."""
43
+ start = time.perf_counter()
44
+ # Simulated or socket ping
45
+ elapsed = (time.perf_counter() - start) * 1000
46
+ return {"jsonrpc": "2.0", "result": "pong", "latency_ms": round(elapsed, 3)}
47
+
48
+ def create_card(
49
+ self, uri: str, content: Any, metadata: dict[str, Any] | None = None
50
+ ) -> dict[str, Any]:
51
+ """Dispatches card.create RPC."""
52
+ card_hash = compute_blake3(json.dumps(content, sort_keys=True).encode("utf-8"))
53
+
54
+ card_data = {
55
+ "uri": uri,
56
+ "hash": card_hash,
57
+ "content": content,
58
+ "tags": (metadata or {}).get("tags", ["crdt", "verified"]),
59
+ "handles": (metadata or {}).get("handles", [uri]),
60
+ "generation_time": (metadata or {}).get(
61
+ "generation_time", "2026-09-19T10:00:00Z"
62
+ ),
63
+ }
64
+ self._mock_cas[uri] = copy.deepcopy(card_data)
65
+ return {"jsonrpc": "2.0", "result": card_data}
66
+
67
+ def get_card(self, uri: str) -> dict[str, Any] | None:
68
+ """Queries card.get from CAS."""
69
+ return copy.deepcopy(self._mock_cas.get(uri))
70
+
71
+ def step_cpn_token(self, place_id: str, delta: int) -> dict[str, Any]:
72
+ """Dispatches transition.step RPC."""
73
+ return {
74
+ "jsonrpc": "2.0",
75
+ "result": {
76
+ "place_id": place_id,
77
+ "delta": delta,
78
+ "token_conservation": "xᵀ · D = 0 [VALID]",
79
+ },
80
+ }
81
+
82
+
83
+ class PlaywrightStateExtractor:
84
+ """
85
+ Simulates / queries Playwright headless Chromium DOM to extract active viewlet state
86
+ into canonical MCard dictionaries.
87
+ """
88
+
89
+ @staticmethod
90
+ def extract_from_dom_attributes(dom_node: dict[str, Any]) -> dict[str, Any]:
91
+ """
92
+ Parses HTML5 data-* attributes and DOM text content into a canonical MCard dict.
93
+ """
94
+ return {
95
+ "uri": dom_node.get("data-card-uri"),
96
+ "hash": dom_node.get("data-card-hash"),
97
+ "content": dom_node.get("content"),
98
+ "tags": dom_node.get("data-tags", []),
99
+ "handles": dom_node.get("data-handles", []),
100
+ "generation_time": dom_node.get("data-generation-time"),
101
+ }
102
+
103
+
104
+ class StateMeetValidator:
105
+ """
106
+ Computes mathematical meet comparison Delta(M_runtime, M_UI).
107
+ Zero diff implies natural isomorphism (M_runtime ≅ M_UI).
108
+ """
109
+
110
+ CRITICAL_FIELDS = ["uri", "hash", "content", "tags", "handles", "generation_time"]
111
+
112
+ @classmethod
113
+ def compute_diff(
114
+ cls, runtime_card: dict[str, Any], ui_card: dict[str, Any]
115
+ ) -> tuple[bool, dict[str, Any]]:
116
+ """
117
+ Returns (is_identical, discrepancies).
118
+ Discrepancies dictionary contains mismatched field values.
119
+ """
120
+ discrepancies: dict[str, Any] = {}
121
+ for field in cls.CRITICAL_FIELDS:
122
+ r_val = runtime_card.get(field)
123
+ u_val = ui_card.get(field)
124
+ if r_val != u_val:
125
+ discrepancies[field] = {
126
+ "runtime": r_val,
127
+ "ui": u_val,
128
+ }
129
+
130
+ is_identical = len(discrepancies) == 0
131
+ return is_identical, discrepancies
132
+
133
+
134
+ class SynestheticLatencyBenchmark:
135
+ """
136
+ Benchmarks sub-100ms human-agent perceptual coupling bound (p95 <= 100ms).
137
+ """
138
+
139
+ @staticmethod
140
+ def run_benchmark(iterations: int = 100) -> dict[str, Any]:
141
+ driver = GatewayDriver(mock_mode=True)
142
+ latencies: list[float] = []
143
+
144
+ for i in range(iterations):
145
+ t0 = time.perf_counter()
146
+ # 1. Dispatch card mutation in CLM
147
+ res = driver.create_card(f"mcard://benchmark/{i}", {"step": i})
148
+ card_uri = res["result"]["uri"]
149
+
150
+ # 2. Query CLM CAS
151
+ r_card = driver.get_card(card_uri)
152
+
153
+ # 3. Simulate UI DOM reflection & extraction
154
+ dom_element = {
155
+ "data-card-uri": r_card["uri"],
156
+ "data-card-hash": r_card["hash"],
157
+ "content": r_card["content"],
158
+ "data-tags": r_card["tags"],
159
+ "data-handles": r_card["handles"],
160
+ "data-generation-time": r_card["generation_time"],
161
+ }
162
+ ui_card = PlaywrightStateExtractor.extract_from_dom_attributes(dom_element)
163
+
164
+ # 4. Assert State Meet Equivalence
165
+ identical, _ = StateMeetValidator.compute_diff(r_card, ui_card)
166
+ assert identical, "State meet mismatch during latency benchmark"
167
+
168
+ t1 = time.perf_counter()
169
+ latencies.append((t1 - t0) * 1000)
170
+
171
+ latencies.sort()
172
+ p50 = statistics.median(latencies)
173
+ p90 = latencies[int(0.90 * len(latencies))]
174
+ p95 = latencies[int(0.95 * len(latencies))]
175
+ p99 = latencies[int(0.99 * len(latencies))]
176
+
177
+ return {
178
+ "iterations": iterations,
179
+ "min_ms": round(min(latencies), 3),
180
+ "max_ms": round(max(latencies), 3),
181
+ "mean_ms": round(statistics.mean(latencies), 3),
182
+ "p50_ms": round(p50, 3),
183
+ "p90_ms": round(p90, 3),
184
+ "p95_ms": round(p95, 3),
185
+ "p99_ms": round(p99, 3),
186
+ "p95_compliant": bool(p95 <= 100.0),
187
+ }
@@ -0,0 +1,87 @@
1
+ """
2
+ Centralized Test Dataset & Container Image Registry Loader (DV-380-02, INV-COORD-04).
3
+
4
+ Provides deterministic programmatic access to registered DatasetMCard and ImageMCard
5
+ assets cataloged in tests/test_data/catalog.json.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ from functools import lru_cache
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ def _find_catalog_path() -> Path:
16
+ for env_var in ("MCARD_ROOT", "CLM_ROOT", "PROJECT_ROOT"):
17
+ if env_var in os.environ:
18
+ candidate = Path(os.environ[env_var]).resolve() / "tests" / "test_data" / "catalog.json"
19
+ if candidate.exists():
20
+ return candidate
21
+ curr = Path(__file__).resolve()
22
+ for parent in [curr] + list(curr.parents):
23
+ candidate = parent / "tests" / "test_data" / "catalog.json"
24
+ if candidate.exists():
25
+ return candidate
26
+ cwd = Path.cwd().resolve()
27
+ for parent in [cwd] + list(cwd.parents):
28
+ candidate = parent / "tests" / "test_data" / "catalog.json"
29
+ if candidate.exists():
30
+ return candidate
31
+ return curr.parents[5] / "tests" / "test_data" / "catalog.json"
32
+
33
+ CATALOG_PATH = _find_catalog_path()
34
+ REPO_ROOT = CATALOG_PATH.parent.parent.parent
35
+
36
+
37
+ @lru_cache(maxsize=1)
38
+ def _load_raw_catalog() -> dict[str, Any]:
39
+ if not CATALOG_PATH.exists():
40
+ raise FileNotFoundError(f"Test dataset catalog missing at: {CATALOG_PATH}")
41
+ with open(CATALOG_PATH, encoding="utf-8") as f:
42
+ return json.load(f)
43
+
44
+
45
+ def list_catalog() -> dict[str, Any]:
46
+ """Returns the entire registered test catalog."""
47
+ return _load_raw_catalog()
48
+
49
+
50
+ def list_datasets() -> list[dict[str, Any]]:
51
+ """Lists all registered DatasetMCard specifications."""
52
+ return _load_raw_catalog().get("datasets", [])
53
+
54
+
55
+ def list_images() -> list[dict[str, Any]]:
56
+ """Lists all registered ImageMCard specifications."""
57
+ return _load_raw_catalog().get("images", [])
58
+
59
+
60
+ def load_dataset(dataset_id: str) -> dict[str, Any]:
61
+ """
62
+ Loads and resolves a registered DatasetMCard by dataset_id.
63
+ Raises KeyError if dataset is not found in catalog.
64
+ """
65
+ datasets = list_datasets()
66
+ for ds in datasets:
67
+ if ds.get("dataset_id") == dataset_id:
68
+ return dict(ds)
69
+ raise KeyError(
70
+ f"DatasetMCard '{dataset_id}' not found in catalog. Available: {[d.get('dataset_id') for d in datasets]}"
71
+ )
72
+
73
+
74
+ def resolve_image(image_name: str) -> dict[str, Any]:
75
+ """
76
+ Loads and resolves a registered ImageMCard by image_name.
77
+ Raises KeyError if image is not found in catalog.
78
+ """
79
+ images = list_images()
80
+ for img in images:
81
+ if img.get("image_name") == image_name or img.get(
82
+ "image_name"
83
+ ) == image_name.replace("_", "-"):
84
+ return dict(img)
85
+ raise KeyError(
86
+ f"ImageMCard '{image_name}' not found in catalog. Available: {[i.get('image_name') for i in images]}"
87
+ )
@@ -0,0 +1,20 @@
1
+ """Shared time and agent-DID helpers (Sprint 132.4).
2
+
3
+ Single source of truth for the timestamp and agent_did patterns that were
4
+ duplicated inline across mcard.py, vcard.py, telemetry.py, clm_logger.py.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import time
11
+
12
+
13
+ def now_g_time() -> str:
14
+ """Canonical ISO-8601 timestamp."""
15
+ return time.strftime("%Y-%m-%dT%H:%M:%S%z")
16
+
17
+
18
+ def default_agent_did() -> str:
19
+ """Canonical agent DID from environment."""
20
+ return os.environ.get("CLM_AGENT_DID", "did:clm:bootstrap")
File without changes
@@ -0,0 +1 @@
1
+ """MCard utility modules."""
@@ -0,0 +1,50 @@
1
+ """
2
+ URL safety utilities for validating URL schemes before opening.
3
+
4
+ Addresses Bandit B310: Audit url open for permitted schemes.
5
+ """
6
+
7
+ import urllib.request
8
+ from urllib.parse import urlparse
9
+
10
+ # Only allow HTTP(S) schemes to prevent file:// and other protocol attacks.
11
+ ALLOWED_SCHEMES = frozenset({"http", "https"})
12
+
13
+
14
+ def safe_urlopen(url_or_request, *, timeout=None, allowed_schemes=None):
15
+ """
16
+ Open a URL after validating the scheme is in the allowlist.
17
+
18
+ Accepts either a string URL or a urllib.request.Request object.
19
+
20
+ Args:
21
+ url_or_request: URL string or urllib.request.Request to open.
22
+ timeout: Optional timeout in seconds.
23
+ allowed_schemes: Optional frozenset of allowed schemes.
24
+ Defaults to {"http", "https"}.
25
+
26
+ Returns:
27
+ The response object from urllib.request.urlopen.
28
+
29
+ Raises:
30
+ ValueError: If the URL scheme is not in the allowlist.
31
+ """
32
+ schemes = allowed_schemes or ALLOWED_SCHEMES
33
+
34
+ if isinstance(url_or_request, urllib.request.Request):
35
+ url = url_or_request.full_url
36
+ else:
37
+ url = url_or_request
38
+
39
+ parsed = urlparse(url)
40
+ if parsed.scheme not in schemes:
41
+ raise ValueError(
42
+ f"URL scheme '{parsed.scheme}' is not allowed. "
43
+ f"Permitted schemes: {sorted(schemes)}"
44
+ )
45
+
46
+ kwargs = {}
47
+ if timeout is not None:
48
+ kwargs["timeout"] = timeout
49
+
50
+ return urllib.request.urlopen(url_or_request, **kwargs) # nosec B310