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,274 @@
1
+ """Declarative Workflow Manifest Domain Models (Sprint 395 / DEL-395-01).
2
+
3
+ Implements Pydantic V2 domain models for canonical CLM Workflow specifications (clm.workflow.v1).
4
+ A Workflow represents a Category-Theoretic Monadic Composition (Kleisli Category)
5
+ chaining multiple Actions algebraically across the Pocket Flow Scheduler.
6
+
7
+ Zero dependencies on Layer 1 through Layer 5.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import re
14
+ from pathlib import Path
15
+ from typing import Any, Literal
16
+
17
+ import yaml
18
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
19
+
20
+ from clm.layer0.exceptions import CLMError
21
+
22
+
23
+ class CyclicDependencyError(CLMError):
24
+ """Raised when a workflow contains a circular dependency between steps."""
25
+ pass
26
+
27
+
28
+ class WorkflowValidationError(CLMError):
29
+ """Raised when static validation of a workflow manifest fails (e.g. dangling reference)."""
30
+ pass
31
+
32
+
33
+ class WorkflowMetadata(BaseModel):
34
+ """Metadata block identifying the workflow and its role in CLM."""
35
+
36
+ model_config = ConfigDict(frozen=True, extra="forbid")
37
+
38
+ type: Literal["workflow", "pocketflow_dag"] = "workflow"
39
+ name: str = Field(..., pattern=r"^[a-z0-9_\-]+$")
40
+ description: str | None = None
41
+ author: str = "did:clm:system"
42
+
43
+
44
+ class WorkflowInput(BaseModel):
45
+ """Specification of an input parameter to the workflow."""
46
+
47
+ model_config = ConfigDict(frozen=True, extra="forbid")
48
+
49
+ name: str
50
+ format: str | None = None
51
+ description: str | None = None
52
+
53
+
54
+ class WorkflowStep(BaseModel):
55
+ """A discrete Action step in the workflow DAG."""
56
+
57
+ model_config = ConfigDict(frozen=True, extra="forbid")
58
+
59
+ id: str = Field(..., pattern=r"^[a-z0-9_\-]+$")
60
+ action: str
61
+ inputs: dict[str, str] = Field(default_factory=dict)
62
+ depends_on: list[str] = Field(default_factory=list)
63
+
64
+ def extract_implicit_dependencies(self) -> set[str]:
65
+ """Extract referenced step IDs from input binding expressions ($step_id.output...)."""
66
+ deps: set[str] = set()
67
+ step_ref_pattern = re.compile(r"^\$([a-z0-9_\-]+)\.output")
68
+ for val in self.inputs.values():
69
+ if isinstance(val, str):
70
+ match = step_ref_pattern.match(val.strip())
71
+ if match:
72
+ ref = match.group(1)
73
+ if ref != "workflow":
74
+ deps.add(ref)
75
+ return deps
76
+
77
+ def all_dependencies(self) -> set[str]:
78
+ """Return combined explicit and implicit dependencies for this step."""
79
+ return set(self.depends_on).union(self.extract_implicit_dependencies())
80
+
81
+
82
+ class WorkflowOutput(BaseModel):
83
+ """Specification of an output produced by the workflow."""
84
+
85
+ model_config = ConfigDict(frozen=True, extra="forbid")
86
+
87
+ name: str
88
+ source: str
89
+ format: str | None = None
90
+ description: str | None = None
91
+
92
+
93
+ class WorkflowManifest(BaseModel):
94
+ """Canonical CLM Workflow specification (clm.workflow.v1).
95
+
96
+ Orchestrates multiple Actions in a directed acyclic graph (DAG) with
97
+ strict Kleisli monadic composition (>>>), parallel tensor branching (⊗),
98
+ and Merkle causal lineage.
99
+ """
100
+
101
+ model_config = ConfigDict(frozen=True, extra="forbid")
102
+
103
+ version: Literal["1.0", "1.0.0"] = "1.0"
104
+ clm: WorkflowMetadata
105
+ inputs: list[WorkflowInput] = Field(default_factory=list)
106
+ steps: list[WorkflowStep] = Field(..., min_length=1)
107
+ outputs: list[WorkflowOutput] = Field(default_factory=list)
108
+
109
+ @model_validator(mode="after")
110
+ def validate_dag_and_references(self) -> WorkflowManifest:
111
+ """Validate step uniqueness, references, and assert acyclicity."""
112
+ # 1. Step uniqueness
113
+ step_ids: set[str] = set()
114
+ step_map: dict[str, WorkflowStep] = {}
115
+ for s in self.steps:
116
+ if s.id in step_ids:
117
+ raise WorkflowValidationError(f"Duplicate step id '{s.id}' in workflow '{self.clm.name}'")
118
+ step_ids.add(s.id)
119
+ step_map[s.id] = s
120
+
121
+ # Known input names
122
+ input_names = {inp.name for inp in self.inputs}
123
+
124
+ # 2. Dependency & Reference validation
125
+ adj: dict[str, set[str]] = {sid: set() for sid in step_ids}
126
+
127
+ workflow_inp_pattern = re.compile(r"^\$workflow\.input(?:\.([a-z0-9_\-]+))?$")
128
+ step_out_pattern = re.compile(r"^\$([a-z0-9_\-]+)\.output")
129
+
130
+ for s in self.steps:
131
+ all_deps = s.all_dependencies()
132
+ for dep in all_deps:
133
+ if dep not in step_ids:
134
+ raise WorkflowValidationError(
135
+ f"Step '{s.id}' references unknown dependency or step '{dep}'"
136
+ )
137
+ if dep == s.id:
138
+ raise CyclicDependencyError(f"Step '{s.id}' has self-referential dependency")
139
+ adj[dep].add(s.id)
140
+
141
+ # Check workflow inputs
142
+ for in_key, in_val in s.inputs.items():
143
+ if isinstance(in_val, str):
144
+ wf_match = workflow_inp_pattern.match(in_val.strip())
145
+ if wf_match:
146
+ inp_field = wf_match.group(1)
147
+ if inp_field and input_names and inp_field not in input_names:
148
+ raise WorkflowValidationError(
149
+ f"Step '{s.id}' references undeclared workflow input '{inp_field}'"
150
+ )
151
+
152
+ # 3. Output source validation
153
+ for out in self.outputs:
154
+ match = step_out_pattern.match(out.source.strip())
155
+ if match:
156
+ ref_step = match.group(1)
157
+ if ref_step not in step_ids and ref_step != "workflow":
158
+ raise WorkflowValidationError(
159
+ f"Workflow output '{out.name}' references unknown step '{ref_step}'"
160
+ )
161
+
162
+ # 4. Topological sort & cycle detection (Kahn's algorithm)
163
+ # Compute in-degree from adj (dep -> dependent)
164
+ calc_in_degree: dict[str, int] = dict.fromkeys(step_ids, 0)
165
+ for dep, dependents in adj.items():
166
+ for target in dependents:
167
+ calc_in_degree[target] += 1
168
+
169
+ queue = [sid for sid, deg in calc_in_degree.items() if deg == 0]
170
+ sorted_order: list[str] = []
171
+
172
+ while queue:
173
+ curr = queue.pop(0)
174
+ sorted_order.append(curr)
175
+ for neighbor in adj[curr]:
176
+ calc_in_degree[neighbor] -= 1
177
+ if calc_in_degree[neighbor] == 0:
178
+ queue.append(neighbor)
179
+
180
+ if len(sorted_order) != len(step_ids):
181
+ cycle_nodes = [sid for sid, deg in calc_in_degree.items() if deg > 0]
182
+ raise CyclicDependencyError(
183
+ f"Workflow '{self.clm.name}' contains a circular dependency involving steps: {cycle_nodes}"
184
+ )
185
+
186
+ return self
187
+
188
+ def get_step(self, step_id: str) -> WorkflowStep:
189
+ """Find a step by its identifier."""
190
+ for s in self.steps:
191
+ if s.id == step_id:
192
+ return s
193
+ raise KeyError(f"Step '{step_id}' not found in workflow")
194
+
195
+ def get_topological_order(self) -> list[str]:
196
+ """Compute valid linear execution order of steps."""
197
+ adj: dict[str, set[str]] = {s.id: set() for s in self.steps}
198
+ in_degree: dict[str, int] = {s.id: 0 for s in self.steps}
199
+
200
+ for s in self.steps:
201
+ for dep in s.all_dependencies():
202
+ adj[dep].add(s.id)
203
+ in_degree[s.id] += 1
204
+
205
+ queue = [sid for sid, deg in in_degree.items() if deg == 0]
206
+ order: list[str] = []
207
+
208
+ while queue:
209
+ curr = queue.pop(0)
210
+ order.append(curr)
211
+ for neighbor in adj[curr]:
212
+ in_degree[neighbor] -= 1
213
+ if in_degree[neighbor] == 0:
214
+ queue.append(neighbor)
215
+
216
+ return order
217
+
218
+ def get_execution_stages(self) -> list[list[str]]:
219
+ """Group steps into parallel execution stages based on dependency depth.
220
+
221
+ Steps in the same stage have no mutual dependencies and their
222
+ ancestor dependencies are fulfilled by preceding stages.
223
+ """
224
+ top_order = self.get_topological_order()
225
+ depths: dict[str, int] = {}
226
+ for sid in top_order:
227
+ step = self.get_step(sid)
228
+ deps = step.all_dependencies()
229
+ if not deps:
230
+ depths[sid] = 0
231
+ else:
232
+ depths[sid] = max(depths[d] for d in deps) + 1
233
+
234
+ max_depth = max(depths.values()) if depths else 0
235
+ stages: list[list[str]] = [[] for _ in range(max_depth + 1)]
236
+ for sid in top_order:
237
+ stages[depths[sid]].append(sid)
238
+
239
+ return stages
240
+
241
+ @classmethod
242
+ def from_dict(cls, data: dict[str, Any]) -> WorkflowManifest:
243
+ """Instantiate from a raw Python dictionary."""
244
+ return cls.model_validate(data)
245
+
246
+ @classmethod
247
+ def from_yaml(cls, source: str | Path) -> WorkflowManifest:
248
+ """Parse from YAML string or file path."""
249
+ p = Path(source) if isinstance(source, (str, Path)) else None
250
+ if p and p.exists() and p.is_file():
251
+ content = p.read_text(encoding="utf-8")
252
+ else:
253
+ content = str(source)
254
+ data = yaml.safe_load(content)
255
+ return cls.from_dict(data)
256
+
257
+ @classmethod
258
+ def from_json(cls, source: str | Path) -> WorkflowManifest:
259
+ """Parse from JSON string or file path."""
260
+ p = Path(source) if isinstance(source, (str, Path)) else None
261
+ if p and p.exists() and p.is_file():
262
+ content = p.read_text(encoding="utf-8")
263
+ else:
264
+ content = str(source)
265
+ data = json.loads(content)
266
+ return cls.from_dict(data)
267
+
268
+ def to_dict(self) -> dict[str, Any]:
269
+ """Convert manifest to Python dictionary."""
270
+ return self.model_dump(exclude_none=True)
271
+
272
+ def to_yaml(self) -> str:
273
+ """Convert manifest to YAML string."""
274
+ return yaml.dump(self.to_dict(), sort_keys=False)
@@ -0,0 +1,431 @@
1
+ """CLM Monadic Template & Narrative Spacetime Monad (Layer 0).
2
+
3
+ Part of Phase 7 (Sprint 383: Layer 0 Kenotic Core & Parser Unification).
4
+ Provides the pure, stateless monadic substrate for chapter-based narrative transformations:
5
+ - Monad Archetype
6
+ - Narrative Threads: Reader, State, Writer, IO, Either (Left, Right), Maybe (Just, Nothing)
7
+ - CLMConfiguration: Abstract (VCard/Why), Concrete (PCard/How), Balanced (MCard/What)
8
+ - NarrativeMonad: Composition of Reader, State, Writer, and IO
9
+ - Chapter: Encapsulates individual narrative transformation steps
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from abc import ABC, abstractmethod
15
+ from collections.abc import Callable
16
+ from dataclasses import dataclass
17
+ from typing import Any, Generic, TypeVar
18
+
19
+ # Type Variables
20
+ T = TypeVar("T") # Result Type
21
+ U = TypeVar("U") # Transformed Result Type
22
+ R = TypeVar("R") # Reader Context Type
23
+ S = TypeVar("S") # State Type
24
+ W = TypeVar("W") # Writer Log Type
25
+ E = TypeVar("E") # Error Type
26
+
27
+
28
+ # =============================================================================
29
+ # 1. The Monad Archetype
30
+ # =============================================================================
31
+
32
+
33
+ class Monad(Generic[T], ABC):
34
+ """The Archetype of the Function.
35
+ Encapsulates Purity, Manages Impurity, Enables Composition.
36
+ """
37
+
38
+ @abstractmethod
39
+ def bind(self, func: Callable[[T], Monad[U]]) -> Monad[U]:
40
+ pass
41
+
42
+ def map(self, func: Callable[[T], U]) -> Monad[U]:
43
+ """Standard map operation."""
44
+ return self.bind(lambda x: self.pure(func(x)))
45
+
46
+ @classmethod
47
+ def pure(cls, value: T) -> Monad[T]:
48
+ return cls.unit(value)
49
+
50
+ @classmethod
51
+ @abstractmethod
52
+ def unit(cls, value: T) -> Monad[T]:
53
+ pass
54
+
55
+
56
+ # =============================================================================
57
+ # 2. The Narrative Threads (Specific Monads)
58
+ # =============================================================================
59
+
60
+
61
+ @dataclass
62
+ class Reader(Monad[T], Generic[R, T]):
63
+ """The Reader Monad: Carries the Cultural Context and Configuration.
64
+ Immutable and accessible everywhere.
65
+ """
66
+
67
+ run: Callable[[R], T]
68
+
69
+ def bind(self, func: Callable[[T], Reader[R, U]]) -> Reader[R, U]:
70
+ return Reader(lambda r: func(self.run(r)).run(r))
71
+
72
+ @classmethod
73
+ def unit(cls, value: T) -> Reader[R, T]:
74
+ return Reader(lambda _: value)
75
+
76
+ @classmethod
77
+ def pure(cls, value: T) -> Reader[R, T]:
78
+ return cls.unit(value)
79
+
80
+ @staticmethod
81
+ def ask() -> Reader[R, R]:
82
+ return Reader(lambda env: env)
83
+
84
+
85
+ @dataclass
86
+ class State(Monad[T], Generic[S, T]):
87
+ """The State Monad: Carries the evolving World State.
88
+ (Village Prosperity, Network Topology, User Progress).
89
+ """
90
+
91
+ run: Callable[[S], tuple[T, S]]
92
+
93
+ def bind(self, func: Callable[[T], State[S, U]]) -> State[S, U]:
94
+ def new_run(s: S) -> tuple[U, S]:
95
+ val, new_s = self.run(s)
96
+ return func(val).run(new_s)
97
+
98
+ return State(new_run)
99
+
100
+ @classmethod
101
+ def unit(cls, value: T) -> State[S, T]:
102
+ return State(lambda s: (value, s))
103
+
104
+ @classmethod
105
+ def pure(cls, value: T) -> State[S, T]:
106
+ return cls.unit(value)
107
+
108
+ @staticmethod
109
+ def get() -> State[S, S]:
110
+ return State(lambda s: (s, s))
111
+
112
+ @staticmethod
113
+ def put(new_state: S) -> State[S, None]:
114
+ return State(lambda _: (None, new_state))
115
+
116
+
117
+ class CallableTuple(tuple):
118
+ """A tuple subclass that is callable, returning itself."""
119
+
120
+ def __call__(self) -> tuple:
121
+ return self
122
+
123
+
124
+ class Writer(Monad[T], Generic[W, T]):
125
+ """The Writer Monad: Accumulates the Log of the journey.
126
+ (The Story Text, The Audit Trail).
127
+ """
128
+
129
+ def __init__(self, run: Any):
130
+ if callable(run) and not isinstance(run, tuple):
131
+ res = run()
132
+ self.run = CallableTuple(res)
133
+ elif isinstance(run, tuple):
134
+ self.run = CallableTuple(run)
135
+ else:
136
+ self.run = CallableTuple((run, []))
137
+
138
+ def bind(self, func: Callable[[T], Writer[W, U]]) -> Writer[W, U]:
139
+ val, log = self.run
140
+ new_writer = func(val)
141
+ new_val, new_log = new_writer.run
142
+ return Writer((new_val, list(log) + list(new_log)))
143
+
144
+ @classmethod
145
+ def unit(cls, value: T) -> Writer[W, T]:
146
+ return cls((value, []))
147
+
148
+ @classmethod
149
+ def pure(cls, value: T) -> Writer[W, T]:
150
+ return cls((value, []))
151
+
152
+ @staticmethod
153
+ def tell(log: list[W]) -> Writer[W, None]:
154
+ return Writer((None, log))
155
+
156
+
157
+ @dataclass
158
+ class IO(Monad[T]):
159
+ """The IO Monad: Handles Effects at the boundaries.
160
+ (User Interaction, System Deployment).
161
+ """
162
+
163
+ effect: Callable[[], T]
164
+
165
+ def unsafe_run(self) -> T:
166
+ """Execute the side effect."""
167
+ return self.effect()
168
+
169
+ def __call__(self) -> T:
170
+ return self.effect()
171
+
172
+ def bind(self, func: Callable[[T], IO[U]]) -> IO[U]:
173
+ return IO(lambda: func(self.effect()).effect())
174
+
175
+ @classmethod
176
+ def unit(cls, value: T) -> IO[T]:
177
+ return IO(lambda: value)
178
+
179
+ @classmethod
180
+ def pure(cls, value: T) -> IO[T]:
181
+ return IO(lambda: value)
182
+
183
+
184
+ class CallableBool(int):
185
+ """An int subclass representing a bool that is callable, returning itself."""
186
+
187
+ def __call__(self) -> bool:
188
+ return bool(self)
189
+
190
+ def __bool__(self) -> bool:
191
+ return self != 0
192
+
193
+
194
+ class Either(Monad[T], Generic[E, T]):
195
+ """The Either Monad: Represents a value that can be one of two types.
196
+ Used for Error Handling (Left=Error, Right=Success) or Branching Logic.
197
+ Supports both Either[T] and Either[E, T] typing specializations.
198
+ """
199
+
200
+ def __init__(self, value: Any, is_left: bool = False):
201
+ self.value = value
202
+ self._is_left = CallableBool(1 if is_left else 0)
203
+
204
+ def __class_getitem__(cls, item: Any) -> Any:
205
+ if not isinstance(item, tuple):
206
+ item = (Any, item)
207
+ return super().__class_getitem__(item)
208
+
209
+ @property
210
+ def is_left(self) -> CallableBool:
211
+ return self._is_left
212
+
213
+ @property
214
+ def is_right(self) -> CallableBool:
215
+ return CallableBool(0 if self._is_left else 1)
216
+
217
+ def bind(self, func: Callable[[T], Either[Any, U]]) -> Either[Any, U]:
218
+ if self._is_left:
219
+ return self # Propagate failure/left value
220
+ return func(self.value)
221
+
222
+ @classmethod
223
+ def unit(cls, value: T) -> Either[Any, T]:
224
+ return Right(value)
225
+
226
+ @classmethod
227
+ def pure(cls, value: T) -> Either[Any, T]:
228
+ return Right(value)
229
+
230
+ @classmethod
231
+ def left(cls, value: Any) -> Left[Any]:
232
+ return Left(value)
233
+
234
+ @classmethod
235
+ def right(cls, value: T) -> Right[T]:
236
+ return Right(value)
237
+
238
+
239
+ class Left(Either[E, Any], Generic[E]):
240
+ def __init__(self, value: E):
241
+ super().__init__(value, is_left=True)
242
+
243
+
244
+ class Right(Either[Any, T], Generic[T]):
245
+ def __init__(self, value: T):
246
+ super().__init__(value, is_left=False)
247
+
248
+
249
+ class Maybe(Monad[T], Generic[T]):
250
+ """The Maybe Monad: Represents an optional value (Just val or Nothing).
251
+ Used for handling absence of value without null checks.
252
+ """
253
+
254
+ def __init__(self, value: T | None, is_nothing: bool = False):
255
+ self.value = value
256
+ self._is_nothing = CallableBool(1 if is_nothing else 0)
257
+
258
+ @property
259
+ def is_nothing(self) -> CallableBool:
260
+ return self._is_nothing
261
+
262
+ @property
263
+ def is_just(self) -> CallableBool:
264
+ return CallableBool(0 if self._is_nothing else 1)
265
+
266
+ def bind(self, func: Callable[[T], Maybe[U]]) -> Maybe[U]:
267
+ if self._is_nothing:
268
+ return self # Propagate Nothing
269
+ return func(self.value) # type: ignore
270
+
271
+ @classmethod
272
+ def unit(cls, value: T) -> Maybe[T]:
273
+ return Just(value)
274
+
275
+ @classmethod
276
+ def pure(cls, value: T) -> Maybe[T]:
277
+ return Just(value)
278
+
279
+ @classmethod
280
+ def nothing(cls) -> Nothing:
281
+ return Nothing()
282
+
283
+ @classmethod
284
+ def just(cls, value: T) -> Just:
285
+ return Just(value)
286
+
287
+
288
+ class Nothing(Maybe[Any]):
289
+ def __init__(self):
290
+ super().__init__(None, is_nothing=True)
291
+
292
+
293
+ class Just(Maybe[T]):
294
+ def __init__(self, value: T):
295
+ super().__init__(value, is_nothing=False)
296
+
297
+
298
+ # =============================================================================
299
+ # 3. The Cubical Logic Model (CLM)
300
+ # =============================================================================
301
+
302
+
303
+ @dataclass
304
+ class CLMConfiguration:
305
+ """Defines the three orthogonal dimensions of any well-formed concept."""
306
+
307
+ abstract: str # VCard (Value/Type): The Principle ("Why")
308
+ concrete: str # PCard (Process/Instance): The Character ("How")
309
+ balanced: str # MCard (Memory/Property): The Evidence ("What")
310
+
311
+ def __repr__(self) -> str:
312
+ return (
313
+ f"🧊 CLM Cube:\n"
314
+ f" - Abstract (Why): {self.abstract}\n"
315
+ f" - Concrete (How): {self.concrete}\n"
316
+ f" - Balanced (What): {self.balanced}"
317
+ )
318
+
319
+
320
+ # =============================================================================
321
+ # 4. The Narrative Monad (Composition)
322
+ # =============================================================================
323
+
324
+
325
+ class NarrativeMonad(Generic[R, S, W, T]):
326
+ """The 'Spacetime' Monad.
327
+ A composition of Reader, State, Writer, and IO.
328
+
329
+ Signature: Context -> State -> IO(Result, NewState, Log)
330
+ """
331
+
332
+ def __init__(self, run: Callable[[R, S], IO[tuple[T, S, list[W]]]]):
333
+ self.run = run
334
+
335
+ def bind(
336
+ self, func: Callable[[T], NarrativeMonad[R, S, W, U]]
337
+ ) -> NarrativeMonad[R, S, W, U]:
338
+ def new_run(r: R, s: S) -> IO[tuple[U, S, list[W]]]:
339
+ # Execute first step
340
+ io_result = self.run(r, s)
341
+
342
+ # Define continuation inside IO to handle effects sequentially
343
+ def continuation() -> tuple[U, S, list[W]]:
344
+ val, s_prime, log1 = io_result.effect()
345
+
346
+ # Execute second step
347
+ next_monad = func(val)
348
+ io_result2 = next_monad.run(r, s_prime)
349
+ val2, s_double_prime, log2 = io_result2.effect()
350
+
351
+ return (val2, s_double_prime, log1 + log2)
352
+
353
+ return IO(continuation)
354
+
355
+ return NarrativeMonad(new_run)
356
+
357
+ @classmethod
358
+ def unit(cls, value: T) -> NarrativeMonad[R, S, W, T]:
359
+ return NarrativeMonad(lambda r, s: IO.unit((value, s, [])))
360
+
361
+ @classmethod
362
+ def lift_io(cls, io_action: IO[T]) -> NarrativeMonad[R, S, W, T]:
363
+ """Lift a pure IO action into the Narrative Monad"""
364
+ return NarrativeMonad(lambda r, s: IO(lambda: (io_action.effect(), s, [])))
365
+
366
+ @classmethod
367
+ def log(cls, message: W) -> NarrativeMonad[R, S, W, None]:
368
+ """Log a message (Writer effect)"""
369
+ return NarrativeMonad(lambda r, s: IO.unit((None, s, [message])))
370
+
371
+ @classmethod
372
+ def get_context(cls) -> NarrativeMonad[R, S, W, R]:
373
+ """Read the context (Reader effect)"""
374
+ return NarrativeMonad(lambda r, s: IO.unit((r, s, [])))
375
+
376
+ @classmethod
377
+ def get_state(cls) -> NarrativeMonad[R, S, W, S]:
378
+ """Read the state (State effect)"""
379
+ return NarrativeMonad(lambda r, s: IO.unit((s, s, [])))
380
+
381
+ @classmethod
382
+ def put_state(cls, new_state: S) -> NarrativeMonad[R, S, W, None]:
383
+ """Update the state (State effect)"""
384
+ return NarrativeMonad(lambda r, s: IO.unit((None, new_state, [])))
385
+
386
+ def execute(self, context: R, initial_state: S) -> tuple[T, S, list[W]]:
387
+ """Run the monadic chain"""
388
+ return self.run(context, initial_state).effect()
389
+
390
+
391
+ # =============================================================================
392
+ # 5. Chapter Template
393
+ # =============================================================================
394
+
395
+
396
+ @dataclass
397
+ class Chapter:
398
+ """A Chapter in the Prologue of Spacetime."""
399
+
400
+ id: int
401
+ title: str
402
+ clm: CLMConfiguration
403
+ mvp_card: str # The Archetype Name
404
+ pkc_task: str # The Container Task
405
+ action: NarrativeMonad[dict, dict, str, Any]
406
+
407
+ def run(self, context: dict, state: dict) -> tuple[Any, dict, list[str]]:
408
+ print(f"\n--- Executing {self.title} ---")
409
+ print(self.clm)
410
+ result, new_state, log = self.action.execute(context, state)
411
+ print(f"Result: {result}")
412
+ print(f"Log: {log}")
413
+ return result, new_state, log
414
+
415
+
416
+ __all__ = [
417
+ "CLMConfiguration",
418
+ "Chapter",
419
+ "Either",
420
+ "IO",
421
+ "Just",
422
+ "Left",
423
+ "Maybe",
424
+ "Monad",
425
+ "NarrativeMonad",
426
+ "Nothing",
427
+ "Reader",
428
+ "Right",
429
+ "State",
430
+ "Writer",
431
+ ]