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