phronomy 0.26.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 (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
3
5
  require "securerandom"
4
6
 
5
7
  module Phronomy
@@ -12,13 +14,13 @@ module Phronomy
12
14
  # - Runtime admission uses a separate opaque owner token.
13
15
  #
14
16
  # Workflow persistence is synchronous at the repository contract but is always
15
- # invoked through Runtime's OffloadPool. Durable terminal save results return
17
+ # invoked through its owning execution environment. Durable terminal save results return
16
18
  # to the same FSMSession before that session becomes halted/completed.
17
19
  # @api private
18
20
  class WorkflowRunner
19
- include Phronomy::Runnable
21
+ include Phronomy::Concurrency::WorkerInputRestricted
20
22
 
21
- FINISH = :__end__
23
+ include Phronomy::Runnable
22
24
 
23
25
  Execution = Data.define(
24
26
  :context,
@@ -89,7 +91,8 @@ module Phronomy
89
91
  @entry_point = entry_point
90
92
  @wait_state_names = wait_state_names
91
93
  @persistence = persistence
92
- @phase_machine_class = Workflow::PhaseMachineBuilder.new(
94
+ @environment = WorkflowExecutionEnvironment.current
95
+ @phase_machine_class = @environment.compile_transitions(
93
96
  entry_point: @entry_point,
94
97
  declared_states: @declared_states,
95
98
  wait_state_names: @wait_state_names,
@@ -97,7 +100,7 @@ module Phronomy
97
100
  entry_actions: @entry_actions,
98
101
  auto_transitions: auto_transitions,
99
102
  exit_actions: exit_actions
100
- ).build
103
+ )
101
104
  end
102
105
 
103
106
  def invoke(input, config: {})
@@ -138,7 +141,7 @@ module Phronomy
138
141
  end
139
142
 
140
143
  # Posts an application-defined event to the currently live Workflow owner.
141
- # EventLoop resolves workflow_instance_id to the current concrete
144
+ # WorkflowExecutionRegistry resolves workflow_instance_id to the current concrete
142
145
  # fsm_session_id. The opaque admission owner token is deliberately not a
143
146
  # routing identity and is not exposed here.
144
147
  def signal(workflow_instance_id:, event:, payload: nil)
@@ -151,7 +154,10 @@ module Phronomy
151
154
  "Valid events: #{@external_events.keys.inspect}"
152
155
  end
153
156
 
154
- Phronomy::Runtime.instance.event_loop.post_to_workflow(
157
+ registry = @environment.existing_registry
158
+ return false unless registry
159
+
160
+ registry.post_to_workflow(
155
161
  workflow_instance_id: workflow_instance_id,
156
162
  event: event_name,
157
163
  payload: payload
@@ -163,8 +169,7 @@ module Phronomy
163
169
  # progression therefore remain EventLoop-owned.
164
170
  # @api private
165
171
  def deliver_on_event_loop(command)
166
- event_loop = Phronomy::Runtime.instance.event_loop
167
- assert_event_loop!(event_loop)
172
+ assert_execution_owner!
168
173
 
169
174
  case command
170
175
  when StartCommand
@@ -206,7 +211,7 @@ module Phronomy
206
211
  end
207
212
 
208
213
  def ensure_blocking_call_allowed!(method_name, async_alternative)
209
- return unless Phronomy::Runtime.instance.event_loop.current?
214
+ return unless Phronomy::WaitPolicy.blocking_forbidden?
210
215
 
211
216
  raise Phronomy::Error,
212
217
  "Cannot call Workflow##{method_name} from the EventLoop thread. " \
@@ -215,7 +220,6 @@ module Phronomy
215
220
 
216
221
  def start_new_execution(input, config, stable_observer: nil)
217
222
  reject_legacy_workflow_identity_key!(config)
218
- runtime = Phronomy::Runtime.instance
219
223
  result_task = Phronomy::TaskResult.deferred(name: "workflow:preparing")
220
224
  explicit_workflow_instance_id = !config[:workflow_instance_id].nil?
221
225
  workflow_instance_id = (config[:workflow_instance_id] || SecureRandom.uuid).to_s.freeze
@@ -237,7 +241,7 @@ module Phronomy
237
241
  owner_token: Object.new.freeze,
238
242
  explicit_workflow_instance_id: explicit_workflow_instance_id
239
243
  )
240
- 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)
241
245
  result_task
242
246
  rescue => error
243
247
  fail_task(result_task, error) if defined?(result_task) && result_task
@@ -245,7 +249,6 @@ module Phronomy
245
249
  end
246
250
 
247
251
  def start_resume_execution(state, input:, event_name:, current_phase:)
248
- runtime = Phronomy::Runtime.instance
249
252
  workflow_instance_id = state.workflow_instance_id.to_s.freeze
250
253
  result_task = Phronomy::TaskResult.deferred(name: "workflow-resume:#{workflow_instance_id}")
251
254
  Phronomy::Tracing::Automatic.observe_task(
@@ -266,7 +269,7 @@ module Phronomy
266
269
  workflow_instance_id: workflow_instance_id,
267
270
  owner_token: Object.new.freeze
268
271
  )
269
- 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)
270
273
  result_task
271
274
  rescue => error
272
275
  fail_task(result_task, error) if defined?(result_task) && result_task
@@ -274,10 +277,8 @@ module Phronomy
274
277
  end
275
278
 
276
279
  def begin_start_on_event_loop(request)
277
- runtime = Phronomy::Runtime.instance
278
- event_loop = runtime.event_loop
279
280
  admitted = false
280
- event_loop.admit_workflow(
281
+ @environment.registry.admit_workflow(
281
282
  request.workflow_instance_id,
282
283
  owner_token: request.owner_token
283
284
  )
@@ -285,7 +286,7 @@ module Phronomy
285
286
 
286
287
  recursion_limit = request.config.fetch(
287
288
  :recursion_limit,
288
- Phronomy.configuration.recursion_limit
289
+ Phronomy::WorkflowSettings.current.recursion_limit
289
290
  )
290
291
  repository = configured_repository
291
292
  persist = request.explicit_workflow_instance_id && !repository.nil?
@@ -310,7 +311,6 @@ module Phronomy
310
311
  end
311
312
 
312
313
  submit_workflow_load(
313
- runtime,
314
314
  repository,
315
315
  request.workflow_instance_id,
316
316
  StartLoadReady,
@@ -318,7 +318,7 @@ module Phronomy
318
318
  )
319
319
  rescue => error
320
320
  if admitted
321
- event_loop.release_workflow(
321
+ @environment.registry.release_workflow(
322
322
  request.workflow_instance_id,
323
323
  owner_token: request.owner_token
324
324
  )
@@ -327,10 +327,8 @@ module Phronomy
327
327
  end
328
328
 
329
329
  def begin_resume_on_event_loop(request)
330
- runtime = Phronomy::Runtime.instance
331
- event_loop = runtime.event_loop
332
330
  admitted = false
333
- event_loop.admit_workflow(
331
+ @environment.registry.admit_workflow(
334
332
  request.workflow_instance_id,
335
333
  owner_token: request.owner_token
336
334
  )
@@ -343,7 +341,7 @@ module Phronomy
343
341
  context: context,
344
342
  workflow_instance_id: request.workflow_instance_id,
345
343
  owner_token: request.owner_token,
346
- recursion_limit: Phronomy.configuration.recursion_limit,
344
+ recursion_limit: Phronomy::WorkflowSettings.current.recursion_limit,
347
345
  repository: nil,
348
346
  persist: false,
349
347
  expected_revision: nil
@@ -358,7 +356,6 @@ module Phronomy
358
356
  end
359
357
 
360
358
  submit_workflow_load(
361
- runtime,
362
359
  repository,
363
360
  request.workflow_instance_id,
364
361
  ResumeLoadReady,
@@ -366,7 +363,7 @@ module Phronomy
366
363
  )
367
364
  rescue => error
368
365
  if admitted
369
- event_loop.release_workflow(
366
+ @environment.registry.release_workflow(
370
367
  request.workflow_instance_id,
371
368
  owner_token: request.owner_token
372
369
  )
@@ -374,12 +371,12 @@ module Phronomy
374
371
  fail_task(request.result_task, error)
375
372
  end
376
373
 
377
- def submit_workflow_load(runtime, repository, workflow_instance_id, ready_class, request)
374
+ def submit_workflow_load(repository, workflow_instance_id, ready_class, request)
378
375
  operation = WorkflowLoadCommand.new(
379
376
  repository: repository,
380
377
  workflow_instance_id: workflow_instance_id.to_s.freeze
381
378
  )
382
- task = runtime.offload.submit(on_full: :raise) do
379
+ task = @environment.submit(on_full: :raise) do
383
380
  record = operation.repository.load(operation.workflow_instance_id)
384
381
  WorkflowLoadResult.new(
385
382
  repository: operation.repository,
@@ -395,7 +392,7 @@ module Phronomy
395
392
  error: error
396
393
  )
397
394
  fail_task(request.result_task, runtime_rejected_error(:load_result)) unless
398
- post_control(runtime, ready)
395
+ post_control(ready)
399
396
  end
400
397
  nil
401
398
  end
@@ -409,7 +406,7 @@ module Phronomy
409
406
 
410
407
  recursion_limit = request.config.fetch(
411
408
  :recursion_limit,
412
- Phronomy.configuration.recursion_limit
409
+ Phronomy::WorkflowSettings.current.recursion_limit
413
410
  )
414
411
  execution = build_new_execution(
415
412
  request.input,
@@ -447,7 +444,7 @@ module Phronomy
447
444
  context: context,
448
445
  workflow_instance_id: request.workflow_instance_id,
449
446
  owner_token: request.owner_token,
450
- recursion_limit: Phronomy.configuration.recursion_limit,
447
+ recursion_limit: Phronomy::WorkflowSettings.current.recursion_limit,
451
448
  repository: ready.result.repository,
452
449
  persist: true,
453
450
  expected_revision: expected_revision
@@ -503,7 +500,7 @@ module Phronomy
503
500
  local_snapshot = normalize_snapshot(snapshot_for(state))
504
501
  return durable_revision if normalized_durable == local_snapshot
505
502
 
506
- raise Phronomy::Persistence::ConflictError,
503
+ raise Phronomy::Persistence::StateConflictError,
507
504
  "Workflow state changed since the supplied halted context for " \
508
505
  "workflow_instance_id #{state.workflow_instance_id.inspect}; explicit reload/reconciliation is required"
509
506
  end
@@ -555,8 +552,7 @@ module Phronomy
555
552
  end
556
553
 
557
554
  def configured_repository
558
- persistence = @persistence || Phronomy.configuration.persistence
559
- persistence&.workflow_states
555
+ @persistence || Phronomy::WorkflowSettings.current.workflow_store
560
556
  end
561
557
 
562
558
  def register_execution(
@@ -566,17 +562,14 @@ module Phronomy
566
562
  resume_phase: nil,
567
563
  stable_observer: nil
568
564
  )
569
- runtime = Phronomy::Runtime.instance
570
- event_loop = runtime.event_loop
571
- assert_event_loop!(event_loop)
565
+ assert_execution_owner!
572
566
  session = build_session_for(
573
567
  execution: execution,
574
- runtime: runtime,
575
568
  resume_event: resume_event,
576
569
  resume_phase: resume_phase,
577
570
  stable_observer: stable_observer
578
571
  )
579
- event_loop.bind_workflow_session(
572
+ @environment.registry.bind_workflow_session(
580
573
  execution.workflow_instance_id,
581
574
  owner_token: execution.owner_token,
582
575
  fsm_session_id: session.id
@@ -591,11 +584,11 @@ module Phronomy
591
584
  error: error
592
585
  )
593
586
  end
594
- event_loop.register(session, completion: source_task)
587
+ @environment.register_session(session, completion: source_task)
595
588
  result_task
596
589
  rescue => error
597
- if event_loop&.current?
598
- event_loop&.release_workflow(
590
+ if @environment.executing?
591
+ @environment.registry.release_workflow(
599
592
  execution.workflow_instance_id,
600
593
  owner_token: execution.owner_token
601
594
  )
@@ -606,9 +599,8 @@ module Phronomy
606
599
  # Called from source_task completion on EventLoop, after the FSMSession has
607
600
  # accepted the durable terminal result (when a durable barrier is required).
608
601
  def finalize_execution(execution:, result_task:, result:, error:)
609
- event_loop = Phronomy::Runtime.instance.event_loop
610
- assert_event_loop!(event_loop)
611
- event_loop.release_workflow(
602
+ assert_execution_owner!
603
+ @environment.registry.release_workflow(
612
604
  execution.workflow_instance_id,
613
605
  owner_token: execution.owner_token
614
606
  )
@@ -621,76 +613,117 @@ module Phronomy
621
613
  context:,
622
614
  event_sink:
623
615
  )
624
- runtime = Phronomy::Runtime.instance
625
- event_loop = runtime.event_loop
626
- assert_event_loop!(event_loop)
627
- event_loop.mark_workflow_admission(
616
+ assert_execution_owner!
617
+ @environment.registry.mark_workflow_admission(
628
618
  execution.workflow_instance_id,
629
619
  owner_token: execution.owner_token,
630
620
  state: :persisting_terminal
631
621
  )
632
622
 
633
- operation = WorkflowTerminalPersistenceCommand.new(
623
+ operation = Phronomy::WorkflowRunner::WorkflowTerminalPersistenceCommand.new(
634
624
  repository: execution.repository,
635
625
  workflow_instance_id: execution.workflow_instance_id.to_s.freeze,
636
626
  expected_revision: execution.expected_revision,
637
627
  snapshot: deep_immutable_copy(snapshot_for(context))
638
628
  )
639
- task = runtime.offload.submit(on_full: :raise) do
640
- revision = operation.repository.save(
641
- operation.workflow_instance_id,
642
- expected_revision: operation.expected_revision,
643
- snapshot: operation.snapshot
629
+
630
+ task = @environment.submit(on_full: :raise) do
631
+ persist_terminal_snapshot(operation)
632
+ end
633
+ task.on_complete do |result, operation_error|
634
+ deliver_terminal_persistence_result(execution, event_sink, result, operation_error)
635
+ end
636
+ terminal_type
637
+ end
638
+
639
+ def persist_terminal_snapshot(operation)
640
+ revision = operation.repository.save(
641
+ operation.workflow_instance_id,
642
+ expected_revision: operation.expected_revision,
643
+ snapshot: operation.snapshot
644
+ )
645
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
646
+ outcome: :success,
647
+ revision: revision,
648
+ error: nil
649
+ )
650
+ rescue Phronomy::Persistence::ConflictError,
651
+ Phronomy::Persistence::NotFoundError,
652
+ Phronomy::Persistence::SerializationError,
653
+ Phronomy::Persistence::UnsupportedBackendError => error
654
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
655
+ outcome: :known_failure,
656
+ revision: nil,
657
+ error: error
658
+ )
659
+ rescue => error
660
+ reconcile_workflow_terminal_f1(operation, error)
661
+ end
662
+
663
+ def deliver_terminal_persistence_result(execution, event_sink, result, operation_error)
664
+ delivery = if operation_error
665
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
666
+ outcome: :outcome_unknown,
667
+ revision: nil,
668
+ error: operation_error
669
+ )
670
+ else
671
+ result
672
+ end
673
+ accepted = event_sink.post(:workflow_terminal_persistence_result, delivery)
674
+ unless accepted
675
+ Phronomy::RuntimeSettings.current.logger&.warn(
676
+ "[Phronomy] EventLoop rejected Workflow terminal persistence result " \
677
+ "for #{execution.workflow_instance_id.inspect}"
644
678
  )
645
- WorkflowTerminalPersistenceResult.new(
679
+ end
680
+ end
681
+
682
+ def reconcile_workflow_terminal_f1(operation, original_error)
683
+ record = operation.repository.load(operation.workflow_instance_id)
684
+ case Phronomy::Persistence::SnapshotComparison.compare_revisioned_snapshot(
685
+ record: record,
686
+ expected_pre_revision: operation.expected_revision,
687
+ intended_snapshot: operation.snapshot
688
+ )
689
+ when :post_state
690
+ revision = Phronomy::Persistence::SnapshotComparison.fetch_value(record, :revision)
691
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
646
692
  outcome: :success,
647
693
  revision: revision,
648
694
  error: nil
649
695
  )
650
- rescue Phronomy::Persistence::ConflictError,
651
- Phronomy::Persistence::NotFoundError,
652
- Phronomy::Persistence::SerializationError,
653
- Phronomy::Persistence::UnsupportedBackendError => error
654
- WorkflowTerminalPersistenceResult.new(
696
+ when :pre_state
697
+ # The uncertain save is now known not to have committed. Do not blindly
698
+ # retry it here; surface a known failure through the ordinary terminal
699
+ # barrier semantics.
700
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
655
701
  outcome: :known_failure,
656
702
  revision: nil,
657
- error: error
703
+ error: original_error
704
+ )
705
+ else
706
+ conflict = Phronomy::Persistence::StateConflictError.new(
707
+ "Workflow terminal Persistence outcome conflicts with both expected " \
708
+ "pre-state and intended post-state for #{operation.workflow_instance_id.inspect}"
658
709
  )
659
- rescue => error
660
- # The Backend SPI does not currently provide a portable commit-outcome
661
- # classifier for arbitrary storage/transport failures. If non-commit is
662
- # not guaranteed by the Phronomy error contract, fail closed as F1.
663
- WorkflowTerminalPersistenceResult.new(
710
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
664
711
  outcome: :outcome_unknown,
665
712
  revision: nil,
666
- error: error
713
+ error: conflict
667
714
  )
668
715
  end
669
- task.on_complete do |result, operation_error|
670
- delivery = if operation_error
671
- WorkflowTerminalPersistenceResult.new(
672
- outcome: :outcome_unknown,
673
- revision: nil,
674
- error: operation_error
675
- )
676
- else
677
- result
678
- end
679
- accepted = event_sink.post(:workflow_terminal_persistence_result, delivery)
680
- unless accepted
681
- Phronomy.configuration.logger&.warn(
682
- "[Phronomy] EventLoop rejected Workflow terminal persistence result " \
683
- "for #{execution.workflow_instance_id.inspect}"
684
- )
685
- end
686
- end
687
- terminal_type
716
+ rescue => reconciliation_error
717
+ Phronomy::WorkflowRunner::WorkflowTerminalPersistenceResult.new(
718
+ outcome: :outcome_unknown,
719
+ revision: nil,
720
+ error: reconciliation_error
721
+ )
688
722
  end
689
723
 
690
724
  def release_and_fail(request, error)
691
- event_loop = Phronomy::Runtime.instance.event_loop
692
- assert_event_loop!(event_loop)
693
- event_loop.release_workflow(
725
+ assert_execution_owner!
726
+ @environment.registry.release_workflow(
694
727
  request.workflow_instance_id,
695
728
  owner_token: request.owner_token
696
729
  )
@@ -706,23 +739,11 @@ module Phronomy
706
739
 
707
740
  def build_session_for(
708
741
  execution:,
709
- runtime:,
710
742
  resume_event: nil,
711
743
  resume_phase: nil,
712
744
  stable_observer: nil
713
745
  )
714
- terminal_barrier = if execution.repository && execution.persist
715
- ->(terminal_type:, context:, event_sink:) {
716
- begin_terminal_persistence_on_event_loop(
717
- execution,
718
- terminal_type: terminal_type,
719
- context: context,
720
- event_sink: event_sink
721
- )
722
- }
723
- end
724
-
725
- Phronomy::FSMSession.new(
746
+ @environment.build_session(
726
747
  context: execution.context,
727
748
  context_metadata: {
728
749
  workflow_instance_id: execution.workflow_instance_id
@@ -735,14 +756,26 @@ module Phronomy
735
756
  external_events: @external_events,
736
757
  phase_machine_class: @phase_machine_class,
737
758
  recursion_limit: execution.recursion_limit,
738
- event_loop: runtime.event_loop,
739
759
  resume_event: resume_event,
740
760
  resume_phase: resume_phase,
741
761
  stable_observer: stable_observer,
742
- terminal_barrier: terminal_barrier
762
+ persist: terminal_persistence_callback(execution)
743
763
  )
744
764
  end
745
765
 
766
+ def terminal_persistence_callback(execution)
767
+ return unless execution.repository && execution.persist
768
+
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
+ }
777
+ end
778
+
746
779
  def resolve_resume_event(current_phase, event)
747
780
  event_name = event.to_sym
748
781
  unless event_name == :resume
@@ -765,14 +798,11 @@ module Phronomy
765
798
  "No external event registered for state #{current_phase.inspect}"
766
799
  end
767
800
 
768
- def post_control(runtime, command)
769
- runtime.event_loop.post(
770
- Phronomy::Event.new(
771
- type: :workflow_control,
772
- target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID,
773
- payload: {command: command}
774
- )
775
- )
801
+ def post_control(command)
802
+ admission = command.is_a?(StartCommand) || command.is_a?(ResumeCommand)
803
+ completion = admission ? command.result_task : command.request.result_task
804
+ @environment.registry.post(command,
805
+ admission: admission, completion: completion)
776
806
  end
777
807
 
778
808
  def runtime_rejected_error(action)
@@ -781,8 +811,8 @@ module Phronomy
781
811
  )
782
812
  end
783
813
 
784
- def assert_event_loop!(event_loop)
785
- return if event_loop.current?
814
+ def assert_execution_owner!
815
+ return if @environment.executing?
786
816
 
787
817
  raise Phronomy::Error,
788
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