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,5 +1,11 @@
1
1
  # Agent Chat and explicit state ownership (R09, second slice)
2
2
 
3
+ > Current boundary: r8 unit 6 supersedes the historical Chat/SDK ownership and
4
+ > adapter extension SPI below. Agent uses LLMAdapter values; the backend owns SDK
5
+ > construction/interception; Tool owns canonical schema validation. See
6
+ > `docs/architecture/r8-unit6.md` and `docs/migrations/r8-unit6.md`.
7
+
8
+
3
9
  ## Purpose and clients
4
10
 
5
11
  Agent::Base is the declaration and lifecycle facade used by application Agent
@@ -62,7 +68,7 @@ hook retains application/test overrides of apply_instructions and Orchestrator's
62
68
  prepare_tool_class(invocation:) cooperation. Moving it into a builder receiving
63
69
  the entire Agent would merely introduce callbacks back into Base.
64
70
 
65
- AgentInvocationSessionBuilder and InvocationRestorer keep using the same hooks.
71
+ InvocationActions and EngineSessionBuilder and InvocationRestorer keep using the same hooks.
66
72
  Saved config is authoritative during recovery; current class declarations do
67
73
  not overwrite it. A failure stops installation at that operation, propagates
68
74
  the same error and does not install later Tools/messages. No new Chat cache,
@@ -1,89 +1,13 @@
1
- # Agent invocation transition ownership
2
-
3
- ## Role and scope
4
-
5
- An Agent invocation moves between preparation, Provider calls, Tool processing,
6
- approval suspension and completion. Three collaborators need the same policy:
7
- AgentInvocation consumes external payloads; PhaseMachineBuilder creates the
8
- executable state machine; AgentInvocationSessionBuilder configures FSMSession's
9
- advance, wait and terminal boundaries. Independently maintained event lists and
10
- transition copies could disagree about what the current phase can accept.
11
-
12
- Refactor 39 gives that shared policy one private owner, InvocationTransitions in
13
- agent/execution. This is Agent data in the existing transition Hash format, not
14
- a new cross-feature DSL or Engine extension. It is not a new execution object.
15
-
16
- ## Ownership and reading order
17
-
18
- | Owner | Responsibility |
19
- |---|---|
20
- | InvocationTransitions | Initial phase, declared/automatic/approval-wait states, Tool event vocabulary and ordered external transition definitions. |
21
- | PhaseMachineBuilder | Translate those external definitions to state_machines; retain automatic state_completed transitions and synchronous entry-action validation. |
22
- | AgentInvocationSessionBuilder | Assemble the invocation, event sink, entry actions and machine; pass shared state and transition metadata to FSMSession. |
23
- | AgentInvocation | Apply payloads and expose the current guard predicates; use the shared Tool event vocabulary. |
24
- | FSMSession | Generic advancement, event delivery and wait/terminal handling. No Agent-specific decisions. |
25
-
26
- Read the policy table for legal external sources and guard priority, the machine
27
- builder for execution mechanics, and the session builder for action wiring.
28
- The EXTERNAL_EVENTS Hash, each transition array and each row are frozen so one
29
- session cannot edit another session's policy. Guard lambdas contain no captured
30
- invocation state; they inspect the context supplied at transition time.
31
- Old private constants and the external_events factory are removed without aliases.
32
-
33
- ## Preserved event and phase contract
34
-
35
- The six Tool events are authorized, approval_required, completed, failed,
36
- rejected and cancelled, each with the tool_ prefix. Each moves waiting_for_tools
37
- to evaluating_tools. LLM completion from calling_llm tries these in order:
38
-
39
- 1. callback_failed? to failed;
40
- 2. handoff_failed? to failed;
41
- 3. handoff_requested? to handed_off;
42
- 4. tool_call_pending? to starting_tools;
43
- 5. unconditional fallback to output_filtering.
44
-
45
- Guards short-circuit. A nil context skips guarded rows and takes the fallback;
46
- guard exceptions propagate unchanged. Each evaluation uses the latest context,
47
- without caching a selected destination. Session applies the event payload first.
48
-
49
- LLM failure/setup failure leads from calling_llm to failed. Tool setup failure
50
- leads from dispatching_tools to failed, while tool_dispatch_prepared returns it
51
- to evaluating_tools. Resume moves suspended to waiting_for_tools. Application
52
- callback failure is accepted from the existing nine active phases only.
53
-
54
- Automatic phases remain idle, filtering_input, building_context, starting_tools,
55
- evaluating_tools, recording_tool_results and output_filtering. calling_llm,
56
- waiting_for_tools and dispatching_tools wait for external events. suspended ends
57
- a segment with halted; resume uses a fresh Session identity. The four declared
58
- terminal phases remain handed_off, completed, blocked and failed.
59
-
60
- FSMSession reads only the from field when determining external-event admission
61
- and waiting. It does not execute metadata guards or choose their destinations;
62
- state_machines does that. Known-but-undeclared and unknown events retain their
63
- existing error behavior; context-consumed stale events retain their early exit.
64
- The internal order of event registration now follows the shared table. Transition
65
- order within each event, which determines behavior, is preserved. The generated
66
- machine's private event-enumeration order is not a supported public contract.
67
-
68
- ## Verification and limits
69
-
70
- Independent behavioral tests run on both Refactor 38 and the candidate: all 195
71
- external-event/source-state combinations, all sixteen LLM condition combinations
72
- including short-circuit order, nil context and guard exceptions, fifteen Session
73
- boundaries, context-before-guard ordering, approval suspension/resume/dispatch,
74
- and invalid source-state handling. Existing Agent, causal durability, Handoff,
75
- Workflow and Engine tests continue to cover their collaborating behavior.
76
-
77
- The candidate also runs the full core/integration suites, offline examples and
78
- real SQLite, API/SPI snapshots, RBS, style, annotations and isolated gem checks.
79
- No SQL, stored format, entry action implementation, Tool restoration, cancellation
80
- or Coordinator sequencing is changed. The new contract tests isolate entry actions
81
- and do not claim live Provider coverage. Live PostgreSQL/LLM and remote CI are
82
- not executed for this candidate. Refactor 39 was independently verified after application at core 4d57614a,
83
- tree 654241da4b2b7f32d988602d28127bc1fa155641. All 10 files and the full tree
84
- matched, and core/integration/examples/SQLite/API/type/gem checks passed.
85
- The published SVG is applied39-01.
86
-
87
- R06 is applied and complete. R07 now has a Refactor 40 candidate; R08/D08, R09
88
- and R10 remain afterward. R09's inheritance behavior and R10's action names
89
- are not altered by the transition-ownership work.
1
+ # Agent transition ownership (r8 unit 7)
2
+
3
+ InvocationTransitions owns both automatic and external transitions, including
4
+ ordered guard evaluation. InvocationActions owns Agent entry operations.
5
+ EngineSessionBuilder configures FSMSession using those definitions;
6
+ PhaseMachineBuilder adapts them to state_machines without defining progress rules.
7
+ ExecutionCoordinator and RecoveryCoordinator retain durable decisions and use
8
+ the Agent-owned ExecutionEnvironment port for execution connection.
9
+
10
+ See [r8 unit 7](r8-unit7.md) and [ADR-067](../decisions/067-agent-progress-and-engine-connection.md).
11
+ Existing event source validation, callback failure priority, short-circuiting,
12
+ Tool outcome priority, suspended/wait/terminal boundaries and session incarnation
13
+ rules are preserved. Agent's Tool child FSM is still a separate remaining task.
@@ -0,0 +1,152 @@
1
+ # Backend responsibilities and asynchronous execution clients
2
+
3
+ The diagram distinguishes responsibility **groups** from its display layers.
4
+ The earlier B1-B6 horizontal bands and topical columns are restored for
5
+ readability. `G14`, `G46`, `G47` and `G48` remain stable responsibility identifiers;
6
+ boundary permissions follow those responsibilities, not B/G numbers or positions.
7
+ Each source directory keeps its own module box and stable module ID. Restoring
8
+ the display does not undo the separation of backend contracts and execution.
9
+
10
+ The restored display puts separated Backend Contracts in B5, alongside but
11
+ distinct from Engine Internals and Execution Contracts. B4 holds Async Clients and separately grouped Implementations. Execution
12
+ operations and values now share G58; the former G59 service split is removed. Backend directories awaiting separation remain visibly mixed
13
+ in B4; their contracts move to B5 in their implemented phase. These are display
14
+ positions, not a shared architectural level or namespace.
15
+
16
+ | Group | Responsibility | Dependency rule |
17
+ |---|---|---|
18
+ | G14 Engine Internals | Runtime, workers, FSM and EventLoop | No feature-specific backend or client dependencies |
19
+ | G58 Execution Framework | Submission protocol, TaskResult, Execution, Blocking, controls and common rules | Only Execution and Common, including neutral settings; Engine implements the mechanism |
20
+ | G46 Async Clients | Execute synchronous backend operations through Execution | Use Backend and Execution contracts; no direct Engine or concrete backend selection |
21
+ | G47 Backend Contracts | Synchronous SPI, values, errors and shared rules | No Engine, Execution Services, Async Client or concrete implementation dependencies |
22
+ | G48 Backend Implementations | Implement the synchronous backend SPI | Depend on the corresponding Contracts; no Engine, Execution Services or client dependencies |
23
+ | G57 Content Service | Content IDs, canonical text/JSON and digest integrity over Storage | Uses neutral Storage and Common; no physical backend, Engine or domain orchestration dependencies |
24
+
25
+ Composition chooses concrete backends and supplies them to consumers. These
26
+ are source-ownership groups, not shared Ruby namespaces. Synchronous APIs can
27
+ be called directly. Diagram links represent source references; calls through
28
+ injected objects require separate runtime review.
29
+
30
+ ## Implemented phase: P1/P2/P3/P4 (`storage`)
31
+
32
+ P3 adds public VectorStore and Embeddings AsyncClients. Their synchronous
33
+ contracts and implementations have no Engine dependencies. See the
34
+ [public receiver migration](../migrations/vector-async-clients.md).
35
+
36
+ ### P2: LLM
37
+
38
+ - Install the AST analyzer, group annotations, SVG formatter and boundary gate
39
+ in `tools/architecture/`. The Architecture workflow checks the committed
40
+ phase and generates evidence for its actual checkout SHA.
41
+ - `LLMAdapter::Base` now defines synchronous generation plus chat construction,
42
+ configuration, message/tool-call values, identity and optional input budget.
43
+ RubyLLM-specific construction and error translation belong to its backend.
44
+ Adapter extensions must implement the new operations; no legacy wrapper remains.
45
+ - `LLMAdapter::AsyncClient` owns the internal `complete_async` and `stream_async`
46
+ operations in `llm_adapter/async/`. It is not a public extension API.
47
+ - `LLMAdapter::RubyLLM` lives in `llm_adapter/backends/` and still inherits Base.
48
+ Its public name, synchronous signatures and default configuration are unchanged.
49
+ - Zeitwerk collapses these two new directories. Neither `Phronomy::Async` nor
50
+ `LLMAdapter::Async` / `LLMAdapter::Backends` is introduced.
51
+ - Agent constructs the framework client around the configured synchronous
52
+ adapter. Custom adapters implement no async methods and need no pool logic.
53
+
54
+ The client preserves `on_full: :raise`, the original pool TaskResult, token
55
+ forwarding and per-chunk cancellation. Transport timeout/retry stays with the
56
+ adapter/provider. Construction starts no Runtime, and the default pool is
57
+ resolved again on each operation; an injected pool stays caller-owned. Only an
58
+ internal lightweight sink runs on the worker; application event callbacks
59
+ continue to run on EventLoop.
60
+
61
+ Use `require "phronomy"` and the public constants. The old implementation path
62
+ `phronomy/llm_adapter/ruby_llm` moves to
63
+ `phronomy/llm_adapter/backends/ruby_llm`; arbitrary partial-load paths are not the
64
+ public loading contract. No shim in the contract directory reintroduces an
65
+ implementation dependency. No public RBS or Stable/Beta API snapshot changes
66
+ are needed in this phase; both are verified unchanged.
67
+
68
+ ### P3: VectorStore and Embeddings
69
+
70
+ - `VectorStore::AsyncClient` owns public add/search/remove/clear async operations;
71
+ `VectorStore::Embeddings::AsyncClient` owns public `embed_async`.
72
+ - Backends implement only synchronous methods. The old `AsyncBackend` mixin
73
+ and inherited async methods are removed; application callers change receiver.
74
+ - InMemory, Pgvector, RedisSearch and RubyLLMEmbeddings move into `backends/`
75
+ directories. Their public constants, synchronous signatures and bodies stay
76
+ unchanged. Zeitwerk explicitly collapses the new `async/` and `backends/` paths.
77
+ - Synchronous RBS uses structural `_CancellationSignal`; async signatures keep
78
+ TaskResult and the execution token. The public API snapshot records the new
79
+ receivers and the intentional removals from Base/InMemory.
80
+ - All three original backend-to-Engine edges are removed. In the measured view,
81
+ M28/M29 join G47 Contracts in B5. M67/M68 are clients in B4 and M71/M72 are
82
+ separately grouped implementations. No pending Storage clients are drawn.
83
+
84
+ ### P4: Storage execution
85
+
86
+ - Public `Storage::AsyncClient#transaction_async` opens the raw backend transaction
87
+ on a worker and yields its scoped Storage::View. Backends stay synchronous.
88
+ - Private `Storage::AsyncClient.submit(pool:)` executes existing domain units
89
+ without adding a transaction, timeout or token. All 16 durable submission sites
90
+ are recorded in the [execution inventory](storage-execution-boundaries.md).
91
+ - M69 is an Async Client and M35 a concrete Storage backend in B4.
92
+ Existing `Storage::Backends` is not collapsed.
93
+ - See the [public API and transaction rules](../migrations/storage-async-client.md).
94
+
95
+ ### ContentStore service ownership
96
+
97
+ ContentStore is a content management service using Storage, not a peer physical
98
+ backend. Its API, canonicalization, StoredContents implementation and owned
99
+ StorageSchema live in `content_store/`. Base remains shared by StoredContents
100
+ and the Persistence exception facade. The unnecessary `backends/` subdivision
101
+ and Zeitwerk collapse are removed without a forwarding file or alias.
102
+
103
+ M10 belongs to G57 Content Service in the persistence column of display band B3.
104
+ M73 is retired, not reused. G47/G48 contain no ContentStore modules. The real
105
+ M10-to-Storage dependency remains in both the drawing and the full graph.
106
+ The boundary gate rejects reverse Storage-to-ContentStore dependencies and any
107
+ service path to a physical backend, Engine, client or domain orchestration.
108
+ Public constants, signatures, content IDs, exceptions and transaction behavior
109
+ remain unchanged. See [ADR-062](../decisions/062-content-store-service.md).
110
+
111
+ ## r8 unit 1 framework boundaries
112
+
113
+ G58/M76/M77 now owns the common Execution framework under `execution/`.
114
+ Engine owns FSM-specific values and the pool/timer/runtime binding, while
115
+ composition selects that binding. Tracing owns explicit observation. Context
116
+ and Tool now own their shared operations, not merely value/error declarations.
117
+ The former G59/M78 split is retired. See [r8 unit 1](r8-unit1.md) for the exact
118
+ implemented scope, API removals and incomplete Persistence/domain work.
119
+
120
+ The full architecture still contains existing cycles outside the checked scope.
121
+ The recorded graph is not an ideal final r8 diagram.
122
+
123
+ ## Pending phase
124
+
125
+ P5 completes the final cross-repository/API/docs review after application.
126
+ All retained P2-P4 responsibilities exist in source; the unnecessary M73 split
127
+ is superseded by G57 service ownership. The measured SVG still comes from
128
+ actual Ruby analysis; the target concept is a separate explanatory artifact.
129
+
130
+ ## Regenerate and review
131
+
132
+ See [tool instructions](../../tools/architecture/README.md). The normal command
133
+ reads `tools/architecture/phase.json`:
134
+
135
+ ```sh
136
+ python tools/architecture/refresh_diagram.py . tmp/architecture
137
+ ```
138
+
139
+ The output includes the matrix, source hashes, boundary report, module/file
140
+ cycles and the delta from baseline 84668606. Source/module/group changes require
141
+ matching annotation updates. No up/down rule or coordinate controls the gate.
142
+ Ruby loader, RBS, cancellation, admission, streaming and configuration tests
143
+ remain necessary: static analysis does not replace them.
144
+
145
+ An unpublished local candidate must use `--candidate`. Its SVG states that it
146
+ is unapplied and disables GitHub source links. Applied commits and CI output
147
+ use actual source links. CI artifacts are generated after the source commit;
148
+ there is no self-referential SVG commit hash or automatic commit/push.
149
+
150
+ Text panels are transparent and module outlines/group colors remain intact.
151
+ Arrows into M33/M41/M44 are hidden by presentation settings; their AST edges,
152
+ source evidence and matrix cells are preserved. Boundary rules inspect all edges.
@@ -28,10 +28,10 @@ candidates are accumulated in hook order.
28
28
 
29
29
  ## 3. Input and result
30
30
 
31
- Hooks receive immutable `Phronomy::Agent::LLMInputBuildContext` metadata. They do
31
+ Hooks receive immutable `Phronomy::Context::LLMInputBuildContext` metadata. They do
32
32
  not receive a mutable Provider chat/message array.
33
33
 
34
- A hook returns `Phronomy::Agent::LLMInputPatch` or `nil`.
34
+ A hook returns `Phronomy::Context::LLMInputPatch` or `nil`.
35
35
 
36
36
  These two types live in the shared `agent/context_contract/` implementation
37
37
  directory together with the Policy input and result contracts. Zeitwerk
@@ -40,7 +40,7 @@ names. Hook registration, invocation, and result handling are unchanged; see
40
40
  [ADR-036](../decisions/036-context-contract-ownership.md).
41
41
 
42
42
  ```ruby
43
- Phronomy::Agent::LLMInputPatch.new(
43
+ Phronomy::Context::LLMInputPatch.new(
44
44
  model_config_patch: {temperature: 0.2},
45
45
  segment_candidates: [
46
46
  {
@@ -85,7 +85,7 @@ same abstraction level.
85
85
  Application code supplies an ordinary reusable Ruby strategy object:
86
86
 
87
87
  ```ruby
88
- class SearchContextPolicy < Phronomy::Agent::ContextPolicy
88
+ class SearchContextPolicy < Phronomy::Context::ContextPolicy
89
89
  def initialize(search:)
90
90
  @search = search
91
91
  end
@@ -0,0 +1,119 @@
1
+ # Agent scope and incarnation lifetime (DX05-E core)
2
+
3
+ An application decides how long it needs an Agent. C04 owns the mechanics for
4
+ acquiring that incarnation and releasing it safely. A scope does not delete saved
5
+ Agents, stop other Agents, close an application-owned Store, or infer whether a
6
+ business operation should be retried.
7
+
8
+ ## Application change
9
+
10
+ Before, a long-running application could create Agents for each request, but
11
+ the Runtime retained their mutable incarnations until shutdown. `close!` changes
12
+ the durable Agent lifecycle; `purge!` deletes saved state. Neither is a substitute
13
+ for releasing a process-local incarnation.
14
+
15
+ ```ruby
16
+ agent = ReviewAgent.create(persistence: store)
17
+ agent.invoke(text) # the Runtime still owns this incarnation afterwards
18
+ ```
19
+
20
+ After, the application declares the ownership boundary once:
21
+
22
+ ```ruby
23
+ Phronomy::Agent.open_scope do |agents|
24
+ agent = agents.create(ReviewAgent, persistence: store)
25
+ agent.invoke_async(text)
26
+ end
27
+ ```
28
+
29
+ `open_scope` waits for a returned TaskResult and returns its resolved value (or
30
+ raises the original exception). A plain block value is returned unchanged.
31
+ It then attempts to unload every Agent it acquired. A Workflow can use
32
+ `agents.create_async` or `agents.load_async` from EventLoop and compose their
33
+ TaskResults. The outer application keeps the scope alive until the Workflow has
34
+ finished. A scope is not serializable Workflow context.
35
+
36
+ An existing application-owned Agent cannot be adopted by `scope.load`: that
37
+ would give the scope permission to release someone else's object. The attempt
38
+ raises `AgentBusyError`. Repeated load in the same scope returns its live instance,
39
+ after checking class and Store compatibility. A listener cannot be replaced on
40
+ an already live instance. Existing `Agent.load`/`get` lookup semantics remain;
41
+ obtaining a reference does not extend its owning scope's lifetime.
42
+
43
+ ## Release contract
44
+
45
+ `agent.unload_async` returns `TaskResult<Boolean>` and is EventLoop-safe.
46
+ `agent.unload!(timeout: nil)` waits externally. Both preserve all durable state:
47
+ root and context revisions, journal position, contents, history, metadata and
48
+ lifecycle status. A closed Agent remains closed after reload.
49
+
50
+ Release requires an exact live owner, no accepted operations or execution
51
+ admission (including approval and recovery waits), and an idle durable execution
52
+ ledger with no retention references. C04 interprets only the presence of opaque
53
+ retention references; their owning domain decides when to remove them.
54
+
55
+ Concurrent release requests share the pending handle. A successful release
56
+ returns `true`; an already unloaded reference returns `false`. A wait timeout
57
+ does not cancel the release. Store check failure rolls back the reservation and
58
+ preserves the original error. Runtime shutdown closes admission and drains
59
+ already accepted transition completion without creating a replacement Runtime.
60
+
61
+ New operations cannot enter during release. Old Agent operations fail with
62
+ `AgentUnloadedError` (`code: "agent.unloaded"`, `agent_id`); asynchronous methods
63
+ return failed TaskResults. Immutable snapshots and `agent_id` remain usable.
64
+ A later load creates a new instance and can bind a new listener. The old
65
+ reference cannot mutate or release that new owner. Purged Agents keep their
66
+ existing `AgentPurgedError` semantics.
67
+
68
+ A concurrent `get` or `load` during release raises `AgentBusyError` immediately;
69
+ it does not wait or create a second owner. Accepted operations make unload fail
70
+ with Busy at its admission gate. In particular, unload inside an Agent terminal
71
+ listener is Busy while that listener is executing. Exit the outer application
72
+ scope after result delivery to release it. Existing immutable local-state reads
73
+ remain available while Runtime shutdown is draining an incomplete transition;
74
+ new execution and mutation admission stays closed.
75
+
76
+ ## Failure and cancellation
77
+
78
+ Scope tracks materialization receipts before starting work and registers acquired
79
+ Agents before returning them. Closure stops new acquisition and waits for the
80
+ physical completion of accepted materialization, even when its handle was
81
+ cancelled. Indeterminate durable create keeps the C04 recovery reservation and
82
+ logs the Agent ID; it is never silently made creatable again.
83
+
84
+ Scope does not cancel or wait for unjoined child execution as an implicit
85
+ cleanup policy. An active Agent is retained and cleanup reports `AgentBusyError`.
86
+ If the main operation failed, its same exception is preserved and secondary
87
+ cleanup failures are logged with Agent IDs. Logger failure cannot replace it.
88
+ If the main operation succeeded, failed cleanup raises `Agent::ScopeCleanupError`
89
+ (`code: "agent.scope_cleanup_failed"`). `result` preserves the successful value;
90
+ `failures` is an array of `{agent_id:, error:}` with the original exceptions.
91
+ All acquired Agents are attempted. No automatic retry, purge, or retention
92
+ removal is performed.
93
+
94
+ ## Responsibility and ordering
95
+
96
+ - C04 owns scope receipts, exact incarnation identity, operation admission,
97
+ busy/retention rules, invalidation and cleanup errors.
98
+ - C07 supplies the existing transaction boundary. C04's Agent Store supplies
99
+ `assert_idle!`, `guard_agent!` and retention reads; neither Persistence nor
100
+ physical Storage gains incarnation release policy.
101
+ - C13 supplies existing TaskResult and physical-completion contracts. It gains
102
+ no scope membership, Agent busy rules or unload states.
103
+ - Engine supplies existing worker execution, serial EventLoop delivery and
104
+ shutdown participation. It gains no Agent-specific states.
105
+ - The operation lease and release reservation share the C04 ownership mutex.
106
+ No Store transaction, Runtime call, application callback or blocking wait runs
107
+ under that mutex. Accepted runner continuations use C04 internal snapshots;
108
+ callbacks initiating a new public operation must acquire a new lease.
109
+ - Agent operation completion is distinct from cancelling its public handle.
110
+ C04 acknowledges release after domain terminal/listener delivery and physical
111
+ worker completion. A queued or physically running cancelled operation still
112
+ blocks release.
113
+
114
+ For notifications, finish child processing, drain notifications, then exit the
115
+ Agent scope. `run_once` retains its existing Store and lifetime behavior; merely
116
+ calling it does not opt into scope cleanup.
117
+
118
+ This is P5 (core). P6 migrates application examples and performs cross-repository
119
+ acceptance against the published or pinned candidate core.
@@ -0,0 +1,134 @@
1
+ # DX05-D: Workflow-owned asynchronous task states
2
+
3
+ Status: unreleased Beta implementation. Based on DX05-A core `c673c958c`.
4
+
5
+ ## Problem and intended application boundary
6
+
7
+ A Workflow application describes which operation a stage starts, how its result
8
+ changes business state, and where that state goes next. Repeating callbacks,
9
+ completion events, error envelopes and state-entry identifiers obscures those
10
+ choices. Workflow already owns the live execution and state-entry lifetime, so it
11
+ can own this correlation without knowing how an Agent, Tool or LLM runs.
12
+
13
+ The business flow is preserved. Failure, cancellation, stale-result and delivery
14
+ semantics become explicit and stronger; this is not a claim that every failure
15
+ path of the previous manual recipe was equivalent.
16
+
17
+ ## Before and after
18
+
19
+ Previously, each state manually arranged delivery (simplified successful path):
20
+
21
+ ```ruby
22
+ workflow = nil
23
+ workflow = Phronomy::Workflow.define(AnswerState) do
24
+ initial :asking
25
+ state :asking
26
+ entry :asking, ->(state) {
27
+ id = state.workflow_instance_id
28
+ agent.invoke_async(state.question).on_complete do |value, error|
29
+ workflow.signal(workflow_instance_id: id, event: :answer_ready,
30
+ payload: {value: value, error: error})
31
+ end
32
+ state
33
+ }
34
+ transition from: :asking, on: :answer_ready, to: :__finish__,
35
+ action: ->(state, event) {
36
+ raise event.payload[:error] if event.payload[:error]
37
+ state.merge(answer: event.payload.fetch(:value).fetch(:output))
38
+ }
39
+ end
40
+ ```
41
+
42
+ That snippet also needs rejection handling and entry-specific guards for safe
43
+ reentry. The application now writes only the operation and successful update:
44
+
45
+ ```ruby
46
+ workflow = Phronomy::Workflow.define(AnswerState) do
47
+ initial :asking
48
+ task_state :asking,
49
+ start: ->(state) { agent.invoke_async(state.question) },
50
+ apply: ->(state, result) { state.merge(answer: result.fetch(:output)) }
51
+ transition from: :asking, to: :__finish__
52
+ end
53
+ ```
54
+
55
+ `start` receives an isolated context snapshot, including current phase and
56
+ Workflow identity. It must return a `Phronomy::TaskResult` promptly. It must not
57
+ perform blocking I/O or wait for another Task. Use `Blocking.call_async` for real
58
+ blocking work. `apply` receives the current context and successful value, runs
59
+ briefly on EventLoop, and returns the same context type or nil. Normal transition
60
+ guards run after `apply`. Nil means no context replacement, not failure.
61
+
62
+ A task state may have ordinary entry/exit actions and external transitions.
63
+ Leaving it abandons observation of its source. Declaring the same state as both
64
+ `state`/`wait_state` and `task_state` is rejected. Ordinary entry/transition actions
65
+ still cannot return a TaskResult. Business signals keep their existing API.
66
+
67
+ ## Contract ownership
68
+
69
+ | Owner | Responsibility |
70
+ |---|---|
71
+ | C02 Workflow | Task-state definition; live execution and state-entry reception; once-only consumption; success application, failure/cancellation classification; stale-result and queued-progression rejection; restart policy |
72
+ | C13 Execution | Thread-safe TaskResult notification and cancellation subscription mechanisms |
73
+ | I07 Workflow runtime binding | Connect C02 decisions to the existing FSM, owner EventLoop and owner Runtime timer/shutdown services |
74
+ | Engine | Generic event/session delivery and lifecycle; no task-state or Agent policy is added |
75
+ | C04 Agent and application | Agent execution, business transformation/fallback, child cancellation policy and producer lifetime |
76
+
77
+ C02 uses its execution-environment port and receives an injected session
78
+ connection. It does not select Runtime or depend on I07. `WorkflowTaskSession`
79
+ in I07 adapts the generic FSMSession. Engine source is unchanged.
80
+
81
+ A revocable C02 reception is created before `start`. Source callbacks retain only
82
+ that reception. Closing it drops source and registry references. Completion is
83
+ sent to the originating registry under an opaque execution owner token, never
84
+ rerouted by a durable Workflow ID. Receiver-side validation checks the current
85
+ entry and consumes once. Exits, reentry, cancellation and terminal decisions
86
+ revoke it. Queued automatic progression also carries an entry-specific key:
87
+ an external transition between result application and automatic dispatch cannot
88
+ advance a later entry. These private messages are not public business signals.
89
+
90
+ ## Outcomes and lifetime
91
+
92
+ | Condition | Caller-visible result |
93
+ |---|---|
94
+ | Start throws, source fails, or apply throws | Workflow fails with the same error object |
95
+ | Source status is `cancelled` | Workflow TaskResult is cancelled with the source error |
96
+ | Source status is `failed` with a CancellationError | Remains failed; class alone does not imply cancellation |
97
+ | Explicit parent token cancellation/deadline | Cancel Workflow observation; do not implicitly cancel the source |
98
+ | Old or duplicated source notification | Ignore; no application or retry |
99
+ | Current completion delivery rejects/throws | Fail on the owner; if owner delivery is wholly unavailable, settle observation only and retain admission until shutdown cleanup |
100
+ | Runtime shutdown while waiting | Settle observation and revoke subscriptions; no claim that child work physically stopped |
101
+ | Known durable terminal result | Use existing save/readback classification and release rules |
102
+ | Unknown durable terminal result | Fail the observation while retaining recovery-required ownership |
103
+
104
+ Cancellation is serialized on the owning EventLoop. Once terminal persistence
105
+ has begun, its existing durable barrier remains authoritative; later cancellation
106
+ does not reverse a committed or uncertain terminal decision. A source passed to
107
+ the state has an independent lifetime. Applications explicitly pass cancellation
108
+ controls to children when desired; they own Agent release and side effects.
109
+
110
+ This API creates no thread/worker for logical waits. Actual I/O and existing
111
+ tracing or checkpoint work may still use the bounded pool. Expected business
112
+ failures may be transformed into ordinary values through TaskResult composition.
113
+ The API does not infer retry, fallback, durable child execution or delivery ACKs.
114
+
115
+ ## Persistence and restart
116
+
117
+ Normal completion and ordinary `wait_state` halts use existing Workflow
118
+ checkpoints. Resuming an ordinary wait into a new task state is supported. Pending
119
+ TaskResults, callbacks and entry keys are process-local and are never serialized.
120
+ Restoring a snapshot whose phase is a task state raises
121
+ `Phronomy::WorkflowTaskResumeError` (C02, subclass of `Phronomy::Error`, code
122
+ `workflow.task_resume_unsupported`) before running its start callback. The
123
+ application must reconcile that operation explicitly instead of automatically
124
+ repeating external side effects. F01 durable child execution remains separate.
125
+
126
+ ## Verification
127
+
128
+ The new task-state regression suite covers immediate and delayed results,
129
+ repeated entry, old success/failure/cancellation, duplicate callbacks, external
130
+ transition races, rejected guards, delivery failure, shutdown, source/parent
131
+ cancellation, owner Runtime selection, retained-reference revocation, strict
132
+ callback result types, normal durable halt/resume, restart rejection and unknown
133
+ durable outcomes. Existing Workflow, TaskResult, API and executable-documentation
134
+ tests remain active. Examples 01/03/04/07/14/20/25/26 use the new API.