phronomy 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +52 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +9 -3
  6. data/benchmark/bench_agent_invoke.rb +25 -50
  7. data/benchmark/bench_context_assembler.rb +14 -11
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/benchmark/bench_tool_schema.rb +1 -1
  10. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  11. data/docs/architecture/agent-transition-ownership.md +13 -89
  12. data/docs/architecture/backend-groups.md +152 -0
  13. data/docs/architecture/before-llm-input.md +3 -3
  14. data/docs/architecture/context-management.md +1 -1
  15. data/docs/architecture/execution-abstraction-review.md +88 -0
  16. data/docs/architecture/execution-boundaries.md +28 -0
  17. data/docs/architecture/execution-metadata-and-values.md +6 -0
  18. data/docs/architecture/multi-agent-handoff.md +6 -5
  19. data/docs/architecture/persistence.md +27 -9
  20. data/docs/architecture/r8-unit1.md +104 -0
  21. data/docs/architecture/r8-unit10.md +72 -0
  22. data/docs/architecture/r8-unit11.md +71 -0
  23. data/docs/architecture/r8-unit12.md +67 -0
  24. data/docs/architecture/r8-unit13.md +61 -0
  25. data/docs/architecture/r8-unit14.md +72 -0
  26. data/docs/architecture/r8-unit15.md +65 -0
  27. data/docs/architecture/r8-unit16.md +54 -0
  28. data/docs/architecture/r8-unit17.md +73 -0
  29. data/docs/architecture/r8-unit18.md +92 -0
  30. data/docs/architecture/r8-unit19.md +61 -0
  31. data/docs/architecture/r8-unit2.md +55 -0
  32. data/docs/architecture/r8-unit20.md +51 -0
  33. data/docs/architecture/r8-unit21.md +79 -0
  34. data/docs/architecture/r8-unit22.md +74 -0
  35. data/docs/architecture/r8-unit23.md +81 -0
  36. data/docs/architecture/r8-unit3.md +130 -0
  37. data/docs/architecture/r8-unit4.md +79 -0
  38. data/docs/architecture/r8-unit5.md +115 -0
  39. data/docs/architecture/r8-unit6.md +71 -0
  40. data/docs/architecture/r8-unit7.md +70 -0
  41. data/docs/architecture/r8-unit8.md +78 -0
  42. data/docs/architecture/r8-unit9.md +73 -0
  43. data/docs/architecture/refactoring-closure.md +10 -1
  44. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  45. data/docs/architecture/storage-execution-boundaries.md +54 -0
  46. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  47. data/docs/architecture/tracing.md +1 -1
  48. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  49. data/docs/architecture.md +24 -11
  50. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  51. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  52. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  53. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  54. data/docs/decisions/032-storage-backend-composition.md +5 -0
  55. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  56. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  57. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  58. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  59. data/docs/decisions/040-configuration-default-composition.md +12 -1
  60. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  61. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  62. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  63. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  64. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  65. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  66. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  67. data/docs/decisions/062-content-store-service.md +52 -0
  68. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  69. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  70. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  71. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  72. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  73. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  74. data/docs/decisions/README.md +18 -1
  75. data/docs/execution-boundaries-preview.md +45 -0
  76. data/docs/features.md +8 -3
  77. data/docs/getting-started.md +9 -6
  78. data/docs/migrations/0.22.md +2 -2
  79. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  80. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  81. data/docs/migrations/persistence-failure-contracts.md +67 -0
  82. data/docs/migrations/r8-unit10.md +36 -0
  83. data/docs/migrations/r8-unit11.md +33 -0
  84. data/docs/migrations/r8-unit12.md +31 -0
  85. data/docs/migrations/r8-unit13.md +36 -0
  86. data/docs/migrations/r8-unit14.md +31 -0
  87. data/docs/migrations/r8-unit15.md +31 -0
  88. data/docs/migrations/r8-unit16.md +26 -0
  89. data/docs/migrations/r8-unit17.md +28 -0
  90. data/docs/migrations/r8-unit18.md +29 -0
  91. data/docs/migrations/r8-unit19.md +28 -0
  92. data/docs/migrations/r8-unit20.md +24 -0
  93. data/docs/migrations/r8-unit21.md +28 -0
  94. data/docs/migrations/r8-unit22.md +29 -0
  95. data/docs/migrations/r8-unit23.md +20 -0
  96. data/docs/migrations/r8-unit4.md +67 -0
  97. data/docs/migrations/r8-unit5.md +75 -0
  98. data/docs/migrations/r8-unit6.md +109 -0
  99. data/docs/migrations/r8-unit7.md +31 -0
  100. data/docs/migrations/r8-unit8.md +33 -0
  101. data/docs/migrations/r8-unit9.md +28 -0
  102. data/docs/migrations/storage-async-client.md +103 -0
  103. data/docs/migrations/vector-async-clients.md +110 -0
  104. data/docs/persistence-backends.md +38 -21
  105. data/docs/runtime-and-concurrency.md +26 -1
  106. data/lib/phronomy/agent/admission.rb +78 -0
  107. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  108. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  109. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  110. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  111. data/lib/phronomy/agent/async_event_api.rb +37 -32
  112. data/lib/phronomy/agent/base.rb +74 -72
  113. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  114. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  115. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  116. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  117. data/lib/phronomy/agent/control_request.rb +21 -0
  118. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  119. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  120. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  121. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  122. data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
  123. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  124. data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
  125. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  126. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
  127. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  128. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  129. data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
  130. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  131. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  132. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
  133. data/lib/phronomy/agent/execution_change.rb +161 -0
  134. data/lib/phronomy/agent/execution_environment.rb +36 -0
  135. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  136. data/lib/phronomy/agent/execution_observation.rb +35 -0
  137. data/lib/phronomy/agent/filterable.rb +182 -0
  138. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  139. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  140. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  141. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  142. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  143. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  144. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
  145. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  146. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  147. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  148. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  149. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  150. data/lib/phronomy/agent/persistence/records.rb +33 -0
  151. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  152. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  153. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  154. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  155. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
  156. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
  157. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
  158. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
  159. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  160. data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
  161. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  162. data/lib/phronomy/agent/retention.rb +17 -0
  163. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  164. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  165. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  166. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  167. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  168. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  169. data/lib/phronomy/agent/settings.rb +47 -0
  170. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  171. data/lib/phronomy/agent/store.rb +253 -0
  172. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
  173. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  174. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  175. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  176. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  177. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  178. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  179. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  180. data/lib/phronomy/context/assembly.rb +202 -0
  181. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  182. data/lib/phronomy/context/candidate.rb +51 -0
  183. data/lib/phronomy/context/constraint.rb +47 -0
  184. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  185. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  186. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  187. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  188. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  189. data/lib/phronomy/context/default_policy.rb +140 -0
  190. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  191. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  192. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  193. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  194. data/lib/phronomy/context/manifest_error.rb +10 -0
  195. data/lib/phronomy/context/prompt_template.rb +98 -0
  196. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  197. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  198. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  199. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  200. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  201. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  202. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  203. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  204. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  205. data/lib/phronomy/embeddings/base.rb +43 -0
  206. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  207. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  208. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  209. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  210. data/lib/phronomy/engine/diagnostics.rb +1 -1
  211. data/lib/phronomy/engine/event_loop.rb +7 -7
  212. data/lib/phronomy/engine/execution_binding.rb +23 -0
  213. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  214. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  215. data/lib/phronomy/engine/fsm_session.rb +3 -3
  216. data/lib/phronomy/engine/metrics.rb +16 -14
  217. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  218. data/lib/phronomy/engine/runtime.rb +25 -12
  219. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  220. data/lib/phronomy/execution/async_operation.rb +41 -0
  221. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  222. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  223. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  224. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  225. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  226. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  227. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  228. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  229. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  230. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  231. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  232. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  233. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  234. data/lib/phronomy/execution/runnable.rb +24 -0
  235. data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
  236. data/lib/phronomy/execution/wait_policy.rb +24 -0
  237. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
  238. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  239. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  240. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  241. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  242. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  243. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  244. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  245. data/lib/phronomy/llm_adapter/base.rb +49 -68
  246. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  247. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  248. data/lib/phronomy/llm_adapter/message.rb +62 -0
  249. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  250. data/lib/phronomy/llm_adapter/request.rb +79 -0
  251. data/lib/phronomy/llm_adapter/response.rb +36 -0
  252. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  253. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  254. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  255. data/lib/phronomy/llm_adapter.rb +3 -16
  256. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  257. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  258. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  259. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  260. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  261. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  262. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  263. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  264. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  265. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  266. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  267. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  268. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  269. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  270. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
  271. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  272. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  273. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  274. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  275. data/lib/phronomy/multi_agent/store.rb +156 -0
  276. data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
  277. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  278. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  279. data/lib/phronomy/persistence/content_repository.rb +26 -0
  280. data/lib/phronomy/persistence/errors.rb +16 -0
  281. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  282. data/lib/phronomy/persistence/persistence.rb +23 -0
  283. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  284. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  285. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  286. data/lib/phronomy/persistence/transaction.rb +86 -0
  287. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  288. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  289. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  290. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  291. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  292. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  293. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  294. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  295. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  296. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  297. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  298. data/lib/phronomy/storage/async/async_client.rb +60 -0
  299. data/lib/phronomy/storage/backend.rb +8 -2
  300. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  301. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  302. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  303. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  304. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  305. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  306. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  307. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  308. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  309. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  310. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  311. data/lib/phronomy/tool/authorization.rb +77 -0
  312. data/lib/phronomy/tool/base.rb +549 -9
  313. data/lib/phronomy/tool/call_request.rb +40 -0
  314. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  315. data/lib/phronomy/tool/operation.rb +88 -0
  316. data/lib/phronomy/tool/schema.rb +214 -0
  317. data/lib/phronomy/tool/settings.rb +31 -0
  318. data/lib/phronomy/tool/tool_error.rb +7 -0
  319. data/lib/phronomy/tool/tool_executor.rb +58 -0
  320. data/lib/phronomy/tools/agent.rb +22 -90
  321. data/lib/phronomy/tools/mcp.rb +21 -14
  322. data/lib/phronomy/tools/vector_search.rb +4 -4
  323. data/lib/phronomy/tracing/automatic.rb +11 -6
  324. data/lib/phronomy/tracing/base.rb +1 -1
  325. data/lib/phronomy/tracing/observation.rb +37 -0
  326. data/lib/phronomy/tracing/settings.rb +36 -0
  327. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  328. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  329. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  330. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  331. data/lib/phronomy/vector_store/base.rb +118 -49
  332. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  333. data/lib/phronomy/vector_store.rb +2 -17
  334. data/lib/phronomy/version.rb +1 -1
  335. data/lib/phronomy/workflow/completion.rb +11 -0
  336. data/lib/phronomy/workflow/execution/workflow.rb +7 -7
  337. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  338. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  339. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  340. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
  341. data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
  342. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  343. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  344. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  345. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  346. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  347. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  348. data/lib/phronomy.rb +57 -16
  349. data/scripts/api_snapshot.rb +35 -15
  350. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  351. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  352. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  353. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  354. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  355. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  356. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  357. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  358. data/sig/phronomy/agent/admission.rbs +19 -0
  359. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  360. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  361. data/sig/phronomy/agent/store.rbs +73 -0
  362. data/sig/phronomy/agent.rbs +14 -138
  363. data/sig/phronomy/common.rbs +7 -0
  364. data/sig/phronomy/content_store/contracts.rbs +13 -0
  365. data/sig/phronomy/context.rbs +152 -0
  366. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  367. data/sig/phronomy/embeddings/base.rbs +17 -0
  368. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  369. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  370. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  371. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  372. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  373. data/sig/phronomy/engine/observability.rbs +13 -0
  374. data/sig/phronomy/execution/async_operation.rbs +7 -0
  375. data/sig/phronomy/execution/blocking.rbs +5 -0
  376. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  377. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  378. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  379. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  380. data/sig/phronomy/execution/errors.rbs +7 -0
  381. data/sig/phronomy/execution/execution.rbs +41 -0
  382. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  383. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  384. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  385. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  386. data/sig/phronomy/execution/task_result.rbs +29 -0
  387. data/sig/phronomy/extensions.rbs +0 -21
  388. data/sig/phronomy/handoff.rbs +10 -4
  389. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  390. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  391. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  392. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  393. data/sig/phronomy/llm_context_window.rbs +10 -0
  394. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  395. data/sig/phronomy/multi_agent/store.rbs +54 -0
  396. data/sig/phronomy/multi_agent.rbs +6 -3
  397. data/sig/phronomy/persistence/errors.rbs +18 -0
  398. data/sig/phronomy/persistence/migration.rbs +13 -0
  399. data/sig/phronomy/persistence/transaction.rbs +19 -0
  400. data/sig/phronomy/persistence.rbs +1 -99
  401. data/sig/phronomy/persistence_composition.rbs +19 -0
  402. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  403. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  404. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  405. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
  406. data/sig/phronomy/tool.rbs +48 -4
  407. data/sig/phronomy/tracing/observation.rbs +7 -0
  408. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  409. data/sig/phronomy/vector_store/base.rbs +23 -0
  410. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  411. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  412. data/sig/phronomy/workflow/store.rbs +8 -0
  413. data/sig/phronomy/workflow.rbs +1 -1
  414. data/sig/phronomy.rbs +0 -49
  415. metadata +318 -117
  416. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  417. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  418. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  419. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  420. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  421. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  422. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  423. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  424. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  425. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  426. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  427. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  428. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  429. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  430. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  431. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  432. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  433. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  434. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  435. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  436. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  437. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  438. data/lib/phronomy/configuration/configuration.rb +0 -68
  439. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  440. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  441. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  442. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  443. data/lib/phronomy/engine/runnable.rb +0 -54
  444. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  445. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  446. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  447. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  448. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  449. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  450. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  451. data/lib/phronomy/recovery/recovery.rb +0 -186
  452. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  453. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  454. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  455. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  456. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  457. data/sig/phronomy/execution_receiver.rbs +0 -34
  458. data/sig/phronomy/llm_adapter.rbs +0 -11
  459. data/sig/phronomy/runtime.rbs +0 -76
  460. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  461. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  462. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  463. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  464. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  465. /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
  466. /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
  467. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  468. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  469. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  470. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  471. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  472. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  473. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  474. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  475. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -0,0 +1,110 @@
1
+ # VectorStore and Embeddings: asynchronous client migration
2
+
3
+ This unreleased P3 change moves the Beta asynchronous API to explicit clients.
4
+ It changes the receiver used by callers. Backend authors continue to implement
5
+ only synchronous methods; method arguments and results remain unchanged.
6
+
7
+ | Previous receiver | New receiver |
8
+ |---|---|
9
+ | `store.add_async(...)` | `async_store.add_async(...)` |
10
+ | `store.search_async(...)` | `async_store.search_async(...)` |
11
+ | `store.remove_async(...)` | `async_store.remove_async(...)` |
12
+ | `store.clear_async(...)` | `async_store.clear_async(...)` |
13
+ | `embeddings.embed_async(text, token, timeout: seconds)` | `async_embeddings.embed_async(text, token, timeout: seconds)` |
14
+
15
+ Construct the clients with `VectorStore::AsyncClient.new(backend: store)` and
16
+ `VectorStore::Embeddings::AsyncClient.new(adapter: embeddings)`. The embedding
17
+ cancellation token stays positional. Synchronous `add`, `search`, `remove`,
18
+ `clear`, `size` and `embed` stay on the existing backend objects.
19
+
20
+ `VectorStore::AsyncBackend` and inherited async methods are removed. There is
21
+ no include/prepend, backend `.async` factory, or automatic compatibility shim.
22
+ Update the receiver wherever an application previously called an async method.
23
+ The clients are public Beta API; their `async/` directories do not introduce
24
+ `Phronomy::Async` or feature-specific `Async` namespaces.
25
+
26
+ ## Complete offline example
27
+
28
+ This example uses an intentionally simple local embedding adapter to demonstrate
29
+ the API. It needs no provider key, network request, or database server. The
30
+ implementation supplies only `embed`; Phronomy supplies asynchronous execution.
31
+ Run it against a checkout containing P3, not the previously released 0.27.0 gem.
32
+
33
+ ```ruby runnable
34
+ require "phronomy"
35
+ require "json"
36
+
37
+ class KeywordEmbeddings < Phronomy::Embeddings::Base
38
+ protected
39
+
40
+ def perform_embed(text, cancellation_token = nil)
41
+ cancellation_token&.raise_if_cancelled!
42
+ words = text.downcase
43
+ [words.include?("ruby") ? 1.0 : 0.0,
44
+ words.include?("python") ? 1.0 : 0.0]
45
+ end
46
+ end
47
+
48
+ begin
49
+ store = Phronomy::VectorStore::InMemory.new(dimension: 2)
50
+ embeddings = KeywordEmbeddings.new
51
+ async_store = Phronomy::VectorStore::AsyncClient.new(backend: store)
52
+ async_embeddings = Phronomy::Embeddings::AsyncClient.new(adapter: embeddings)
53
+
54
+ {"ruby" => "Ruby guide", "python" => "Python guide"}.each do |id, title|
55
+ async_embeddings.embed_async(title).flat_map do |vector|
56
+ async_store.add_async(id: id, embedding: vector, metadata: {title: title})
57
+ end.wait_result
58
+ end
59
+
60
+ matches = async_embeddings.embed_async("Ruby").flat_map do |vector|
61
+ async_store.search_async(query_embedding: vector, k: 1)
62
+ end.wait_result
63
+ puts JSON.generate(matches)
64
+ ensure
65
+ Phronomy.reset_runtime!
66
+ end
67
+ ```
68
+
69
+ The result contains the `ruby` document. The application waits only outside
70
+ worker callbacks; `flat_map` returns the next TaskResult without blocking a
71
+ worker on another operation. `Tools::VectorSearch` continues to call synchronous
72
+ `embed` and `search` inside its existing execution boundary, so this migration
73
+ does not add nested offload work to that Tool.
74
+
75
+ ## Execution and cancellation
76
+
77
+ Both clients accept optional `pool:` injection. Usually omit it. Construction
78
+ starts no Runtime, and each operation resolves the current default pool. An
79
+ explicit pool remains caller-owned across Runtime resets; it must provide the
80
+ existing OffloadPool submit contract, including its timer service for timeouts.
81
+
82
+ Queue admission uses `on_full: :raise`. A full or stopped pool raises immediately;
83
+ an accepted operation returns the original TaskResult. Timeout includes queue
84
+ wait. Cancellation or timeout before worker pickup skips the operation; after
85
+ execution starts it settles the result without forcibly stopping I/O or undoing
86
+ backend effects. These are the existing OffloadPool semantics.
87
+
88
+ Synchronous RBS signatures accept the structural `_CancellationSignal`
89
+ interface, which requires only `raise_if_cancelled!`. Existing
90
+ `Concurrency::CancellationToken` objects still satisfy it. Async RBS signatures
91
+ live in `sig/phronomy/vector_store/async/async_client.rbs` and
92
+ `sig/phronomy/embeddings/async/async_client.rbs` and retain the full CancellationToken
93
+ and TaskResult contract needed by the execution pool. Do not substitute a
94
+ minimal synchronous signal for an async operation's full token.
95
+
96
+ ## Source ownership
97
+
98
+ The public class names below are unchanged. Their implementation files move:
99
+
100
+ | Public class | New path under `lib/phronomy/` |
101
+ |---|---|
102
+ | `VectorStore::InMemory` | `vector_store/backends/in_memory.rb` |
103
+ | `VectorStore::Pgvector` | `vector_store/backends/pgvector.rb` |
104
+ | `VectorStore::RedisSearch` | `vector_store/backends/redis_search.rb` |
105
+ | `VectorStore::Embeddings::RubyLLMEmbeddings` | `embeddings/backends/ruby_llm_embeddings.rb` |
106
+
107
+ Use `require "phronomy"` and the public constants. Direct partial-load paths are
108
+ not the public loading contract; no require shim is kept in the contract
109
+ directories. Backend Contracts have no execution or implementation dependency.
110
+ P4 Storage/ContentStore work remains pending.
@@ -2,8 +2,11 @@
2
2
 
3
3
  [ADR-058](decisions/058-neutral-storage-primitives.md) defines the current
4
4
  extension contract. This is an intentional breaking replacement of the old
5
- fixed-repository Backend SPI. Application code keeps `Persistence.new(backend:)`,
6
- `Persistence.in_memory`, its eight domain repositories and result queries.
5
+ fixed-repository Backend SPI. Application construction now uses
6
+ `PersistenceComposition.build(backend:)` or `.in_memory`; pass its `agent`, `team`,
7
+ or `workflow` component to that domain. `Persistence.new(backend:)` provides only
8
+ the neutral coordinator. The old eight-repository facade and its queries were
9
+ removed; see [r8 unit 3](architecture/r8-unit3.md).
7
10
 
8
11
  ## Ownership and composition
9
12
 
@@ -13,9 +16,10 @@ record types, active-execution policy, content digest algorithm or watermark API
13
16
 
14
17
  Agent, Team, Workflow and ContentStore declare their own resource schemas.
15
18
  `PersistenceComposition::StorageSchema` gathers those declarations and
16
- `PersistenceComposition::Repositories` assembles domain wrappers over a View.
17
- The Team and Workflow schema files live beside their features in nested loader
18
- roots; loading metadata does not load their runtime implementations.
19
+ `PersistenceComposition` binds domain record adapters to one coordinator.
20
+ Team and Workflow schemas live in their existing `persistence/` implementation
21
+ directories. An isolated Agent uses `PersistenceComposition.agent` and does not
22
+ construct Team or Workflow stores.
19
23
  `ContentStore::StoredContents` owns SHA-256 identity and digest verification.
20
24
  The Agent-owned `Watermark` composes guarded revision and stream-head conditions.
21
25
 
@@ -41,11 +45,10 @@ view.check!(guards: guards, conditions: conditions)
41
45
 
42
46
  The backend declares `spi_version: 2` and true values for `atomic_resources`,
43
47
  `record_cas`, `stream_cas`, `conditional_unique`, `guarded_checks` and
44
- `nested_savepoints`. Persistence validates these capabilities and the required
45
- resource declarations before exposing repositories. Old duck-typed backends are
46
- rejected with `UnsupportedBackendError`; there is no eight-slot compatibility
47
- adapter. Public Persistence capabilities retain `atomic_all`, `atomic_admission`
48
- and `optimistic_revision`, derived by composition from these primitives.
48
+ `nested_savepoints`. Persistence validates backend capabilities; composition
49
+ validates the required domain resource declarations before binding adapters.
50
+ Unsupported backends raise `Persistence::UnsupportedBackendError`. There is no
51
+ eight-slot compatibility adapter or duplicated facade capability API.
49
52
 
50
53
  A root handle routes to the current transaction on the same backend/thread.
51
54
  A bound view and all its handles expire on commit or rollback and reject use from
@@ -139,12 +142,24 @@ stale revisions and ordinary constraint conflicts remain `ConflictError`.
139
142
  meanings; `TransactionError` identifies invalid scope use. The removed
140
143
  `ActiveExecutionConflictError` and `Storage::Repositories` have no aliases.
141
144
 
142
- The following sections describe the retained **domain Persistence** surface.
145
+ The following sections describe the **domain-owned record protocols**, used by
146
+ domain implementations and backend conformance tests. They are not a shared
147
+ application facade. Unqualified
148
+ ConflictError, NotFoundError, SerializationError and UnsupportedBackendError in
149
+ these domain sections refer to `Phronomy::Persistence` errors. Repository exits
150
+ translate the four raw Storage categories, preserving `cause`, message and trace.
151
+ Backend implementation code continues to raise `Phronomy::Storage` errors as
152
+ specified above. Unknown/transport errors and Storage::TransactionError are not
153
+ translated. See [ADR-061](decisions/061-persistence-failure-contracts.md) and the
154
+ [application migration](migrations/persistence-failure-contracts.md).
143
155
 
144
156
  ## Contents repository
145
157
 
146
- `ContentStore::StoredContents < ContentStore::Base` supplies this domain surface
147
- over neutral Blobs, with text/JSON helpers and canonical content-ID calculation.
158
+ `ContentStore::StoredContents < ContentStore::Base` supplies text/JSON helpers
159
+ and canonical content-ID calculation over neutral Blobs. The private
160
+ Persistence::ContentRepository adapts its primitive methods to the upper failure
161
+ contract when accessed through `persistence.contents`. Direct StoredContents
162
+ callers retain the raw Storage error surface.
148
163
 
149
164
  Required primitive surface:
150
165
 
@@ -159,7 +174,7 @@ Required semantics:
159
174
  - content is immutable and content-addressed;
160
175
  - writing identical bytes is idempotent and returns the same content ID;
161
176
  - `fetch` returns a binary `String` isolated from caller mutation;
162
- - a missing content ID raises `Storage::NotFoundError`;
177
+ - a missing content ID through `persistence.contents` raises `Persistence::NotFoundError`;
163
178
  - one content ID must never resolve to different bytes; a digest-integrity
164
179
  violation raises `ContentStore::IntegrityError`.
165
180
 
@@ -361,8 +376,9 @@ Array of supported values
361
376
  Hash with String/Symbol keys and supported values
362
377
  ```
363
378
 
364
- If a value cannot be represented, raise `Storage::SerializationError` rather
365
- than silently converting it into a lossy form. JSON backends may return String
379
+ If a value cannot be represented, the backend/codec raises
380
+ `Storage::SerializationError`; the domain repository exposes
381
+ `Persistence::SerializationError`. Do not silently convert it into a lossy form. JSON backends may return String
366
382
  keys after decoding; `WorkflowRunner` deliberately accepts String and Symbol keys
367
383
  and normalizes them when comparing durable snapshots.
368
384
 
@@ -391,16 +407,17 @@ differs, raise `ConflictError`. On success return `true`.
391
407
  The operation must not return a replacement AgentRoot or Journal. A mismatch is a
392
408
  conflict, not a request to reload/merge mutable state.
393
409
 
394
- When used inside `Persistence#transaction`, a SQL backend should perform the
410
+ When used inside `Agent::Store#transaction`, a SQL backend should perform the
395
411
  watermark check in the same database transaction as the subsequent durable
396
412
  write.
397
413
 
398
414
  ## Transaction contract
399
415
 
400
- A transaction block may combine operations across all repositories:
416
+ A domain transaction combines its own record operations. Multiple domains join
417
+ one coordinator scope explicitly; each receives only its own record protocol:
401
418
 
402
419
  ```ruby
403
- persistence.transaction do |tx|
420
+ stores.agent.transaction do |tx|
404
421
  tx.assert_agent_watermark!(...)
405
422
  content_ref = tx.contents.put_text("...")
406
423
  tx.journals.append(...)
@@ -493,7 +510,7 @@ require "phronomy"
493
510
  require "phronomy/testing/persistence_contract"
494
511
 
495
512
  RSpec.describe MyPersistenceBackend do
496
- let(:persistence) { Phronomy::Persistence.new(backend: described_class.new(...)) }
513
+ let(:stores) { Phronomy::PersistenceComposition.build(backend: described_class.new(...)) }
497
514
 
498
515
  it_behaves_like "a persistence content store"
499
516
  it_behaves_like "an Agent repository"
@@ -507,7 +524,7 @@ RSpec.describe MyPersistenceBackend do
507
524
  end
508
525
  ```
509
526
 
510
- `Persistence.in_memory` is run through the same shipped contract source in
527
+ `PersistenceComposition.in_memory` is run through the same shipped contract source in
511
528
  Phronomy CI. The files under `spec/support/shared_examples/` are compatibility
512
529
  require wrappers only; the authoritative shared-example implementations live
513
530
  under `lib/phronomy/testing/persistence_contract/` so the core suite and external
@@ -292,7 +292,7 @@ remain durable conflict defense rather than distributed ownership.
292
292
  ## Tool execution modes
293
293
 
294
294
  The default dispatch helper is the private
295
- `Phronomy::Agent::Context::Capability::ToolExecutor`, colocated with Capability
295
+ `Phronomy::Tool::ToolExecutor`, colocated with Capability
296
296
  Base. Agent ToolInvocation supplies Runtime and admission policy for the
297
297
  standard path, and owns authorization and logical result handling. Custom
298
298
  `call_async` implementations keep the public Tool protocol. This ownership is
@@ -577,10 +577,35 @@ TaskResult reports only caller-facing settlement.
577
577
  ## EventLoop metrics
578
578
 
579
579
  `Phronomy::Metrics.snapshot` also reports EventLoop queue depth and lag values.
580
+
581
+ Metrics and Diagnostics only observe resources already owned by the default
582
+ Runtime. Calling `Metrics.snapshot`, `Diagnostics.snapshot` or `Diagnostics.dump`
583
+ does not initialize a Runtime, pool, EventLoop or timer. Missing resources report
584
+ zero values, including `offload_pool_size: 0` when no default pool exists. Once a
585
+ pool exists, this field remains its configured capacity, not a live-worker count.
586
+ Named pools are not aggregated into the default-pool metrics.
587
+
588
+ Retained resources remain observable during and after shutdown, including
589
+ incomplete cleanup. Observation does not reopen admission or change a shutdown
590
+ result. After the default Runtime is reset, missing resources again report zero;
591
+ the next snapshot uses any newly initialized default Runtime. Counters are sampled
592
+ independently and do not constitute an atomic cross-resource snapshot.
580
593
  Use these to distinguish worker saturation from EventLoop backlog/latency.
581
594
 
582
595
  ## Shutdown
583
596
 
597
+ Pool creation and registration close atomically before the shutdown resource set
598
+ is captured. Accepted continuations can still acquire resources during Runtime's
599
+ draining phase. Once resource shutdown begins, all pools close admission before
600
+ any pool waits for its workers.
601
+
602
+ The stop-phase monotonic deadline is shared by EventLoop and every pool join.
603
+ A join timeout does not stop a synchronous worker. Runtime reports incomplete
604
+ cleanup while any worker remains, even if that worker's TaskResult has already
605
+ been cancelled or timed out. It does not forcefully interrupt worker threads.
606
+ See [R8 unit19](architecture/r8-unit19.md) and its
607
+ [migration notes](migrations/r8-unit19.md) for the shutdown budget and result rules.
608
+
584
609
  `Runtime#shutdown` is terminal for that Runtime. It drains/terminates the
585
610
  Runtime-owned EventLoop, then closes pools and timers according to the Runtime
586
611
  shutdown contract.
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # The short durable phase of an Agent's already acquired runtime admission.
8
+ # A coordinator may join it to its reservation check; it cannot start an FSM,
9
+ # publish a provisional result or make the Agent interpret a parent's schema.
10
+ # @api public
11
+ class Admission
12
+ attr_reader :agent_id, :execution_id, :correlation
13
+
14
+ # Constructed by Agent execution, never restored from a durable record.
15
+ # @api private
16
+ def initialize(persistence:, root:, input:, config:, preparation_metadata:)
17
+ @persistence, @root, @input, @config = persistence, root, input, config
18
+ @preparation_metadata = preparation_metadata
19
+ @agent_id = root.agent_id
20
+ @execution_id = (config[:phronomy_reserved_execution_id] || SecureRandom.uuid).to_s.freeze
21
+ @correlation = Phronomy::Values::Immutable.copy(config[:phronomy_reservation])
22
+ @thread = Thread.current
23
+ end
24
+
25
+ # Own a standalone admission transaction.
26
+ # @api public
27
+ def accept
28
+ @persistence.coordinator.atomic { |scope| accept_in(scope) }
29
+ nil
30
+ end
31
+
32
+ # Participate without committing or publishing live state.
33
+ # @api public
34
+ def accept_in(scope)
35
+ check_thread!
36
+ raise Phronomy::ConfigurationError, "Agent admission is single-use" if @scope
37
+ @scope = scope
38
+ @persistence.participate(scope) do |records|
39
+ raise Phronomy::Error, "agent is closed: #{agent_id}" if @root.lifecycle_status == :closed
40
+ input_ref = records.contents.put_text(@input)
41
+ context_ref = records.contents.put_json(@config.fetch(:durable_context)) if @config.key?(:durable_context)
42
+ input_record = JournalRecord.new(agent_id: agent_id, kind: :input_received,
43
+ channel: :external, role: :user, content_ref: input_ref,
44
+ context_generation: @root.transcript_generation, context_candidate: false)
45
+ execution = AgentExecution.start(agent_root: @root, input_record: input_record,
46
+ execution_id: execution_id, metadata: {
47
+ "reservation" => correlation, "current_input_ref" => input_ref,
48
+ "durable_context_ref" => context_ref,
49
+ "execution_extension" => @config[:phronomy_execution_participant]&.binding&.to_h
50
+ }.merge(@preparation_metadata).compact)
51
+ input_record = JournalRecord.from_h(input_record.to_h.merge("execution_id" => execution_id))
52
+ execution = execution.with(execution_revision: 0, working_records: [input_record])
53
+ records.executions.create_active(execution)
54
+ next_root = @root.with(agent_revision: @root.agent_revision + 1, lifecycle_status: :active)
55
+ records.agents.save(agent_id, expected_revision: @root.agent_revision, root: next_root)
56
+ @result = [execution, next_root].freeze
57
+ end
58
+ nil
59
+ end
60
+
61
+ # Available to Agent execution only after the outermost successful response.
62
+ # @api private
63
+ def result
64
+ check_thread!
65
+ unless @result && @scope&.committed?
66
+ raise Phronomy::Persistence::TransactionError, "Agent admission has not committed"
67
+ end
68
+ @result
69
+ end
70
+
71
+ private
72
+
73
+ def check_thread!
74
+ raise Phronomy::Persistence::TransactionError, "Agent admission belongs to another execution context" unless @thread.equal?(Thread.current)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../common/error"
3
+ require_relative "../common/error"
4
4
 
5
5
  module Phronomy
6
6
  class AgentAlreadyExistsError < Phronomy::Error; end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../common/error"
3
+ require_relative "../common/error"
4
4
 
5
5
  module Phronomy
6
6
  class AgentBusyError < Phronomy::Error; end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../common/error"
3
+ require_relative "../common/error"
4
4
 
5
5
  module Phronomy
6
6
  class AgentPurgedError < Phronomy::Error; end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../common/error"
3
+ require_relative "../../common/error"
4
4
 
5
5
  module Phronomy
6
6
  class ExecutionRehydrationRequiredError < Phronomy::Error; end
@@ -12,6 +12,35 @@ module Phronomy
12
12
  # - approval notifications share the Agent listener as :approval_required;
13
13
  # - Recovery resolution uses resolve/resolve_async.
14
14
  module AsyncEventApi
15
+ def start_reserved_async(reservation:, input:, config: {})
16
+ unless reservation.is_a?(ReservedExecution) && reservation.agent_id == agent_id
17
+ raise ArgumentError, "reservation must belong to this Agent"
18
+ end
19
+ config = __invocation_config(_prepare_invocation_config(config, nil))
20
+ ExactExecution.start(agent: self, execution_id: reservation.execution_id, input: input,
21
+ config: config.merge(phronomy_reservation: reservation.correlation))
22
+ end
23
+
24
+ def resume_async(execution_id, config: {})
25
+ config = __invocation_config(_prepare_invocation_config(config, nil))
26
+ ExactExecution.start(agent: self, execution_id: execution_id, input: nil, config: config, resume_only: true)
27
+ end
28
+
29
+ def resume(execution_id, config: {})
30
+ _check_event_loop_reentrancy(:resume, :resume_async)
31
+ resume_async(execution_id, config: config).wait_result
32
+ end
33
+
34
+ def cancel_async(execution_id)
35
+ __assert_live_agent!
36
+ environment = __execution_environment
37
+ environment.submit(on_full: :raise) do
38
+ requested = persistence.request_cancellation(agent_id: agent_id, execution_id: execution_id)
39
+ ExecutionCancellation.signal(execution_id, agent_id, environment: environment) if requested
40
+ persistence.observe_execution(agent_id: agent_id, execution_id: execution_id)
41
+ end
42
+ end
43
+
15
44
  def invoke(
16
45
  input,
17
46
  config: {},
@@ -96,7 +125,7 @@ module Phronomy
96
125
 
97
126
  def approve_async(execution_id, approval_request_id:, approved: true, config: {})
98
127
  _reject_removed_generic_identity_keys!(config)
99
- owner = Phronomy::Agent::ExecutionRegistry.existing_for(Phronomy::Runtime.instance)&.agent_execution_owner(execution_id)
128
+ owner = Phronomy::Agent::ExecutionRegistry.existing_current&.agent_execution_owner(execution_id)
100
129
  coordinator = if owner&.agent&.equal?(self)
101
130
  owner.coordinator
102
131
  else
@@ -115,8 +144,8 @@ module Phronomy
115
144
  expected_execution_revision:,
116
145
  subject:,
117
146
  outcome:,
118
- result: Phronomy::Recovery::MISSING,
119
- error: Phronomy::Recovery::MISSING
147
+ result: Phronomy::Agent::RecoveryRules::MISSING,
148
+ error: Phronomy::Agent::RecoveryRules::MISSING
120
149
  )
121
150
  _check_event_loop_reentrancy(:resolve, :resolve_async)
122
151
  resolve_async(
@@ -134,8 +163,8 @@ module Phronomy
134
163
  expected_execution_revision:,
135
164
  subject:,
136
165
  outcome:,
137
- result: Phronomy::Recovery::MISSING,
138
- error: Phronomy::Recovery::MISSING
166
+ result: Phronomy::Agent::RecoveryRules::MISSING,
167
+ error: Phronomy::Agent::RecoveryRules::MISSING
139
168
  )
140
169
  Phronomy::Agent::RecoveryCoordinator.new(self).resolve(
141
170
  execution_id,
@@ -149,26 +178,9 @@ module Phronomy
149
178
 
150
179
  private
151
180
 
152
- # Framework-private execution-local event routing. This is intentionally
153
- # not a public compatibility path for invoke_async(..., on_event:).
154
- def __invoke_async_with_event_sink(
155
- input,
156
- on_event:, config: {},
157
- invocation_context: nil
158
- )
159
- raise ArgumentError, "on_event is required" unless on_event
160
-
161
- config = _prepare_invocation_config(config, invocation_context)
162
- _start_agent_operation(
163
- input,
164
- config: config,
165
- mode: :invoke,
166
- listener: on_event
167
- )
168
- end
169
-
170
181
  def _start_agent_operation(input, config:, mode:, listener:)
171
- config = _snapshot_durable_context(config)
182
+ config = __invocation_config(_snapshot_durable_context(config))
183
+ @_phronomy_execution_wiring = config.dup.freeze
172
184
  approval = _approval_configuration_snapshot(nil)
173
185
  execution_coordinator_for(config).start(
174
186
  input,
@@ -220,14 +232,7 @@ module Phronomy
220
232
  @execution_coordinator ||= Agent::ExecutionCoordinator.new(self)
221
233
  end
222
234
 
223
- def execution_coordinator_for(config)
224
- multi_agent = config.key?(:phronomy_handoff_bindings) ||
225
- config.key?(:phronomy_handoff_context)
226
- return execution_coordinator unless multi_agent
227
-
228
- @multi_agent_execution_coordinator ||=
229
- Phronomy::Agent::HandoffExecutionCoordinator.new(self)
230
- end
235
+ def execution_coordinator_for(_config) = execution_coordinator
231
236
  end
232
237
  end
233
238
  end