phronomy 0.27.0 → 0.28.0

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 (475) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +52 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +9 -3
  6. data/benchmark/bench_agent_invoke.rb +25 -50
  7. data/benchmark/bench_context_assembler.rb +14 -11
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/benchmark/bench_tool_schema.rb +1 -1
  10. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  11. data/docs/architecture/agent-transition-ownership.md +13 -89
  12. data/docs/architecture/backend-groups.md +152 -0
  13. data/docs/architecture/before-llm-input.md +3 -3
  14. data/docs/architecture/context-management.md +1 -1
  15. data/docs/architecture/execution-abstraction-review.md +88 -0
  16. data/docs/architecture/execution-boundaries.md +28 -0
  17. data/docs/architecture/execution-metadata-and-values.md +6 -0
  18. data/docs/architecture/multi-agent-handoff.md +6 -5
  19. data/docs/architecture/persistence.md +27 -9
  20. data/docs/architecture/r8-unit1.md +104 -0
  21. data/docs/architecture/r8-unit10.md +72 -0
  22. data/docs/architecture/r8-unit11.md +71 -0
  23. data/docs/architecture/r8-unit12.md +67 -0
  24. data/docs/architecture/r8-unit13.md +61 -0
  25. data/docs/architecture/r8-unit14.md +72 -0
  26. data/docs/architecture/r8-unit15.md +65 -0
  27. data/docs/architecture/r8-unit16.md +54 -0
  28. data/docs/architecture/r8-unit17.md +73 -0
  29. data/docs/architecture/r8-unit18.md +92 -0
  30. data/docs/architecture/r8-unit19.md +61 -0
  31. data/docs/architecture/r8-unit2.md +55 -0
  32. data/docs/architecture/r8-unit20.md +51 -0
  33. data/docs/architecture/r8-unit21.md +79 -0
  34. data/docs/architecture/r8-unit22.md +74 -0
  35. data/docs/architecture/r8-unit23.md +81 -0
  36. data/docs/architecture/r8-unit3.md +130 -0
  37. data/docs/architecture/r8-unit4.md +79 -0
  38. data/docs/architecture/r8-unit5.md +115 -0
  39. data/docs/architecture/r8-unit6.md +71 -0
  40. data/docs/architecture/r8-unit7.md +70 -0
  41. data/docs/architecture/r8-unit8.md +78 -0
  42. data/docs/architecture/r8-unit9.md +73 -0
  43. data/docs/architecture/refactoring-closure.md +10 -1
  44. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  45. data/docs/architecture/storage-execution-boundaries.md +54 -0
  46. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  47. data/docs/architecture/tracing.md +1 -1
  48. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  49. data/docs/architecture.md +24 -11
  50. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  51. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  52. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  53. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  54. data/docs/decisions/032-storage-backend-composition.md +5 -0
  55. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  56. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  57. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  58. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  59. data/docs/decisions/040-configuration-default-composition.md +12 -1
  60. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  61. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  62. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  63. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  64. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  65. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  66. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  67. data/docs/decisions/062-content-store-service.md +52 -0
  68. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  69. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  70. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  71. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  72. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  73. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  74. data/docs/decisions/README.md +18 -1
  75. data/docs/execution-boundaries-preview.md +45 -0
  76. data/docs/features.md +8 -3
  77. data/docs/getting-started.md +9 -6
  78. data/docs/migrations/0.22.md +2 -2
  79. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  80. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  81. data/docs/migrations/persistence-failure-contracts.md +67 -0
  82. data/docs/migrations/r8-unit10.md +36 -0
  83. data/docs/migrations/r8-unit11.md +33 -0
  84. data/docs/migrations/r8-unit12.md +31 -0
  85. data/docs/migrations/r8-unit13.md +36 -0
  86. data/docs/migrations/r8-unit14.md +31 -0
  87. data/docs/migrations/r8-unit15.md +31 -0
  88. data/docs/migrations/r8-unit16.md +26 -0
  89. data/docs/migrations/r8-unit17.md +28 -0
  90. data/docs/migrations/r8-unit18.md +29 -0
  91. data/docs/migrations/r8-unit19.md +28 -0
  92. data/docs/migrations/r8-unit20.md +24 -0
  93. data/docs/migrations/r8-unit21.md +28 -0
  94. data/docs/migrations/r8-unit22.md +29 -0
  95. data/docs/migrations/r8-unit23.md +20 -0
  96. data/docs/migrations/r8-unit4.md +67 -0
  97. data/docs/migrations/r8-unit5.md +75 -0
  98. data/docs/migrations/r8-unit6.md +109 -0
  99. data/docs/migrations/r8-unit7.md +31 -0
  100. data/docs/migrations/r8-unit8.md +33 -0
  101. data/docs/migrations/r8-unit9.md +28 -0
  102. data/docs/migrations/storage-async-client.md +103 -0
  103. data/docs/migrations/vector-async-clients.md +110 -0
  104. data/docs/persistence-backends.md +38 -21
  105. data/docs/runtime-and-concurrency.md +26 -1
  106. data/lib/phronomy/agent/admission.rb +78 -0
  107. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  108. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  109. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  110. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  111. data/lib/phronomy/agent/async_event_api.rb +37 -32
  112. data/lib/phronomy/agent/base.rb +74 -72
  113. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  114. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  115. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  116. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  117. data/lib/phronomy/agent/control_request.rb +21 -0
  118. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  119. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  120. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  121. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  122. data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
  123. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  124. data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
  125. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  126. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
  127. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  128. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  129. data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
  130. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  131. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  132. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
  133. data/lib/phronomy/agent/execution_change.rb +161 -0
  134. data/lib/phronomy/agent/execution_environment.rb +36 -0
  135. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  136. data/lib/phronomy/agent/execution_observation.rb +35 -0
  137. data/lib/phronomy/agent/filterable.rb +182 -0
  138. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  139. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  140. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  141. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  142. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  143. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  144. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
  145. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  146. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  147. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  148. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  149. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  150. data/lib/phronomy/agent/persistence/records.rb +33 -0
  151. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  152. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  153. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  154. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  155. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
  156. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
  157. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
  158. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
  159. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  160. data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
  161. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  162. data/lib/phronomy/agent/retention.rb +17 -0
  163. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  164. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  165. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  166. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  167. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  168. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  169. data/lib/phronomy/agent/settings.rb +47 -0
  170. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  171. data/lib/phronomy/agent/store.rb +253 -0
  172. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
  173. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  174. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  175. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  176. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  177. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  178. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  179. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  180. data/lib/phronomy/context/assembly.rb +202 -0
  181. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  182. data/lib/phronomy/context/candidate.rb +51 -0
  183. data/lib/phronomy/context/constraint.rb +47 -0
  184. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  185. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  186. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  187. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  188. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  189. data/lib/phronomy/context/default_policy.rb +140 -0
  190. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  191. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  192. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  193. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  194. data/lib/phronomy/context/manifest_error.rb +10 -0
  195. data/lib/phronomy/context/prompt_template.rb +98 -0
  196. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  197. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  198. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  199. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  200. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  201. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  202. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  203. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  204. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  205. data/lib/phronomy/embeddings/base.rb +43 -0
  206. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  207. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  208. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  209. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  210. data/lib/phronomy/engine/diagnostics.rb +1 -1
  211. data/lib/phronomy/engine/event_loop.rb +7 -7
  212. data/lib/phronomy/engine/execution_binding.rb +23 -0
  213. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  214. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  215. data/lib/phronomy/engine/fsm_session.rb +3 -3
  216. data/lib/phronomy/engine/metrics.rb +16 -14
  217. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  218. data/lib/phronomy/engine/runtime.rb +25 -12
  219. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  220. data/lib/phronomy/execution/async_operation.rb +41 -0
  221. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  222. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  223. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  224. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  225. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  226. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  227. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  228. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  229. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  230. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  231. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  232. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  233. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  234. data/lib/phronomy/execution/runnable.rb +24 -0
  235. data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
  236. data/lib/phronomy/execution/wait_policy.rb +24 -0
  237. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
  238. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  239. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  240. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  241. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  242. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  243. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  244. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  245. data/lib/phronomy/llm_adapter/base.rb +49 -68
  246. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  247. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  248. data/lib/phronomy/llm_adapter/message.rb +62 -0
  249. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  250. data/lib/phronomy/llm_adapter/request.rb +79 -0
  251. data/lib/phronomy/llm_adapter/response.rb +36 -0
  252. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  253. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  254. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  255. data/lib/phronomy/llm_adapter.rb +3 -16
  256. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  257. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  258. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  259. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  260. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  261. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  262. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  263. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  264. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  265. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  266. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  267. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  268. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  269. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  270. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
  271. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  272. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  273. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  274. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  275. data/lib/phronomy/multi_agent/store.rb +156 -0
  276. data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
  277. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  278. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  279. data/lib/phronomy/persistence/content_repository.rb +26 -0
  280. data/lib/phronomy/persistence/errors.rb +16 -0
  281. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  282. data/lib/phronomy/persistence/persistence.rb +23 -0
  283. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  284. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  285. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  286. data/lib/phronomy/persistence/transaction.rb +86 -0
  287. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  288. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  289. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  290. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  291. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  292. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  293. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  294. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  295. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  296. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  297. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  298. data/lib/phronomy/storage/async/async_client.rb +60 -0
  299. data/lib/phronomy/storage/backend.rb +8 -2
  300. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  301. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  302. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  303. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  304. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  305. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  306. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  307. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  308. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  309. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  310. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  311. data/lib/phronomy/tool/authorization.rb +77 -0
  312. data/lib/phronomy/tool/base.rb +549 -9
  313. data/lib/phronomy/tool/call_request.rb +40 -0
  314. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  315. data/lib/phronomy/tool/operation.rb +88 -0
  316. data/lib/phronomy/tool/schema.rb +214 -0
  317. data/lib/phronomy/tool/settings.rb +31 -0
  318. data/lib/phronomy/tool/tool_error.rb +7 -0
  319. data/lib/phronomy/tool/tool_executor.rb +58 -0
  320. data/lib/phronomy/tools/agent.rb +22 -90
  321. data/lib/phronomy/tools/mcp.rb +21 -14
  322. data/lib/phronomy/tools/vector_search.rb +4 -4
  323. data/lib/phronomy/tracing/automatic.rb +11 -6
  324. data/lib/phronomy/tracing/base.rb +1 -1
  325. data/lib/phronomy/tracing/observation.rb +37 -0
  326. data/lib/phronomy/tracing/settings.rb +36 -0
  327. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  328. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  329. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  330. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  331. data/lib/phronomy/vector_store/base.rb +118 -49
  332. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  333. data/lib/phronomy/vector_store.rb +2 -17
  334. data/lib/phronomy/version.rb +1 -1
  335. data/lib/phronomy/workflow/completion.rb +11 -0
  336. data/lib/phronomy/workflow/execution/workflow.rb +7 -7
  337. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  338. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  339. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  340. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
  341. data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
  342. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  343. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  344. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  345. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  346. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  347. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  348. data/lib/phronomy.rb +57 -16
  349. data/scripts/api_snapshot.rb +35 -15
  350. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  351. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  352. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  353. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  354. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  355. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  356. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  357. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  358. data/sig/phronomy/agent/admission.rbs +19 -0
  359. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  360. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  361. data/sig/phronomy/agent/store.rbs +73 -0
  362. data/sig/phronomy/agent.rbs +14 -138
  363. data/sig/phronomy/common.rbs +7 -0
  364. data/sig/phronomy/content_store/contracts.rbs +13 -0
  365. data/sig/phronomy/context.rbs +152 -0
  366. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  367. data/sig/phronomy/embeddings/base.rbs +17 -0
  368. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  369. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  370. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  371. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  372. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  373. data/sig/phronomy/engine/observability.rbs +13 -0
  374. data/sig/phronomy/execution/async_operation.rbs +7 -0
  375. data/sig/phronomy/execution/blocking.rbs +5 -0
  376. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  377. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  378. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  379. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  380. data/sig/phronomy/execution/errors.rbs +7 -0
  381. data/sig/phronomy/execution/execution.rbs +41 -0
  382. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  383. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  384. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  385. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  386. data/sig/phronomy/execution/task_result.rbs +29 -0
  387. data/sig/phronomy/extensions.rbs +0 -21
  388. data/sig/phronomy/handoff.rbs +10 -4
  389. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  390. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  391. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  392. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  393. data/sig/phronomy/llm_context_window.rbs +10 -0
  394. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  395. data/sig/phronomy/multi_agent/store.rbs +54 -0
  396. data/sig/phronomy/multi_agent.rbs +6 -3
  397. data/sig/phronomy/persistence/errors.rbs +18 -0
  398. data/sig/phronomy/persistence/migration.rbs +13 -0
  399. data/sig/phronomy/persistence/transaction.rbs +19 -0
  400. data/sig/phronomy/persistence.rbs +1 -99
  401. data/sig/phronomy/persistence_composition.rbs +19 -0
  402. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  403. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  404. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  405. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
  406. data/sig/phronomy/tool.rbs +48 -4
  407. data/sig/phronomy/tracing/observation.rbs +7 -0
  408. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  409. data/sig/phronomy/vector_store/base.rbs +23 -0
  410. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  411. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  412. data/sig/phronomy/workflow/store.rbs +8 -0
  413. data/sig/phronomy/workflow.rbs +1 -1
  414. data/sig/phronomy.rbs +0 -49
  415. metadata +318 -117
  416. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  417. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  418. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  419. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  420. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  421. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  422. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  423. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  424. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  425. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  426. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  427. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  428. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  429. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  430. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  431. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  432. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  433. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  434. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  435. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  436. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  437. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  438. data/lib/phronomy/configuration/configuration.rb +0 -68
  439. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  440. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  441. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  442. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  443. data/lib/phronomy/engine/runnable.rb +0 -54
  444. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  445. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  446. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  447. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  448. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  449. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  450. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  451. data/lib/phronomy/recovery/recovery.rb +0 -186
  452. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  453. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  454. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  455. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  456. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  457. data/sig/phronomy/execution_receiver.rbs +0 -34
  458. data/sig/phronomy/llm_adapter.rbs +0 -11
  459. data/sig/phronomy/runtime.rbs +0 -76
  460. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  461. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  462. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  463. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  464. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  465. /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
  466. /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
  467. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  468. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  469. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  470. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  471. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  472. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  473. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  474. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  475. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  require "securerandom"
6
6
 
@@ -14,7 +14,7 @@ module Phronomy
14
14
  # - Runtime admission uses a separate opaque owner token.
15
15
  #
16
16
  # Workflow persistence is synchronous at the repository contract but is always
17
- # invoked through Runtime's OffloadPool. Durable terminal save results return
17
+ # invoked through its owning execution environment. Durable terminal save results return
18
18
  # to the same FSMSession before that session becomes halted/completed.
19
19
  # @api private
20
20
  class WorkflowRunner
@@ -91,7 +91,8 @@ module Phronomy
91
91
  @entry_point = entry_point
92
92
  @wait_state_names = wait_state_names
93
93
  @persistence = persistence
94
- @phase_machine_class = Workflow::PhaseMachineBuilder.new(
94
+ @environment = WorkflowExecutionEnvironment.current
95
+ @phase_machine_class = @environment.compile_transitions(
95
96
  entry_point: @entry_point,
96
97
  declared_states: @declared_states,
97
98
  wait_state_names: @wait_state_names,
@@ -99,7 +100,7 @@ module Phronomy
99
100
  entry_actions: @entry_actions,
100
101
  auto_transitions: auto_transitions,
101
102
  exit_actions: exit_actions
102
- ).build
103
+ )
103
104
  end
104
105
 
105
106
  def invoke(input, config: {})
@@ -153,7 +154,7 @@ module Phronomy
153
154
  "Valid events: #{@external_events.keys.inspect}"
154
155
  end
155
156
 
156
- registry = Phronomy::WorkflowExecutionRegistry.existing_for(Phronomy::Runtime.instance)
157
+ registry = @environment.existing_registry
157
158
  return false unless registry
158
159
 
159
160
  registry.post_to_workflow(
@@ -168,8 +169,7 @@ module Phronomy
168
169
  # progression therefore remain EventLoop-owned.
169
170
  # @api private
170
171
  def deliver_on_event_loop(command)
171
- event_loop = Phronomy::Runtime.instance.event_loop
172
- assert_event_loop!(event_loop)
172
+ assert_execution_owner!
173
173
 
174
174
  case command
175
175
  when StartCommand
@@ -211,7 +211,7 @@ module Phronomy
211
211
  end
212
212
 
213
213
  def ensure_blocking_call_allowed!(method_name, async_alternative)
214
- return unless Phronomy::Runtime.instance.event_loop.current?
214
+ return unless Phronomy::WaitPolicy.blocking_forbidden?
215
215
 
216
216
  raise Phronomy::Error,
217
217
  "Cannot call Workflow##{method_name} from the EventLoop thread. " \
@@ -220,7 +220,6 @@ module Phronomy
220
220
 
221
221
  def start_new_execution(input, config, stable_observer: nil)
222
222
  reject_legacy_workflow_identity_key!(config)
223
- runtime = Phronomy::Runtime.instance
224
223
  result_task = Phronomy::TaskResult.deferred(name: "workflow:preparing")
225
224
  explicit_workflow_instance_id = !config[:workflow_instance_id].nil?
226
225
  workflow_instance_id = (config[:workflow_instance_id] || SecureRandom.uuid).to_s.freeze
@@ -242,7 +241,7 @@ module Phronomy
242
241
  owner_token: Object.new.freeze,
243
242
  explicit_workflow_instance_id: explicit_workflow_instance_id
244
243
  )
245
- fail_task(result_task, runtime_rejected_error(:start)) unless post_control(runtime, command)
244
+ fail_task(result_task, runtime_rejected_error(:start)) unless post_control(command)
246
245
  result_task
247
246
  rescue => error
248
247
  fail_task(result_task, error) if defined?(result_task) && result_task
@@ -250,7 +249,6 @@ module Phronomy
250
249
  end
251
250
 
252
251
  def start_resume_execution(state, input:, event_name:, current_phase:)
253
- runtime = Phronomy::Runtime.instance
254
252
  workflow_instance_id = state.workflow_instance_id.to_s.freeze
255
253
  result_task = Phronomy::TaskResult.deferred(name: "workflow-resume:#{workflow_instance_id}")
256
254
  Phronomy::Tracing::Automatic.observe_task(
@@ -271,7 +269,7 @@ module Phronomy
271
269
  workflow_instance_id: workflow_instance_id,
272
270
  owner_token: Object.new.freeze
273
271
  )
274
- fail_task(result_task, runtime_rejected_error(:resume)) unless post_control(runtime, command)
272
+ fail_task(result_task, runtime_rejected_error(:resume)) unless post_control(command)
275
273
  result_task
276
274
  rescue => error
277
275
  fail_task(result_task, error) if defined?(result_task) && result_task
@@ -279,10 +277,8 @@ module Phronomy
279
277
  end
280
278
 
281
279
  def begin_start_on_event_loop(request)
282
- runtime = Phronomy::Runtime.instance
283
- event_loop = runtime.event_loop
284
280
  admitted = false
285
- Phronomy::WorkflowExecutionRegistry.for(event_loop).admit_workflow(
281
+ @environment.registry.admit_workflow(
286
282
  request.workflow_instance_id,
287
283
  owner_token: request.owner_token
288
284
  )
@@ -290,7 +286,7 @@ module Phronomy
290
286
 
291
287
  recursion_limit = request.config.fetch(
292
288
  :recursion_limit,
293
- Phronomy.configuration.recursion_limit
289
+ Phronomy::WorkflowSettings.current.recursion_limit
294
290
  )
295
291
  repository = configured_repository
296
292
  persist = request.explicit_workflow_instance_id && !repository.nil?
@@ -315,7 +311,6 @@ module Phronomy
315
311
  end
316
312
 
317
313
  submit_workflow_load(
318
- runtime,
319
314
  repository,
320
315
  request.workflow_instance_id,
321
316
  StartLoadReady,
@@ -323,7 +318,7 @@ module Phronomy
323
318
  )
324
319
  rescue => error
325
320
  if admitted
326
- Phronomy::WorkflowExecutionRegistry.for(event_loop).release_workflow(
321
+ @environment.registry.release_workflow(
327
322
  request.workflow_instance_id,
328
323
  owner_token: request.owner_token
329
324
  )
@@ -332,10 +327,8 @@ module Phronomy
332
327
  end
333
328
 
334
329
  def begin_resume_on_event_loop(request)
335
- runtime = Phronomy::Runtime.instance
336
- event_loop = runtime.event_loop
337
330
  admitted = false
338
- Phronomy::WorkflowExecutionRegistry.for(event_loop).admit_workflow(
331
+ @environment.registry.admit_workflow(
339
332
  request.workflow_instance_id,
340
333
  owner_token: request.owner_token
341
334
  )
@@ -348,7 +341,7 @@ module Phronomy
348
341
  context: context,
349
342
  workflow_instance_id: request.workflow_instance_id,
350
343
  owner_token: request.owner_token,
351
- recursion_limit: Phronomy.configuration.recursion_limit,
344
+ recursion_limit: Phronomy::WorkflowSettings.current.recursion_limit,
352
345
  repository: nil,
353
346
  persist: false,
354
347
  expected_revision: nil
@@ -363,7 +356,6 @@ module Phronomy
363
356
  end
364
357
 
365
358
  submit_workflow_load(
366
- runtime,
367
359
  repository,
368
360
  request.workflow_instance_id,
369
361
  ResumeLoadReady,
@@ -371,7 +363,7 @@ module Phronomy
371
363
  )
372
364
  rescue => error
373
365
  if admitted
374
- Phronomy::WorkflowExecutionRegistry.for(event_loop).release_workflow(
366
+ @environment.registry.release_workflow(
375
367
  request.workflow_instance_id,
376
368
  owner_token: request.owner_token
377
369
  )
@@ -379,12 +371,12 @@ module Phronomy
379
371
  fail_task(request.result_task, error)
380
372
  end
381
373
 
382
- def submit_workflow_load(runtime, repository, workflow_instance_id, ready_class, request)
374
+ def submit_workflow_load(repository, workflow_instance_id, ready_class, request)
383
375
  operation = WorkflowLoadCommand.new(
384
376
  repository: repository,
385
377
  workflow_instance_id: workflow_instance_id.to_s.freeze
386
378
  )
387
- task = runtime.offload.submit(on_full: :raise) do
379
+ task = @environment.submit(on_full: :raise) do
388
380
  record = operation.repository.load(operation.workflow_instance_id)
389
381
  WorkflowLoadResult.new(
390
382
  repository: operation.repository,
@@ -400,7 +392,7 @@ module Phronomy
400
392
  error: error
401
393
  )
402
394
  fail_task(request.result_task, runtime_rejected_error(:load_result)) unless
403
- post_control(runtime, ready)
395
+ post_control(ready)
404
396
  end
405
397
  nil
406
398
  end
@@ -414,7 +406,7 @@ module Phronomy
414
406
 
415
407
  recursion_limit = request.config.fetch(
416
408
  :recursion_limit,
417
- Phronomy.configuration.recursion_limit
409
+ Phronomy::WorkflowSettings.current.recursion_limit
418
410
  )
419
411
  execution = build_new_execution(
420
412
  request.input,
@@ -452,7 +444,7 @@ module Phronomy
452
444
  context: context,
453
445
  workflow_instance_id: request.workflow_instance_id,
454
446
  owner_token: request.owner_token,
455
- recursion_limit: Phronomy.configuration.recursion_limit,
447
+ recursion_limit: Phronomy::WorkflowSettings.current.recursion_limit,
456
448
  repository: ready.result.repository,
457
449
  persist: true,
458
450
  expected_revision: expected_revision
@@ -508,7 +500,7 @@ module Phronomy
508
500
  local_snapshot = normalize_snapshot(snapshot_for(state))
509
501
  return durable_revision if normalized_durable == local_snapshot
510
502
 
511
- raise Phronomy::Storage::ConflictError,
503
+ raise Phronomy::Persistence::StateConflictError,
512
504
  "Workflow state changed since the supplied halted context for " \
513
505
  "workflow_instance_id #{state.workflow_instance_id.inspect}; explicit reload/reconciliation is required"
514
506
  end
@@ -560,8 +552,7 @@ module Phronomy
560
552
  end
561
553
 
562
554
  def configured_repository
563
- persistence = @persistence || Phronomy.configuration.persistence
564
- persistence&.workflow_states
555
+ @persistence || Phronomy::WorkflowSettings.current.workflow_store
565
556
  end
566
557
 
567
558
  def register_execution(
@@ -571,17 +562,14 @@ module Phronomy
571
562
  resume_phase: nil,
572
563
  stable_observer: nil
573
564
  )
574
- runtime = Phronomy::Runtime.instance
575
- event_loop = runtime.event_loop
576
- assert_event_loop!(event_loop)
565
+ assert_execution_owner!
577
566
  session = build_session_for(
578
567
  execution: execution,
579
- runtime: runtime,
580
568
  resume_event: resume_event,
581
569
  resume_phase: resume_phase,
582
570
  stable_observer: stable_observer
583
571
  )
584
- Phronomy::WorkflowExecutionRegistry.for(event_loop).bind_workflow_session(
572
+ @environment.registry.bind_workflow_session(
585
573
  execution.workflow_instance_id,
586
574
  owner_token: execution.owner_token,
587
575
  fsm_session_id: session.id
@@ -596,12 +584,11 @@ module Phronomy
596
584
  error: error
597
585
  )
598
586
  end
599
- event_loop.register(session, completion: source_task,
600
- receiver: Phronomy::WorkflowExecutionRegistry.for(event_loop))
587
+ @environment.register_session(session, completion: source_task)
601
588
  result_task
602
589
  rescue => error
603
- if event_loop&.current?
604
- Phronomy::WorkflowExecutionRegistry.for(event_loop).release_workflow(
590
+ if @environment.executing?
591
+ @environment.registry.release_workflow(
605
592
  execution.workflow_instance_id,
606
593
  owner_token: execution.owner_token
607
594
  )
@@ -612,9 +599,8 @@ module Phronomy
612
599
  # Called from source_task completion on EventLoop, after the FSMSession has
613
600
  # accepted the durable terminal result (when a durable barrier is required).
614
601
  def finalize_execution(execution:, result_task:, result:, error:)
615
- event_loop = Phronomy::Runtime.instance.event_loop
616
- assert_event_loop!(event_loop)
617
- Phronomy::WorkflowExecutionRegistry.for(event_loop).release_workflow(
602
+ assert_execution_owner!
603
+ @environment.registry.release_workflow(
618
604
  execution.workflow_instance_id,
619
605
  owner_token: execution.owner_token
620
606
  )
@@ -627,10 +613,8 @@ module Phronomy
627
613
  context:,
628
614
  event_sink:
629
615
  )
630
- runtime = Phronomy::Runtime.instance
631
- event_loop = runtime.event_loop
632
- assert_event_loop!(event_loop)
633
- Phronomy::WorkflowExecutionRegistry.for(event_loop).mark_workflow_admission(
616
+ assert_execution_owner!
617
+ @environment.registry.mark_workflow_admission(
634
618
  execution.workflow_instance_id,
635
619
  owner_token: execution.owner_token,
636
620
  state: :persisting_terminal
@@ -643,7 +627,7 @@ module Phronomy
643
627
  snapshot: deep_immutable_copy(snapshot_for(context))
644
628
  )
645
629
 
646
- task = runtime.offload.submit(on_full: :raise) do
630
+ task = @environment.submit(on_full: :raise) do
647
631
  persist_terminal_snapshot(operation)
648
632
  end
649
633
  task.on_complete do |result, operation_error|
@@ -663,10 +647,10 @@ module Phronomy
663
647
  revision: revision,
664
648
  error: nil
665
649
  )
666
- rescue Phronomy::Storage::ConflictError,
667
- Phronomy::Storage::NotFoundError,
668
- Phronomy::Storage::SerializationError,
669
- Phronomy::Storage::UnsupportedBackendError => error
650
+ rescue Phronomy::Persistence::ConflictError,
651
+ Phronomy::Persistence::NotFoundError,
652
+ Phronomy::Persistence::SerializationError,
653
+ Phronomy::Persistence::UnsupportedBackendError => error
670
654
  Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
671
655
  outcome: :known_failure,
672
656
  revision: nil,
@@ -688,7 +672,7 @@ module Phronomy
688
672
  end
689
673
  accepted = event_sink.post(:workflow_terminal_persistence_result, delivery)
690
674
  unless accepted
691
- Phronomy.configuration.logger&.warn(
675
+ Phronomy::RuntimeSettings.current.logger&.warn(
692
676
  "[Phronomy] EventLoop rejected Workflow terminal persistence result " \
693
677
  "for #{execution.workflow_instance_id.inspect}"
694
678
  )
@@ -697,13 +681,13 @@ module Phronomy
697
681
 
698
682
  def reconcile_workflow_terminal_f1(operation, original_error)
699
683
  record = operation.repository.load(operation.workflow_instance_id)
700
- case Phronomy::Recovery.compare_revisioned_snapshot(
684
+ case Phronomy::Persistence::SnapshotComparison.compare_revisioned_snapshot(
701
685
  record: record,
702
686
  expected_pre_revision: operation.expected_revision,
703
687
  intended_snapshot: operation.snapshot
704
688
  )
705
689
  when :post_state
706
- revision = Phronomy::Recovery.fetch_value(record, :revision)
690
+ revision = Phronomy::Persistence::SnapshotComparison.fetch_value(record, :revision)
707
691
  Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
708
692
  outcome: :success,
709
693
  revision: revision,
@@ -719,7 +703,7 @@ module Phronomy
719
703
  error: original_error
720
704
  )
721
705
  else
722
- conflict = Phronomy::Storage::ConflictError.new(
706
+ conflict = Phronomy::Persistence::StateConflictError.new(
723
707
  "Workflow terminal Persistence outcome conflicts with both expected " \
724
708
  "pre-state and intended post-state for #{operation.workflow_instance_id.inspect}"
725
709
  )
@@ -738,9 +722,8 @@ module Phronomy
738
722
  end
739
723
 
740
724
  def release_and_fail(request, error)
741
- event_loop = Phronomy::Runtime.instance.event_loop
742
- assert_event_loop!(event_loop)
743
- Phronomy::WorkflowExecutionRegistry.for(event_loop).release_workflow(
725
+ assert_execution_owner!
726
+ @environment.registry.release_workflow(
744
727
  request.workflow_instance_id,
745
728
  owner_token: request.owner_token
746
729
  )
@@ -756,12 +739,11 @@ module Phronomy
756
739
 
757
740
  def build_session_for(
758
741
  execution:,
759
- runtime:,
760
742
  resume_event: nil,
761
743
  resume_phase: nil,
762
744
  stable_observer: nil
763
745
  )
764
- Phronomy::FSMSession.new(
746
+ @environment.build_session(
765
747
  context: execution.context,
766
748
  context_metadata: {
767
749
  workflow_instance_id: execution.workflow_instance_id
@@ -774,27 +756,24 @@ module Phronomy
774
756
  external_events: @external_events,
775
757
  phase_machine_class: @phase_machine_class,
776
758
  recursion_limit: execution.recursion_limit,
777
- event_loop: runtime.event_loop,
778
759
  resume_event: resume_event,
779
760
  resume_phase: resume_phase,
780
761
  stable_observer: stable_observer,
781
- terminal_policy: build_terminal_policy(execution)
762
+ persist: terminal_persistence_callback(execution)
782
763
  )
783
764
  end
784
765
 
785
- def build_terminal_policy(execution)
766
+ def terminal_persistence_callback(execution)
786
767
  return unless execution.repository && execution.persist
787
768
 
788
- Phronomy::WorkflowTerminalPolicy.new(
789
- persist: ->(terminal_type:, context:, event_sink:) {
790
- begin_terminal_persistence_on_event_loop(
791
- execution,
792
- terminal_type: terminal_type,
793
- context: context,
794
- event_sink: event_sink
795
- )
796
- }
797
- )
769
+ ->(terminal_type:, context:, event_sink:) {
770
+ begin_terminal_persistence_on_event_loop(
771
+ execution,
772
+ terminal_type: terminal_type,
773
+ context: context,
774
+ event_sink: event_sink
775
+ )
776
+ }
798
777
  end
799
778
 
800
779
  def resolve_resume_event(current_phase, event)
@@ -819,10 +798,10 @@ module Phronomy
819
798
  "No external event registered for state #{current_phase.inspect}"
820
799
  end
821
800
 
822
- def post_control(runtime, command)
801
+ def post_control(command)
823
802
  admission = command.is_a?(StartCommand) || command.is_a?(ResumeCommand)
824
803
  completion = admission ? command.result_task : command.request.result_task
825
- Phronomy::WorkflowExecutionRegistry.for(runtime.event_loop).post(command,
804
+ @environment.registry.post(command,
826
805
  admission: admission, completion: completion)
827
806
  end
828
807
 
@@ -832,8 +811,8 @@ module Phronomy
832
811
  )
833
812
  end
834
813
 
835
- def assert_event_loop!(event_loop)
836
- return if event_loop.current?
814
+ def assert_execution_owner!
815
+ return if @environment.executing?
837
816
 
838
817
  raise Phronomy::Error,
839
818
  "Workflow live-state progression must run on EventLoop"
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Workflow-owned progress limit and optional state repository. Composition
5
+ # supplies these values; existing execution boundaries decide when to read.
6
+ # @api private
7
+ class WorkflowSettings
8
+ class << self
9
+ # Bind at boot without reading settings or starting runtime resources.
10
+ def install_provider(&provider)
11
+ raise ArgumentError, "settings provider requires a block" unless provider
12
+ @provider = provider
13
+ nil
14
+ end
15
+
16
+ # Read only when requested; no cache or change notification is involved.
17
+ def current
18
+ @provider.call
19
+ end
20
+ end
21
+
22
+ attr_accessor :recursion_limit, :workflow_store
23
+
24
+ def initialize(recursion_limit:)
25
+ @recursion_limit = recursion_limit
26
+ end
27
+ end
28
+ end
@@ -9,37 +9,43 @@ module Phronomy
9
9
  def initialize(view) = @view = view
10
10
 
11
11
  def load(workflow_instance_id)
12
- @view.atomic do |bound|
13
- entry = bound.records(Phronomy::WorkflowStorageSchema::STATES).read(workflow_instance_id.to_s)
14
- entry && decode(entry, workflow_instance_id)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ @view.atomic do |bound|
14
+ entry = bound.records(Phronomy::Workflow::Persistence::StorageSchema::STATES).read(workflow_instance_id.to_s)
15
+ entry && decode(entry, workflow_instance_id)
16
+ end
15
17
  end
16
18
  end
17
19
 
18
20
  def save(workflow_instance_id, expected_revision:, snapshot:)
19
- expected = expected_revision.nil? ? nil : Integer(expected_revision)
20
- revision = expected.nil? ? 1 : expected + 1
21
- record = Codec.encode_workflow_state(workflow_instance_id: workflow_instance_id,
22
- workflow_revision: revision, snapshot: snapshot)
23
- @view.atomic do |bound|
24
- records = bound.records(Phronomy::WorkflowStorageSchema::STATES)
25
- values = {key: workflow_instance_id.to_s, attributes: {}, record: record}
26
- entry = if expected.nil?
27
- records.insert(**values, revision: revision)
28
- else
29
- records.replace(**values, expected_revision: expected, next_revision: revision)
30
- end
31
- decoded = decode(entry, workflow_instance_id)
32
- unless decoded.fetch(:revision) == revision
33
- raise Phronomy::Storage::SerializationError, "backend returned another Workflow revision"
21
+ Phronomy::Persistence::StorageBoundary.call do
22
+ expected = expected_revision.nil? ? nil : Integer(expected_revision)
23
+ revision = expected.nil? ? 1 : expected + 1
24
+ record = Codec.encode_workflow_state(workflow_instance_id: workflow_instance_id,
25
+ workflow_revision: revision, snapshot: snapshot)
26
+ @view.atomic do |bound|
27
+ records = bound.records(Phronomy::Workflow::Persistence::StorageSchema::STATES)
28
+ values = {key: workflow_instance_id.to_s, attributes: {}, record: record}
29
+ entry = if expected.nil?
30
+ records.insert(**values, revision: revision)
31
+ else
32
+ records.replace(**values, expected_revision: expected, next_revision: revision)
33
+ end
34
+ decoded = decode(entry, workflow_instance_id)
35
+ unless decoded.fetch(:revision) == revision
36
+ raise Phronomy::Storage::SerializationError, "backend returned another Workflow revision"
37
+ end
38
+ revision
34
39
  end
35
- revision
40
+ rescue Phronomy::Storage::NotFoundError => error
41
+ raise Phronomy::Storage::ConflictError, error.message
36
42
  end
37
- rescue Phronomy::Storage::NotFoundError => error
38
- raise Phronomy::Storage::ConflictError, error.message
39
43
  end
40
44
 
41
45
  def delete(workflow_instance_id, expected_revision:)
42
- @view.records(Phronomy::WorkflowStorageSchema::STATES).delete(key: workflow_instance_id.to_s, expected_revision: Integer(expected_revision))
46
+ Phronomy::Persistence::StorageBoundary.call do
47
+ @view.records(Phronomy::Workflow::Persistence::StorageSchema::STATES).delete(key: workflow_instance_id.to_s, expected_revision: Integer(expected_revision))
48
+ end
43
49
  end
44
50
 
45
51
  private
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Domain-owned Workflow metadata, loadable without the Workflow runtime.
5
+ # @api private
6
+ class Workflow
7
+ module Persistence
8
+ module StorageSchema
9
+ STATES = Phronomy::Storage::Resource.new(id: "workflow.states", kind: :records)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Connects one compiled Workflow to its owning Runtime. Construction and
5
+ # transition compilation do not start workers or register execution state.
6
+ # @api private
7
+ class WorkflowEngineEnvironment < WorkflowExecutionEnvironment
8
+ def initialize(runtime: Phronomy::Runtime.instance)
9
+ @runtime = runtime
10
+ end
11
+
12
+ def registry = WorkflowExecutionRegistry.for(@runtime.event_loop)
13
+ def existing_registry = WorkflowExecutionRegistry.existing_for(@runtime)
14
+ def executing? = @runtime.event_loop.current?
15
+
16
+ def submit(**options, &operation)
17
+ Phronomy::Execution.submit(runtime: @runtime, **options, &operation)
18
+ end
19
+
20
+ def compile_transitions(**definition)
21
+ WorkflowPhaseMachineBuilder.new(**definition).build
22
+ end
23
+
24
+ def build_session(entry_actions:, persist: nil, **options)
25
+ # Preserve nil/scalar results: initial entry must not re-adopt metadata
26
+ # unless the application actually returns a context.
27
+ initial_actions = entry_actions.to_h do |state_name, callbacks|
28
+ [state_name, callbacks.map { |callback| ->(context) { WorkflowActionRules.entry(callback, context, state_name) } }]
29
+ end
30
+ Phronomy::FSMSession.new(**options, entry_actions: initial_actions, event_loop: @runtime.event_loop,
31
+ terminal_policy: persist && WorkflowTerminalPolicy.new(persist: persist))
32
+ end
33
+
34
+ def register_session(session, completion:)
35
+ @runtime.event_loop.register(session, completion: completion, receiver: registry)
36
+ end
37
+ end
38
+ end