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,519 @@
1
+ """Pocket Flow Graph Scheduling & Monadic Lifecycle (DEL-388-01, INV-388-01..04).
2
+
3
+ Implements the category-theoretic Pocket Flow execution lifecycle:
4
+ Phase 1: prep() — Applicative Functor (<*>), parallel precondition gates
5
+ Phase 2: exec() — Polynomial Functor (A -> B), isolated computation
6
+ Phase 3: post() — Monadic Bind (>>=), output-dependent invariant checks with rollback
7
+
8
+ Enforces Noetherian token conservation (x^T · D = 0) on every transition firing
9
+ and raises NoetherErr on conservation violation.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import asyncio
14
+ import copy
15
+ import logging
16
+ from dataclasses import asdict, dataclass, field
17
+ from typing import Any, Callable
18
+
19
+ from clm.layer0.exceptions import CLMError
20
+ from clm.layer1.petri import NoetherErr
21
+ from clm.layer1.store import PTRSharedStore
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class PostGateRollbackError(CLMError):
27
+ """Raised when Monadic post-gate verification collapses to ⊥, triggering rollback."""
28
+ pass
29
+
30
+
31
+ @dataclass
32
+ class PrepResult:
33
+ """Result of the Applicative Pre-Gate (<*>)."""
34
+ success: bool
35
+ error: str | None = None
36
+ context: dict[str, Any] = field(default_factory=dict)
37
+
38
+
39
+ @dataclass
40
+ class ExecResult:
41
+ """Result of the Polynomial Functor execution (A -> B)."""
42
+ success: bool
43
+ output: Any = None
44
+ error: str | None = None
45
+
46
+
47
+ @dataclass
48
+ class PostResult:
49
+ """Result of the Monadic Post-Gate (>>=)."""
50
+ success: bool
51
+ error: str | None = None
52
+ witness: Any = None
53
+
54
+
55
+ class PrepPhase:
56
+ """Applicative Pre-Gate (<*>).
57
+
58
+ Evaluates independent preconditions concurrently without observing
59
+ or depending on unproduced outputs.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ checks: list[Callable[[Any, dict[str, Any]], bool | tuple[bool, str]]]
65
+ | Callable[[Any, dict[str, Any]], bool | tuple[bool, str]]
66
+ | None = None,
67
+ ):
68
+ if checks is None:
69
+ self.checks = []
70
+ elif isinstance(checks, list):
71
+ self.checks = checks
72
+ else:
73
+ self.checks = [checks]
74
+
75
+ def add_check(self, check: Callable[[Any, dict[str, Any]], bool | tuple[bool, str]]) -> None:
76
+ self.checks.append(check)
77
+
78
+ def evaluate(self, store: Any, context: dict[str, Any] | None = None) -> PrepResult:
79
+ ctx = context or {}
80
+ for check in self.checks:
81
+ try:
82
+ res = check(store, ctx)
83
+ if isinstance(res, tuple):
84
+ passed, msg = res
85
+ if not passed:
86
+ return PrepResult(success=False, error=msg, context=ctx)
87
+ elif not res:
88
+ return PrepResult(success=False, error="Precondition check failed", context=ctx)
89
+ except Exception as e:
90
+ return PrepResult(success=False, error=f"Precondition exception: {e}", context=ctx)
91
+ return PrepResult(success=True, context=ctx)
92
+
93
+ async def evaluate_async(self, store: Any, context: dict[str, Any] | None = None) -> PrepResult:
94
+ """Evaluate all preconditions concurrently as pure Applicative Functor (<*>)."""
95
+ ctx = context or {}
96
+ if not self.checks:
97
+ return PrepResult(success=True, context=ctx)
98
+
99
+ loop = asyncio.get_running_loop()
100
+
101
+ def _run_sync(c):
102
+ return c(store, ctx)
103
+
104
+ tasks = [loop.run_in_executor(None, _run_sync, c) for c in self.checks]
105
+ results = await asyncio.gather(*tasks, return_exceptions=True)
106
+
107
+ for res in results:
108
+ if isinstance(res, Exception):
109
+ return PrepResult(success=False, error=str(res), context=ctx)
110
+ if isinstance(res, tuple):
111
+ passed, msg = res
112
+ if not passed:
113
+ return PrepResult(success=False, error=msg, context=ctx)
114
+ elif not res:
115
+ return PrepResult(success=False, error="Precondition check failed", context=ctx)
116
+
117
+ return PrepResult(success=True, context=ctx)
118
+
119
+
120
+ class ExecPhase:
121
+ """Polynomial Functor (A -> B).
122
+
123
+ Morphism computing isolated output from inputs within the declared context.
124
+ """
125
+
126
+ def __init__(self, execute_fn: Callable[[Any, Any, dict[str, Any]], Any] | None = None):
127
+ self.execute_fn = execute_fn
128
+
129
+ def evaluate(self, inputs: Any, store: Any, context: dict[str, Any] | None = None) -> ExecResult:
130
+ ctx = context or {}
131
+ if self.execute_fn is None:
132
+ return ExecResult(success=True, output=inputs)
133
+ try:
134
+ output = self.execute_fn(inputs, store, ctx)
135
+ return ExecResult(success=True, output=output)
136
+ except Exception as e:
137
+ logger.error(f"[ExecPhase] Computation error: {e}", exc_info=True)
138
+ return ExecResult(success=False, error=str(e))
139
+
140
+
141
+ class PostPhase:
142
+ """Monadic Post-Gate (>>=).
143
+
144
+ Output-dependent invariant inspection. If verification fails (Meet = ⊥),
145
+ triggers transactional rollback.
146
+ """
147
+
148
+ def __init__(self, verify_fn: Callable[[Any, Any, dict[str, Any]], bool | tuple[bool, str]] | None = None):
149
+ self.verify_fn = verify_fn
150
+
151
+ def evaluate(self, output: Any, store: Any, context: dict[str, Any] | None = None) -> PostResult:
152
+ ctx = context or {}
153
+ if self.verify_fn is None:
154
+ return PostResult(success=True, witness={"status": "default_pass"})
155
+ try:
156
+ res = self.verify_fn(output, store, ctx)
157
+ if isinstance(res, tuple):
158
+ passed, msg = res
159
+ if not passed:
160
+ return PostResult(success=False, error=msg)
161
+ return PostResult(success=True, witness={"message": msg})
162
+ elif not res:
163
+ return PostResult(success=False, error="Monadic post-condition check collapsed to ⊥")
164
+ return PostResult(success=True, witness={"verified": True})
165
+ except Exception as e:
166
+ return PostResult(success=False, error=f"Postcondition exception: {e}")
167
+
168
+
169
+ @dataclass
170
+ class DAGNode:
171
+ """A node in the Pocket Flow DAG, isomorphic to a CPN Transition (t in T)."""
172
+ id: str
173
+ prep: PrepPhase | Callable | list[Callable] | None = None
174
+ exec: ExecPhase | Callable | None = None
175
+ post: PostPhase | Callable | None = None
176
+ dependencies: list[str] = field(default_factory=list)
177
+ inputs: list[str] = field(default_factory=list)
178
+ outputs: list[str] = field(default_factory=list)
179
+ tokens_consumed: int = 1
180
+ tokens_produced: int = 1
181
+ metadata: dict[str, Any] = field(default_factory=dict)
182
+
183
+ def __post_init__(self):
184
+ # Normalize prep
185
+ if not isinstance(self.prep, PrepPhase):
186
+ self.prep = PrepPhase(self.prep)
187
+ # Normalize exec
188
+ if not isinstance(self.exec, ExecPhase):
189
+ self.exec = ExecPhase(self.exec)
190
+ # Normalize post
191
+ if not isinstance(self.post, PostPhase):
192
+ self.post = PostPhase(self.post)
193
+
194
+
195
+ @dataclass
196
+ class SchedulerResult:
197
+ """Aggregated result from executing a Pocket Flow DAG."""
198
+ success: bool
199
+ node_results: dict[str, Any] = field(default_factory=dict)
200
+ store: Any = None
201
+ error: str | None = None
202
+ telemetry: dict[str, Any] = field(default_factory=dict)
203
+
204
+
205
+ class PocketFlowScheduler:
206
+ """Coordinates multi-node agentic DAG execution using the Pocket Flow lifecycle.
207
+
208
+ Guarantees:
209
+ - INV-388-01: Applicative prep (<*>) and Monadic post (>>=) typeclass asymmetry.
210
+ - INV-388-02: Noetherian token conservation (x^T · D = 0^T) across transition firings.
211
+ - Transactional rollback on post-phase failure.
212
+ """
213
+
214
+ def __init__(self, nodes: list[DAGNode] | None = None):
215
+ self.nodes: dict[str, DAGNode] = {}
216
+ if nodes:
217
+ for n in nodes:
218
+ self.add_node(n)
219
+
220
+ def add_node(self, node: DAGNode) -> None:
221
+ self.nodes[node.id] = node
222
+
223
+ def add_nodes(self, nodes: list[DAGNode]) -> None:
224
+ for n in nodes:
225
+ self.add_node(n)
226
+
227
+ def topological_sort(self) -> list[DAGNode]:
228
+ """Compute topological order of DAG nodes."""
229
+ in_degree: dict[str, int] = dict.fromkeys(self.nodes, 0)
230
+ adj: dict[str, list[str]] = {nid: [] for nid in self.nodes}
231
+
232
+ for nid, node in self.nodes.items():
233
+ for dep in node.dependencies:
234
+ if dep in self.nodes:
235
+ adj[dep].append(nid)
236
+ in_degree[nid] += 1
237
+
238
+ queue = [nid for nid, deg in in_degree.items() if deg == 0]
239
+ order = []
240
+
241
+ while queue:
242
+ curr = queue.pop(0)
243
+ order.append(self.nodes[curr])
244
+ for neighbor in adj[curr]:
245
+ in_degree[neighbor] -= 1
246
+ if in_degree[neighbor] == 0:
247
+ queue.append(neighbor)
248
+
249
+ if len(order) != len(self.nodes):
250
+ raise CLMError(f"PocketFlowScheduler cycle detected: graph contains a cycle among {[nid for nid, deg in in_degree.items() if deg > 0]}")
251
+
252
+ return order
253
+
254
+ def _snapshot_store(self, store: Any) -> Any:
255
+ """Create an isolated snapshot of store state for atomic rollback."""
256
+ if isinstance(store, PTRSharedStore):
257
+ snap = PTRSharedStore()
258
+ snap.inputs = copy.deepcopy(store.inputs)
259
+ snap.outputs = copy.deepcopy(store.outputs)
260
+ snap.intermediate = copy.deepcopy(store.intermediate)
261
+ snap.auth_tokens = copy.deepcopy(store.auth_tokens)
262
+ snap.caller_did = store.caller_did
263
+ snap.target_did = store.target_did
264
+ snap.gas_budget = store.gas_budget
265
+ snap.gas_used = store.gas_used
266
+ snap.pcard_hash = store.pcard_hash
267
+ snap.target_hash = store.target_hash
268
+ return snap
269
+ elif isinstance(store, dict):
270
+ return copy.deepcopy(store)
271
+ return copy.deepcopy(store)
272
+
273
+ def _restore_store(self, target_store: Any, snapshot: Any) -> None:
274
+ """Restore store from snapshot."""
275
+ if isinstance(target_store, PTRSharedStore) and isinstance(snapshot, PTRSharedStore):
276
+ target_store.inputs = copy.deepcopy(snapshot.inputs)
277
+ target_store.outputs = copy.deepcopy(snapshot.outputs)
278
+ target_store.intermediate = copy.deepcopy(snapshot.intermediate)
279
+ target_store.auth_tokens = copy.deepcopy(snapshot.auth_tokens)
280
+ target_store.caller_did = snapshot.caller_did
281
+ target_store.target_did = snapshot.target_did
282
+ target_store.gas_budget = snapshot.gas_budget
283
+ target_store.gas_used = snapshot.gas_used
284
+ elif isinstance(target_store, dict) and isinstance(snapshot, dict):
285
+ target_store.clear()
286
+ target_store.update(copy.deepcopy(snapshot))
287
+
288
+ def _get_store_val(self, store: Any, key: str) -> Any:
289
+ if isinstance(store, PTRSharedStore) or isinstance(store, dict):
290
+ return store.get(key)
291
+ return None
292
+
293
+ def _set_store_val(self, store: Any, key: str, val: Any) -> None:
294
+ if isinstance(store, PTRSharedStore):
295
+ store.set(key, val)
296
+ elif isinstance(store, dict):
297
+ store[key] = val
298
+
299
+ def verify_noetherian_invariants(
300
+ self, node: DAGNode, token_markings: dict[str, int]
301
+ ) -> None:
302
+ """Assert Noetherian token conservation (x^T · D = 0) for transition firing.
303
+
304
+ Raises NoetherErr if input tokens are insufficient or consumed != produced.
305
+ """
306
+ # 1. Input token availability check: M >= D^-
307
+ for inp in node.inputs:
308
+ current = token_markings.get(inp, 0)
309
+ if current < node.tokens_consumed:
310
+ raise NoetherErr(
311
+ f"Noetherian Invariant Violation on transition '{node.id}': "
312
+ f"Place '{inp}' has {current} tokens, but transition requires {node.tokens_consumed}."
313
+ )
314
+
315
+ # 2. Token conservation balance check: sum(D) = 0 for standard closed transitions
316
+ delta = node.tokens_produced - node.tokens_consumed
317
+ if delta != 0 and node.metadata.get("enforce_conservation", True):
318
+ raise NoetherErr(
319
+ f"Noetherian Invariant Violation on transition '{node.id}': "
320
+ f"Conservation x^T · D = 0 violated (consumed {node.tokens_consumed}, produced {node.tokens_produced}, delta = {delta})."
321
+ )
322
+
323
+ def execute(
324
+ self, store: Any = None, context: dict[str, Any] | None = None
325
+ ) -> SchedulerResult:
326
+ """Synchronously execute the Pocket Flow DAG."""
327
+ if store is None:
328
+ store = PTRSharedStore()
329
+ ctx = context or {}
330
+ order = self.topological_sort()
331
+
332
+ # Initialize Petri Net token markings from store inputs
333
+ token_markings: dict[str, int] = {}
334
+ for nid, node in self.nodes.items():
335
+ for inp in node.inputs:
336
+ if inp not in token_markings:
337
+ val = self._get_store_val(store, f"{inp}_tokens")
338
+ if val is None:
339
+ val = self._get_store_val(store, f"{inp}")
340
+ if val is None:
341
+ val = 1
342
+ token_markings[inp] = int(val) if isinstance(val, (int, float, str)) and str(val).isdigit() else 1
343
+ for outp in node.outputs:
344
+ if outp not in token_markings:
345
+ val = self._get_store_val(store, f"{outp}_tokens")
346
+ if val is None:
347
+ val = 0
348
+ token_markings[outp] = int(val) if isinstance(val, (int, float, str)) and str(val).isdigit() else 0
349
+
350
+ node_results: dict[str, Any] = {}
351
+
352
+ for node in order:
353
+ snapshot = self._snapshot_store(store)
354
+ logger.debug(f"[PocketFlowScheduler] Firing node '{node.id}'")
355
+
356
+ # Invariant check: Noetherian token conservation prior to execution
357
+ self.verify_noetherian_invariants(node, token_markings)
358
+
359
+ # Phase 1: prep (Applicative Functor <*>)
360
+ prep_res = node.prep.evaluate(store, ctx)
361
+ if not prep_res.success:
362
+ err_msg = f"Node '{node.id}' prep phase failed: {prep_res.error}"
363
+ logger.warning(err_msg)
364
+ return SchedulerResult(
365
+ success=False,
366
+ node_results=node_results,
367
+ store=store,
368
+ error=err_msg,
369
+ )
370
+
371
+ # Resolve input token payload from store inputs
372
+ input_payload = None
373
+ if node.inputs:
374
+ input_payload = {inp: self._get_store_val(store, inp) for inp in node.inputs}
375
+ if len(node.inputs) == 1:
376
+ input_payload = input_payload[node.inputs[0]]
377
+
378
+ # Phase 2: exec (Polynomial Functor A -> B)
379
+ exec_res = node.exec.evaluate(input_payload, store, ctx)
380
+ if not exec_res.success:
381
+ self._restore_store(store, snapshot)
382
+ err_msg = f"Node '{node.id}' exec phase failed: {exec_res.error}"
383
+ logger.warning(err_msg)
384
+ return SchedulerResult(
385
+ success=False,
386
+ node_results=node_results,
387
+ store=store,
388
+ error=err_msg,
389
+ )
390
+
391
+ # Phase 3: post (Monadic Bind >>=)
392
+ post_res = node.post.evaluate(exec_res.output, store, ctx)
393
+ if not post_res.success:
394
+ # Atomically rollback store to pre-node state (<1ms)
395
+ self._restore_store(store, snapshot)
396
+ err_msg = f"Node '{node.id}' post-gate failed (Meet = ⊥): {post_res.error}"
397
+ logger.warning(f"[ROLLBACK] {err_msg}")
398
+ return SchedulerResult(
399
+ success=False,
400
+ node_results=node_results,
401
+ store=store,
402
+ error=err_msg,
403
+ )
404
+
405
+ # Update token markings upon successful firing: M' = M + D · y
406
+ for inp in node.inputs:
407
+ token_markings[inp] -= node.tokens_consumed
408
+ for outp in node.outputs:
409
+ token_markings[outp] += node.tokens_produced
410
+ # Commit output value to store
411
+ self._set_store_val(store, outp, exec_res.output)
412
+
413
+ # Record node execution result
414
+ node_results[node.id] = {
415
+ "prep": asdict(prep_res),
416
+ "exec": asdict(exec_res),
417
+ "post": asdict(post_res),
418
+ "output": exec_res.output,
419
+ }
420
+
421
+ return SchedulerResult(
422
+ success=True,
423
+ node_results=node_results,
424
+ store=store,
425
+ telemetry={"executed_nodes": len(order), "final_markings": token_markings},
426
+ )
427
+
428
+ async def execute_async(
429
+ self, store: Any = None, context: dict[str, Any] | None = None
430
+ ) -> SchedulerResult:
431
+ """Asynchronously execute the Pocket Flow DAG with parallel Applicative prep."""
432
+ if store is None:
433
+ store = PTRSharedStore()
434
+ ctx = context or {}
435
+ order = self.topological_sort()
436
+
437
+ token_markings: dict[str, int] = {}
438
+ for nid, node in self.nodes.items():
439
+ for inp in node.inputs:
440
+ if inp not in token_markings:
441
+ val = self._get_store_val(store, f"{inp}_tokens")
442
+ if val is None:
443
+ val = self._get_store_val(store, f"{inp}")
444
+ if val is None:
445
+ val = 1
446
+ token_markings[inp] = int(val) if isinstance(val, (int, float, str)) and str(val).isdigit() else 1
447
+ for outp in node.outputs:
448
+ if outp not in token_markings:
449
+ val = self._get_store_val(store, f"{outp}_tokens")
450
+ if val is None:
451
+ val = 0
452
+ token_markings[outp] = int(val) if isinstance(val, (int, float, str)) and str(val).isdigit() else 0
453
+
454
+ node_results: dict[str, Any] = {}
455
+
456
+ for node in order:
457
+ snapshot = self._snapshot_store(store)
458
+ self.verify_noetherian_invariants(node, token_markings)
459
+
460
+ # Phase 1: Async Applicative prep (<*>)
461
+ prep_res = await node.prep.evaluate_async(store, ctx)
462
+ if not prep_res.success:
463
+ err_msg = f"Node '{node.id}' prep phase failed: {prep_res.error}"
464
+ return SchedulerResult(success=False, node_results=node_results, store=store, error=err_msg)
465
+
466
+ input_payload = None
467
+ if node.inputs:
468
+ input_payload = {inp: self._get_store_val(store, inp) for inp in node.inputs}
469
+ if len(node.inputs) == 1:
470
+ input_payload = input_payload[node.inputs[0]]
471
+
472
+ # Phase 2: exec
473
+ loop = asyncio.get_running_loop()
474
+ exec_res = await loop.run_in_executor(None, node.exec.evaluate, input_payload, store, ctx)
475
+ if not exec_res.success:
476
+ self._restore_store(store, snapshot)
477
+ err_msg = f"Node '{node.id}' exec phase failed: {exec_res.error}"
478
+ return SchedulerResult(success=False, node_results=node_results, store=store, error=err_msg)
479
+
480
+ # Phase 3: post
481
+ post_res = node.post.evaluate(exec_res.output, store, ctx)
482
+ if not post_res.success:
483
+ self._restore_store(store, snapshot)
484
+ err_msg = f"Node '{node.id}' post-gate failed (Meet = ⊥): {post_res.error}"
485
+ return SchedulerResult(success=False, node_results=node_results, store=store, error=err_msg)
486
+
487
+ for inp in node.inputs:
488
+ token_markings[inp] -= node.tokens_consumed
489
+ for outp in node.outputs:
490
+ token_markings[outp] += node.tokens_produced
491
+ self._set_store_val(store, outp, exec_res.output)
492
+
493
+ node_results[node.id] = {
494
+ "prep": asdict(prep_res),
495
+ "exec": asdict(exec_res),
496
+ "post": asdict(post_res),
497
+ "output": exec_res.output,
498
+ }
499
+
500
+ return SchedulerResult(
501
+ success=True,
502
+ node_results=node_results,
503
+ store=store,
504
+ telemetry={"executed_nodes": len(order), "final_markings": token_markings},
505
+ )
506
+
507
+
508
+ __all__ = [
509
+ "PrepResult",
510
+ "ExecResult",
511
+ "PostResult",
512
+ "PrepPhase",
513
+ "ExecPhase",
514
+ "PostPhase",
515
+ "DAGNode",
516
+ "PocketFlowScheduler",
517
+ "SchedulerResult",
518
+ "PostGateRollbackError",
519
+ ]