phronomy 0.25.0 → 0.27.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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -0
  3. data/CONTRIBUTING.md +4 -4
  4. data/README.md +14 -7
  5. data/VERIFY.sh +27 -27
  6. data/benchmark/bench_agent_invoke.rb +26 -22
  7. data/benchmark/bench_context_assembler.rb +4 -5
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  10. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  11. data/docs/architecture/agent-context.md +5 -3
  12. data/docs/architecture/agent-transition-ownership.md +89 -0
  13. data/docs/architecture/before-llm-input.md +6 -0
  14. data/docs/architecture/context-management.md +30 -0
  15. data/docs/architecture/context-preparation-steps.md +90 -0
  16. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  17. data/docs/architecture/execution-metadata-and-values.md +80 -0
  18. data/docs/architecture/generator-verifier-ownership.md +111 -0
  19. data/docs/architecture/multi-agent-handoff.md +8 -2
  20. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  21. data/docs/architecture/persistence.md +29 -6
  22. data/docs/architecture/refactoring-closure.md +334 -0
  23. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  24. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  25. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  26. data/docs/architecture/tracing.md +4 -4
  27. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  28. data/docs/architecture.md +209 -0
  29. data/docs/async-composition.md +205 -0
  30. data/docs/decisions/010-cooperative-first-concurrency.md +23 -23
  31. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  32. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  33. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  34. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  35. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  36. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  37. data/docs/decisions/032-storage-backend-composition.md +80 -0
  38. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  39. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  40. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  41. data/docs/decisions/036-context-contract-ownership.md +86 -0
  42. data/docs/decisions/037-common-definition-ownership.md +62 -0
  43. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  44. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  45. data/docs/decisions/040-configuration-default-composition.md +94 -0
  46. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  47. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  48. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  49. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  50. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  51. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  52. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  53. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  54. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  55. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  56. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  57. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  58. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  59. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  60. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  61. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  62. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  63. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  64. data/docs/decisions/README.md +35 -6
  65. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  66. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  67. data/docs/features.md +21 -12
  68. data/docs/getting-started.md +10 -10
  69. data/docs/migrations/0.15.md +5 -0
  70. data/docs/migrations/durable-semantic-coordination-v2.md +7 -1
  71. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  72. data/docs/migrations/neutral-storage-spi.md +43 -0
  73. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  74. data/docs/migrations/shared-state-multi-agent.md +44 -0
  75. data/docs/migrations/storage-backend-composition.md +117 -0
  76. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  77. data/docs/persistence-backends.md +162 -204
  78. data/docs/runtime-and-concurrency.md +205 -60
  79. data/lib/phronomy/agent/api/agent.rb +17 -0
  80. data/lib/phronomy/agent/async_event_api.rb +2 -2
  81. data/lib/phronomy/agent/base.rb +55 -251
  82. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  83. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  84. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  85. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  86. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  87. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  88. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  89. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  90. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  91. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  92. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  93. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  94. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  95. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  96. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  97. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  98. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  99. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +20 -96
  100. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  101. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  102. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +12 -13
  103. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  104. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  105. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  106. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  107. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  108. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  109. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  110. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  111. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  112. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +22 -73
  113. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  114. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  115. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  116. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  117. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  118. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  119. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  120. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  121. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  122. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  123. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  124. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  125. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  126. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  127. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  128. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  129. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  130. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  131. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  132. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  133. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  134. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  135. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  136. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  137. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  138. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  139. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  140. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +36 -41
  141. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  142. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +9 -13
  143. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  144. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  145. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  146. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  147. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  148. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  149. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  150. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  151. data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +2 -2
  152. data/lib/phronomy/common/configuration_error.rb +7 -0
  153. data/lib/phronomy/common/error.rb +5 -0
  154. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  155. data/lib/phronomy/common/values/serializable.rb +32 -0
  156. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  157. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  158. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  159. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  160. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  161. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +15 -8
  162. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  163. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  164. data/lib/phronomy/engine/concurrency/offload_pool.rb +29 -16
  165. data/lib/phronomy/engine/concurrency/operation_binding.rb +43 -0
  166. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +4 -62
  167. data/lib/phronomy/engine/concurrency/result_collector.rb +99 -0
  168. data/lib/phronomy/engine/concurrency/result_composition.rb +145 -0
  169. data/lib/phronomy/engine/concurrency/subscriptions.rb +68 -0
  170. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  171. data/lib/phronomy/engine/event_loop.rb +160 -626
  172. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  173. data/lib/phronomy/engine/execution.rb +229 -0
  174. data/lib/phronomy/engine/execution_cancellation_error.rb +14 -0
  175. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  176. data/lib/phronomy/engine/execution_timeout_error.rb +14 -0
  177. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  178. data/lib/phronomy/engine/fsm_session.rb +38 -31
  179. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +3 -1
  180. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +3 -1
  181. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +3 -1
  182. data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +13 -1
  183. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  184. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  185. data/lib/phronomy/engine/runtime/timer_queue.rb +11 -0
  186. data/lib/phronomy/engine/runtime.rb +80 -84
  187. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  188. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  189. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  190. data/lib/phronomy/engine/{task.rb → task_result.rb} +101 -42
  191. data/lib/phronomy/engine/timeout_error.rb +7 -0
  192. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  193. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  194. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  195. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  196. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  197. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  198. data/lib/phronomy/llm_adapter/base.rb +2 -2
  199. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  200. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  201. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  202. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  203. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  204. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  205. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  206. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  207. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +8 -8
  208. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  209. data/lib/phronomy/multi_agent/orchestrator.rb +36 -68
  210. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  211. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  212. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  213. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  214. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  215. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  216. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  217. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  218. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  219. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  220. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  221. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  222. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  223. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  224. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  225. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  226. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  227. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  228. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  229. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  230. data/lib/phronomy/storage/backend.rb +101 -0
  231. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  232. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  233. data/lib/phronomy/storage/blobs.rb +31 -0
  234. data/lib/phronomy/storage/condition.rb +25 -0
  235. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  236. data/lib/phronomy/storage/conflict_error.rb +9 -0
  237. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  238. data/lib/phronomy/storage/entry.rb +33 -0
  239. data/lib/phronomy/storage/guard_ref.rb +13 -0
  240. data/lib/phronomy/storage/not_found_error.rb +9 -0
  241. data/lib/phronomy/storage/record_codec.rb +177 -0
  242. data/lib/phronomy/storage/records.rb +61 -0
  243. data/lib/phronomy/storage/resource.rb +126 -0
  244. data/lib/phronomy/storage/scope.rb +25 -0
  245. data/lib/phronomy/storage/serialization_error.rb +9 -0
  246. data/lib/phronomy/storage/streams.rb +44 -0
  247. data/lib/phronomy/storage/transaction_error.rb +10 -0
  248. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  249. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  250. data/lib/phronomy/storage/validation.rb +53 -0
  251. data/lib/phronomy/storage/view.rb +131 -0
  252. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +5 -3
  253. data/lib/phronomy/testing/fake_clock.rb +13 -9
  254. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  255. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  256. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  257. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  258. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  259. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  260. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  261. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  262. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  263. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  264. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  265. data/lib/phronomy/tools/agent.rb +6 -6
  266. data/lib/phronomy/vector_store/async_backend.rb +5 -5
  267. data/lib/phronomy/vector_store/embeddings/base.rb +2 -2
  268. data/lib/phronomy/version.rb +1 -1
  269. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  270. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  271. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  272. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  273. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +126 -75
  274. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  275. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  276. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  277. data/lib/phronomy/workflow/phase_machine_builder.rb +8 -8
  278. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  279. data/lib/phronomy.rb +52 -94
  280. data/scripts/api_snapshot.rb +4 -2
  281. data/scripts/storage_spi_snapshot.rb +36 -0
  282. data/sig/phronomy/agent.rbs +4 -5
  283. data/sig/phronomy/execution_receiver.rbs +34 -0
  284. data/sig/phronomy/extensions.rbs +5 -5
  285. data/sig/phronomy/handoff.rbs +4 -2
  286. data/sig/phronomy/multi_agent.rbs +19 -3
  287. data/sig/phronomy/persistence.rbs +7 -91
  288. data/sig/phronomy/runtime.rbs +34 -7
  289. data/sig/phronomy/storage.rbs +174 -0
  290. data/sig/phronomy/tool.rbs +11 -2
  291. data/sig/phronomy/workflow.rbs +1 -1
  292. metadata +236 -99
  293. data/lib/phronomy/agent/execution_coordinator.rb +0 -3138
  294. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  295. data/lib/phronomy/agent/recovery_support.rb +0 -504
  296. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  297. data/lib/phronomy/agent/tool_executor.rb +0 -55
  298. data/lib/phronomy/generator_verifier.rb +0 -369
  299. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  300. data/lib/phronomy/multi_agent/fan_out_invocation.rb +0 -137
  301. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +0 -118
  302. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  303. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  304. data/lib/phronomy/persistence/in_memory.rb +0 -690
  305. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  306. data/lib/phronomy/persistence.rb +0 -276
  307. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  308. data/lib/phronomy/workflow_recovery.rb +0 -123
  309. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  310. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  311. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  312. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  313. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  314. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  315. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  316. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  317. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  318. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  319. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  320. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  321. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  322. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  323. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -0,0 +1,74 @@
1
+ # ADR-039: Runtime and Configuration Lifecycle Ownership
2
+
3
+ ## Status
4
+
5
+ Accepted on the architecture refactoring branch.
6
+
7
+ Concrete-default ownership in decision 1 is amended by
8
+ [040-configuration-default-composition](040-configuration-default-composition.md).
9
+ The reset ownership and algorithm below remain current.
10
+
11
+ Amends the configuration ownership in
12
+ [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md).
13
+ Its loading rules, direct-root allowlist, and compatibility requirements remain
14
+ in force.
15
+
16
+ ## Context
17
+
18
+ `Phronomy.reset_runtime!` shuts down the default Runtime and replaces global
19
+ configuration only after successful Runtime cleanup. Its configuration grace
20
+ period preservation is application lifecycle coordination between two owners.
21
+ Placing this operation in `configuration/global_configuration.rb` made ordinary
22
+ configuration readers point to a file that also controlled Runtime shutdown.
23
+
24
+ Engine reads configuration for pool sizes, logging, and shutdown defaults.
25
+ Configuration access must not also own the reverse Runtime lifecycle operation.
26
+ Moving that operation into `common/` would give common definitions an Engine
27
+ dependency. Moving it into an Engine primitive would mix global configuration
28
+ replacement with Runtime execution mechanics.
29
+
30
+ ## Decision
31
+
32
+ 1. Keep configuration values, concrete defaults, global access, replacement,
33
+ and scoped overrides in `configuration/`.
34
+ 2. Place `Phronomy.reset_runtime!` in
35
+ `runtime_composition/global_runtime.rb`. This responsibility group coordinates
36
+ Runtime and configuration. Engine and configuration implementation files
37
+ must not require the composition file or the application entry point.
38
+ 3. Keep this namespace-reopening file outside Zeitwerk name inference. Load it
39
+ explicitly from `lib/phronomy.rb` after global configuration access is
40
+ available. Do not introduce a `Phronomy::RuntimeComposition` constant,
41
+ compatibility alias, registration hook, or alternate reset API.
42
+ 4. Preserve the reset method body and signature. In particular, Runtime cleanup
43
+ happens before configuration replacement; an error from cleanup propagates
44
+ without resetting configuration. Preserve the timeout default, previous
45
+ grace value handling, laziness of the default Runtime, and return value.
46
+
47
+ ## Dependency interpretation and remaining work
48
+
49
+ The direct configuration-to-Runtime reference moves to the composition owner.
50
+ Ordinary configuration readers still resolve to the configuration accessor
51
+ file. Reference analysis must resolve each `Phronomy` operation to its actual
52
+ method definition, not assign every operation to an arbitrary namespace file.
53
+
54
+ This step does not remove the separate cycle formed by concrete defaults:
55
+ `Configuration` constructs `LLMAdapter::RubyLLM`, its base supplies asynchronous
56
+ execution through `Runtime`, and Runtime reads global configuration. The
57
+ default tracer is also concrete. Preserving `Configuration.new` behavior while
58
+ separating default construction requires a separate design decision. Do not
59
+ hide these references with dynamic constant lookup or claim that the entire
60
+ configuration/Engine cycle has disappeared.
61
+
62
+ ## Verification obligations
63
+
64
+ - Guard that configuration files can be loaded without loading Engine or
65
+ Runtime composition, exposing the reset operation, or directly referencing
66
+ Runtime. This loading check does not promise a new public partial-loading API.
67
+ - Verify ordinary application loading exposes the existing reset API and eager
68
+ loading creates no synthetic composition namespace.
69
+ - Compare production method definitions against the preceding layout candidate;
70
+ this extraction changes no method bodies.
71
+ - Run the existing configuration, Runtime lifecycle, shutdown participant,
72
+ compatibility, and integration tests and repository style/type gates.
73
+ - Report remaining cycles with the same analyzer and distinguish the unapplied
74
+ candidate source tree from the applied baseline commit.
@@ -0,0 +1,94 @@
1
+ # ADR-040: Configuration Default Composition
2
+
3
+ ## Status
4
+
5
+ Accepted on the architecture refactoring branch.
6
+
7
+ Amends concrete-default ownership in
8
+ [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
9
+ and [039-runtime-configuration-lifecycle-ownership](039-runtime-configuration-lifecycle-ownership.md).
10
+ Their loading, public compatibility, and Runtime reset rules remain in force.
11
+ The provider-call and framework-owned async boundary in
12
+ [027-llm-adapter-provider-boundary](027-llm-adapter-provider-boundary.md) is unchanged.
13
+
14
+ ## Context
15
+
16
+ `Configuration` holds settings read by Engine and other framework components.
17
+ Its constructor also selected and instantiated `LLMAdapter::RubyLLM` and
18
+ `Tracing::NullTracer`. The adapter base uses Runtime for default asynchronous
19
+ execution, while Runtime reads configuration. Tracing also reads configuration.
20
+ Concrete default selection therefore created reverse implementation references
21
+ from settings to their consumers.
22
+
23
+ Simply moving the constructor into another file that reopens `Configuration`
24
+ would not separate these responsibilities. Changing `Configuration.new` to
25
+ produce unset components, replacing it with a different application factory,
26
+ or sharing prebuilt defaults would change existing behavior.
27
+
28
+ ## Decision
29
+
30
+ 1. `configuration/` owns setting values, validation, global access, replacement,
31
+ and scoped overrides. It must not select concrete adapters, tracers, or Runtime
32
+ implementations. Scalar defaults remain in `Configuration#initialize`.
33
+ 2. `runtime_composition/configuration_defaults.rb` selects `RubyLLM` and
34
+ `NullTracer` with explicit constant references in zero-argument factories.
35
+ It binds these factories through `Configuration.install_default_factories`
36
+ during application loading. This method is `@api private`; it is a narrow
37
+ internal boot operation, not a configurable provider registry or extension SPI.
38
+ 3. `Configuration` owns the factory slots it consumes. Binding freezes those
39
+ slots once. The inherited constructor uses the same binding for subclasses,
40
+ avoiding class-instance-variable inheritance differences. The constructor
41
+ invokes each factory for each configuration; it does not cache instances or
42
+ lazily replace attributes on access. Applications still use `tracer=` and
43
+ `llm_adapter=` to supply their instances.
44
+ 4. `lib/phronomy.rb` explicitly loads the binding file after Zeitwerk setup and
45
+ before exposing global configuration access or loading lifecycle extensions.
46
+ The binding file is excluded from automatic namespace inference, as is the
47
+ existing Runtime coordination file. It introduces no composition namespace.
48
+ Merely binding factories does not create global configuration, instantiate
49
+ components, or start Runtime. Arbitrary internal file loading remains outside
50
+ the public application-loading contract.
51
+ 5. Preserve the zero-argument constructor, fresh default instances, subclass
52
+ behavior, shallow scoped restoration, explicit overrides, Runtime laziness,
53
+ reset ordering, exception propagation, and previous grace-period handling.
54
+ No adapter SPI, async method, provider behavior, transport policy, public
55
+ signature, or persisted format changes in this step.
56
+
57
+ The binding in decision 2 is limited to default component construction. It does
58
+ not add a Runtime reset registration hook or amend ADR-039's reset algorithm.
59
+
60
+ ## Dependency interpretation and remaining work
61
+
62
+ This is dependency inversion at the component selection boundary. The settings
63
+ owner defines and consumes factory slots; application composition supplies their
64
+ implementations. Settings can be instantiated with local factories without
65
+ loading Engine, adapters, or tracing. No dynamic constant lookup is used to
66
+ hide implementation references.
67
+
68
+ The runtime call from `Configuration#initialize` through the factory to a
69
+ concrete constructor still exists. Static reference graphs do not generally
70
+ follow this injected call; reports must distinguish source references from
71
+ runtime factory invocation. Construction does not call `Runtime.instance`:
72
+ the adapter's default pool is acquired later when an async method is called.
73
+ The previous graph cycle was not infinite constructor recursion.
74
+
75
+ `LLMAdapter::Base` still owns framework async wrappers and references Runtime.
76
+ Runtime, pools, tracing, and Agent retain other dependencies and cycles. This
77
+ step does not claim complete provider independence or an acyclic repository.
78
+
79
+ ## Verification obligations
80
+
81
+ - Guard that configuration source does not reference Runtime, LLMAdapter, or
82
+ Tracing, and demonstrate settings construction with supplied local factories
83
+ without those implementations or application composition loaded.
84
+ - Verify binding and normal/eager loading do not instantiate global settings
85
+ or start Runtime, including after preloading configuration definitions.
86
+ - Preserve fresh defaults, subclass construction, explicit overrides, scoped
87
+ component identity restoration, and reset behavior on failed Runtime cleanup.
88
+ - Run the adapter contract/routing tests, configuration and Runtime lifecycle
89
+ tests, integration tests, and repository style/type/annotation gates.
90
+ - Preserve the API snapshot without regeneration and report the private boot
91
+ method separately; do not claim that the snapshot covers Configuration or
92
+ every framework singleton operation.
93
+ - Compare dependencies against the applied baseline with the same analyzer and
94
+ identify the unapplied candidate's exact source tree.
@@ -0,0 +1,100 @@
1
+ # ADR-041: Feature-Owned Identity Registries
2
+
3
+ ## Status
4
+
5
+ Accepted on the architecture refactoring branch.
6
+
7
+ Amends the implementation owner of the process-local identity registry in
8
+ [025-process-local-agent-ownership-and-runtime-admission](025-process-local-agent-ownership-and-runtime-admission.md).
9
+ The one-owner invariant, Runtime lifetime, public identity operations, durable
10
+ conflict defenses, and EventLoop execution admission remain in force.
11
+
12
+ ## Context
13
+
14
+ Runtime instantiated Agent and Team identity registries, exposed their specific
15
+ operations, and invoked their shutdown methods directly. Agent identity
16
+ reservation interpreted Agent lifecycle and Storage exceptions inside Engine.
17
+ These are feature policies even though their lifetime is bounded by Runtime.
18
+
19
+ Moving files while preserving concrete construction, forwarding methods, or
20
+ aliases under Runtime would retain the same dependency. A process-global weak
21
+ map would also lose the required strong ownership and Runtime isolation.
22
+
23
+ ## Decision
24
+
25
+ 1. `Agent::OwnershipRegistry` in `agent/ownership_registry.rb` owns Agent identity
26
+ reservation, materialization, purge, uncertain-outcome policy, and detachment.
27
+ `MultiAgent::TeamOwnershipRegistry` in `multi_agent/team_ownership_registry.rb`
28
+ owns Team identity and construction exclusion. Feature callers use these
29
+ components directly. The old Runtime registry constants and forwarding
30
+ methods are removed; they were internal and receive no compatibility aliases.
31
+ 2. Each registry lazily registers itself under its class key through Runtime's
32
+ existing internal shutdown participant contract. Candidate construction has
33
+ no side effects. Concurrent registration returns one authoritative instance
34
+ per Runtime. Runtime strongly retains it; neither GC nor execution completion
35
+ releases an identity. Registration after closure is rejected even for an
36
+ existing key.
37
+ 3. `Runtime#__shutdown_participant(key:)` only looks up an existing participant,
38
+ including during/after shutdown. It does not create one or admit work.
39
+ Registry `for(runtime)` reuses it; each registry's gate rejects create/load/
40
+ purge admission after closure. `existing_for(runtime)` supports `get` without
41
+ creating a registry. Admitted transitions may finish through the retained
42
+ instance after closure. Lookup is not an admission bypass.
43
+ 4. Participants implement `begin_draining` and `wait_until_idle(deadline)`.
44
+ Runtime closes every gate under its lifecycle lock, including on EventLoop
45
+ failure, before waiting outside that lock with one absolute monotonic deadline.
46
+ Closure must be short, idempotent, and must not call Runtime. For identity
47
+ registries, idle means no active construction/purge transition, not no live
48
+ objects. Stable recovery-required entries do not prevent shutdown.
49
+ 5. Add the optional `after_runtime_shutdown` operation. Runtime invokes it outside
50
+ its lifecycle lock only if all participant waits succeed, no participant hook
51
+ has failed, EventLoop's idle/join checks pass without cancellation timeout,
52
+ and pools/timers shut down successfully. Existing participants without the
53
+ hook remain valid. The hook must be idempotent, short, and perform no I/O;
54
+ its return value is ignored. Agent detachment and Team directory clearing
55
+ implement this protocol. Runtime does not know their concrete classes.
56
+ 6. A finalization exception does not skip later participants. Cleanup is then
57
+ incomplete, the first failure is retained, and default Runtime replacement
58
+ is prohibited. Completed releases are not rolled back. Repeated shutdown
59
+ returns the cached result rather than rerunning partial finalization.
60
+ 7. Purge completion/abort/uncertainty updates the Agent object before publishing
61
+ the corresponding registry state, under the same registry mutex. These
62
+ internal Agent hooks only update local fields and cannot call Runtime or
63
+ perform I/O. Shutdown cannot observe a stable transition and detach the
64
+ Agent before that object's transition has finished.
65
+
66
+ ## Guarantees and limits
67
+
68
+ | Subject | Property and provider | Failure scope / X0 | Result |
69
+ |---|---|---|---|
70
+ | Mutable Agent identity in one Runtime | At most one live owner per ID, reserved by Agent::OwnershipRegistry before materialization | Normal operation, F0 known failures, and F2 conflicts; X0 not crossed by the registry | YES within this Runtime; no cross-process exclusion |
71
+ | Uncertain Agent create/purge | Existing recovery-required policy retains the reservation | F1 durable outcome uncertainty, possibly with F0; no new X0 guarantee | YES for fail-closed local reservation; NO claim of outcome reconciliation |
72
+ | Old Agent references at Runtime replacement | Registry finalization detaches references before a completed reset | Normal shutdown and F0/F3 stop failures; X0 not managed here | CONDITIONAL on cleanup completion; failed/incomplete cleanup retains the Runtime |
73
+ | Durable facts after Runtime/process loss | Existing Persistence and recovery contracts | F4; X0 remains separate | No new guarantee; confirmed durable state and external effects are not rewritten by this change |
74
+
75
+ Team's existing conflict, Persistence identity, and construction-failure behavior
76
+ remain unchanged; Agent-specific recovery semantics are not imposed on Team.
77
+ No record format, transaction domain, public API, RBS shape, adapter SPI, or
78
+ worker mechanism changes. Runtime construction/shutdown of an unused instance
79
+ must not cause additional Agent, MultiAgent, or Storage loads; application-entry
80
+ bootstrap is measured separately.
81
+
82
+ EventLoop still owns Agent execution state/admission and Workflow-specific
83
+ control. Runtime's execution-owner and admission queries remain until a separate
84
+ execution-service extraction. This step does not make Engine feature-neutral.
85
+ Static graphs also do not follow the dynamic participant callbacks: the feature
86
+ implementation still runs through Engine's generic shutdown contract.
87
+
88
+ ## Verification obligations
89
+
90
+ - Concurrent registration/materialization, exact-instance get/load, duplicate
91
+ create rejection, incompatible class/Persistence checks, and purge behavior.
92
+ - Lookup without registration, closed admission on shutdown and EventLoop
93
+ failure, admitted construction completion, and incomplete-cleanup retention.
94
+ - Finalization after quiescence, outside the lifecycle lock, with all later
95
+ participants visited after an exception and no reset after failure.
96
+ - Purge object/registry transitions cannot be observed as idle halfway through.
97
+ - Source/loading boundaries, API snapshot, RBS, annotations, and existing suites.
98
+
99
+ Tests of simulated uncertain outcomes verify reservation policy, not arbitrary
100
+ external-effect exactly-once behavior or cross-process ownership.
@@ -0,0 +1,106 @@
1
+ # ADR-042: Feature-Owned Execution State on EventLoop
2
+
3
+ **Status**: Accepted
4
+ **Date**: 2026-09-21
5
+ **Amends**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md), [025-process-local-agent-ownership-and-runtime-admission](025-process-local-agent-ownership-and-runtime-admission.md), [026-workflow-runtime-admission-and-durable-terminal-barrier](026-workflow-runtime-admission-and-durable-terminal-barrier.md) for implementation ownership, delivery and shutdown admission
6
+ **Complements**: [041-feature-owned-identity-registries](041-feature-owned-identity-registries.md)
7
+
8
+ ## Problem
9
+
10
+ EventLoop implemented Agent admissions, execution snapshots, completion waiters,
11
+ physical-work tracking and recovery errors, as well as Workflow segment
12
+ admissions, routing and recovery-required state. Its dispatcher selected Agent
13
+ coordinators and Workflow runners. These are feature policies inside Engine;
14
+ retaining them behind dynamic calls would not remove the ownership problem.
15
+
16
+ ## Decision
17
+
18
+ `Agent::ExecutionRegistry` owns Agent admission, immutable execution entries,
19
+ read-only owner lookup, completion waiters and physical quiescence supervision.
20
+ `WorkflowExecutionRegistry`, under `workflow/execution/`, owns Workflow segment
21
+ admission, instance-to-session routing and recovery-required admissions.
22
+ Both retain the existing semantic identities, exceptions and durable barriers.
23
+ Neither registry is the Runtime-lifetime Agent/Team identity registry of ADR-041.
24
+
25
+ All normal feature state mutation still runs on the one EventLoop thread.
26
+ Workers only enqueue results or physical-completion notifications. The Engine
27
+ `ExecutionReceiver` base defines an internal registration/delivery/lifecycle
28
+ contract; it is not an application callback or plugin SPI. Feature code creates
29
+ its receiver lazily. Engine never constructs a concrete feature receiver and
30
+ does not interpret feature IDs, commands, admission states or exceptions.
31
+ Runtime provides only a non-creating EventLoop lookup for feature inspection.
32
+
33
+ ### Registration, delivery and drain
34
+
35
+ One EventLoop strongly retains one receiver per feature key. Registration is
36
+ allowed only while running. Existing lookup never reopens admission or creates
37
+ another loop. A receiver is bound to its original EventLoop.
38
+
39
+ The generic FIFO delivery contains a registered receiver, immutable command,
40
+ admission flag and optional caller completion. An in-memory envelope token
41
+ identifies only a pending delivery; it is not durable identity, a generation
42
+ counter, or authority for accepting a semantic result. Feature coordinators
43
+ continue to validate execution revisions, operation IDs and current FSM state.
44
+
45
+ Engine closes new receiver registration and new admission deliveries before
46
+ testing idleness. Agent start/approval resume, Recovery install/resolve and
47
+ Workflow start/resume are new requests at this boundary. A request queued before
48
+ drain remains accepted and may establish its feature admission during drain.
49
+ Existing worker results, physical-completion notifications, cancellation and
50
+ session events may continue while the loop drains. A continuation cannot claim
51
+ a new admission after the gate closes.
52
+
53
+ Engine counts a queued or currently dispatching receiver message until dispatch
54
+ finishes. This covers the interval before the feature admission exists. Feature
55
+ idleness covers admission-before-FSM-registration and logical-completion-before-
56
+ physical-quiescence intervals. Suspended/recovery-required admissions retain
57
+ their exclusion without indefinitely preventing shutdown.
58
+
59
+ Feature state reads/writes and Engine idleness use the same lifecycle mutex.
60
+ `idle?` is called with that lock held and must be short, nonblocking, and must
61
+ not call Runtime or reacquire the lock. Other state operations use the shared
62
+ synchronization helper. Delivery, TaskResult callbacks and receiver shutdown
63
+ execute outside the lock. Workflow routing resolves the admission and enqueues
64
+ to the currently admitted FSM under that same lock; it does not use a stale
65
+ lookup followed by an independent enqueue.
66
+
67
+ Generic FSM registration may attach its receiver. When the FSM reports
68
+ recovery-required retirement, Engine notifies that receiver; Workflow decides
69
+ how to retain its logical admission. Engine does not inspect Workflow state.
70
+
71
+ ### Failure and final invalidation
72
+
73
+ On dispatcher failure, new delivery is closed. Engine fails current/queued
74
+ request completions and FSM waiters, then notifies every receiver with the
75
+ failure on the failing EventLoop thread. Each receiver clears its own state.
76
+
77
+ On normal shutdown, Engine notifies receivers only after its thread has joined.
78
+ This is exclusive final reference invalidation, not management-thread execution
79
+ progression. Agent fails retained nonterminal completion waiters with
80
+ `ExecutionRehydrationRequiredError`; Engine never selects that exception.
81
+ Workflow retains the prior behavior of not fabricating a terminal result from
82
+ an uncertain durable outcome. A receiver cleanup exception does not prevent
83
+ later receivers from being visited; cleanup is incomplete and default Runtime
84
+ replacement is refused. A loop still alive at timeout is not invalidated.
85
+
86
+ ## Guarantees and limits
87
+
88
+ | Subject/property | Provider | Failure/boundary | Result and condition |
89
+ |---|---|---|---|
90
+ | Same-process exclusion and single-writer live state | Feature registries, EventLoop delivery and shared lifecycle lock | F2/F3; no X0 | YES within one Runtime; no cross-process exclusion |
91
+ | Clean shutdown waits for accepted admission and supervised physical work | Engine pending-delivery count plus feature idle predicates and OffloadPool completion | F0/F3; may observe work that crossed X0 | CONDITIONAL on work becoming quiescent before shutdown deadlines; no external effect rollback |
92
+ | Failed/terminated loop cannot continue authoritative live mutation | Closed delivery gate, loop-thread checks and exclusive receiver invalidation | F0/F3/F4; no new X0 | YES for framework-managed live state; process loss requires existing durable recovery |
93
+ | Uncertain durable execution is not released as successful | Existing Agent/Workflow durable barriers and recovery states | F1/F4; X0 unchanged | CONDITIONAL on the existing Persistence/reconciliation contracts; this change adds no durability or exactly-once guarantee |
94
+
95
+ Public Agent/Workflow APIs, persisted formats, TaskResult semantics and the
96
+ OffloadPool worker implementation are unchanged. Internal EventLoop feature
97
+ methods and Runtime Agent forwarding methods are removed without aliases.
98
+
99
+ ## Rejected alternatives
100
+
101
+ - Move Agent errors to Engine/common while keeping Agent decisions in Engine.
102
+ - Have Engine construct concrete feature registries or choose coordinators.
103
+ - Move only static references while retaining Workflow-specific maps/dispatch.
104
+ - Split feature and idle locks, allowing shutdown to miss newly admitted work.
105
+ - Invoke arbitrary callbacks without registered receiver and lifecycle rules.
106
+ - Replace OffloadPool physical-completion supervision with new worker threads.
@@ -0,0 +1,75 @@
1
+ # ADR-043: Storage-Owned Execution Constraint Notifications
2
+
3
+ The raw fixed-repository/error portions are amended by [ADR-058](058-neutral-storage-primitives.md). Domain ownership and applicable transaction/uncertainty decisions remain in force.
4
+
5
+
6
+ **Status**: Accepted
7
+ **Date**: 2026-09-21
8
+ **Refines**: [033-domain-persistence-ownership](033-domain-persistence-ownership.md) and the Beta Backend error contract in [persistence-backends](../persistence-backends.md)
9
+
10
+ ## Problem
11
+
12
+ InMemory and the SQLite/PostgreSQL reference backends raised `AgentBusyError`
13
+ for stored nonterminal-execution constraints. A storage implementation therefore
14
+ selected a feature lifecycle exception, even though domain repositories already
15
+ owned record conversion and public operation semantics.
16
+
17
+ ## Decision
18
+
19
+ `Storage::ActiveExecutionConflictError < Storage::ConflictError` identifies only
20
+ a stored nonterminal execution preventing another admission or an idle-only
21
+ operation for the same owner. It contains no Agent/Team class, domain object,
22
+ callback, or new persisted field. Generic duplicate IDs, stale revisions and
23
+ other precondition failures remain ordinary `Storage::ConflictError` values;
24
+ connection errors and uncertain outcomes retain their existing error types.
25
+
26
+ Raw execution repositories use this subtype from `create_active`, `assert_idle!`,
27
+ and any existing `save` branch that detects the same active-owner constraint.
28
+ Agent and MultiAgent execution repository facades translate only this subtype
29
+ to the existing public `AgentBusyError`, preserving its message and Ruby cause.
30
+ They do not catch every `ConflictError` or reinterpret database exceptions.
31
+ The existing Team public error remains unchanged; choosing a different Team
32
+ lifecycle exception would be a separate API decision.
33
+
34
+ Constraint detection and the write remain inside the same backend consistency
35
+ boundary. No preflight lookup is moved above the backend. Domain translation
36
+ occurs inside the existing transaction block so the mapped exception still
37
+ causes rollback before commit. SQL statements, lock order, connection binding,
38
+ indexes, persisted records and transaction boundaries are unchanged.
39
+
40
+ ## Compatibility and migration
41
+
42
+ This intentionally changes the Beta raw Backend error contract. Callers of
43
+ `backend.executions` and `backend.team_executions` must catch the new storage
44
+ subtype instead of `AgentBusyError`. Callers of the domain-facing Persistence,
45
+ Agent and Team APIs continue receiving `AgentBusyError` for that condition.
46
+
47
+ The core, InMemory and both reference SQL backends are migrated together. Apply
48
+ the core before the new SQL sources; the new backend sources require the new
49
+ storage constant. An old backend that still raises `AgentBusyError` passes
50
+ through an updated domain facade unchanged, but no longer conforms to the new
51
+ raw SPI. There is no fallback alias in Storage or Engine.
52
+
53
+ The shared backend suite now checks raw notifications separately from domain
54
+ exceptions, including duplicate-ID and stale-revision distinction and rollback.
55
+ The storage isolation guard removes its former Agent lifecycle file exemption.
56
+
57
+ ## Guarantees and limits
58
+
59
+ | Subject/property | Provider | Failure/boundary | Result |
60
+ |---|---|---|---|
61
+ | One nonterminal execution per stored owner | Existing atomic admission, backend lock/transaction/DB constraint | F2; no X0 | CONDITIONAL on the backend satisfying the existing atomic_admission contract; no new cross-process owner lease |
62
+ | Uncommitted writes roll back when a mapped constraint escapes | Existing storage transaction with domain conversion inside its block | F0/F2; no X0 | YES for an ordinary known pre-commit constraint failure; not a claim about uncertain commits |
63
+ | Caller-facing busy errors retain their meaning | Feature repository translation of the dedicated subtype | F0/F2; no X0 | YES for the documented domain repository paths; unrelated conflicts are not converted |
64
+
65
+ This change adds no F1 outcome reconciliation, F4 rehydration, external-effect
66
+ rollback or exactly-once guarantee. The eight repository names and Agent
67
+ watermark that ADR-033 leaves in Storage remain an explicit intermediate state.
68
+ Removing the exception dependency does not make the entire SPI domain-neutral.
69
+
70
+ ## Rejected alternatives
71
+
72
+ - Move `AgentBusyError` into common definitions or alias it from Storage.
73
+ - Translate every `ConflictError` into a busy error.
74
+ - Check active state in the domain facade before performing a separate write.
75
+ - Migrate only InMemory and silently leave the SQL raw contract inconsistent.
@@ -0,0 +1,106 @@
1
+ # ADR-044: Agent Default and One-Shot Composition
2
+
3
+ **Status**: Accepted on the architecture refactoring branch
4
+ **Date**: 2026-09-21
5
+ **Refines**: [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
6
+ and [040-configuration-default-composition](040-configuration-default-composition.md)
7
+
8
+ ## Problem
9
+
10
+ `Agent::Base` selected the concrete default with `Persistence.in_memory`.
11
+ `Agent.run_once`, defined beside Agent lifecycle namespace loading, also selected
12
+ and constructed Persistence. Both files therefore depended on the higher-level
13
+ Persistence composition API, but for different reasons.
14
+
15
+ Default selection is an injected construction dependency of Agent execution.
16
+ One-shot execution is itself a composition API. Treating both as lower-level
17
+ Agent execution obscured those responsibilities. Replacing one-shot storage with
18
+ the configured shared Persistence would change isolation and existing behavior.
19
+
20
+ ## Decision
21
+
22
+ 1. Agent owns the private `Agent::DefaultPersistence` construction contract. A
23
+ zero-argument factory is bound once and frozen during application loading.
24
+ `Base` invokes it only after the explicit `persistence:` and configured
25
+ `configuration.persistence` values have both been excluded, preserving their
26
+ existing truthy fallback order. It neither caches the constructed instance
27
+ nor writes it to configuration. Subclasses consume the same binding.
28
+ 2. `runtime_composition/agent_defaults.rb` selects `Persistence.in_memory` in
29
+ that factory. Binding does not invoke it, create settings, or start Runtime.
30
+ The binding is not a public plugin registry, an alternative application
31
+ configuration mechanism, or a new Runtime service locator. Runtime and
32
+ configuration resets do not replace or reinstall it.
33
+ 3. `agent/composition/run_once.rb` owns the actual `Agent.run_once` definition.
34
+ It remains `Phronomy::Agent.run_once`, with the same parameters, forwarding,
35
+ result, and exception behavior. It constructs fresh ephemeral Persistence on
36
+ every call even when global Persistence is configured. It does not consume
37
+ Base's fallback factory. Rejecting simultaneous `on_event:` and a block still
38
+ occurs before storage or Agent construction.
39
+ 4. `agent/api/agent.rb` retains `StreamEvent` and lifecycle extension loading.
40
+ It has no delegate to the higher-level one-shot implementation. The
41
+ application entry explicitly requires the namespace extensions, factory
42
+ binding, and one-shot method definition after Zeitwerk setup and global
43
+ configuration access installation. The composition directory and binding
44
+ file are ignored by Zeitwerk because they wire existing constants rather
45
+ than introduce matching public namespaces. Agent execution never requires
46
+ the application entry or either composition file.
47
+ 5. Normal application loading, first constant access, repeated requires, and
48
+ eager loading retain constant identity and exactly one lifecycle extension.
49
+ Explicit namespace loading also handles the internal unbound factory
50
+ contract having been loaded first. Arbitrary partial loading of framework
51
+ implementation files remains outside the public application-loading API.
52
+
53
+ ## Compatibility and guarantees
54
+
55
+ The public constructor and one-shot API, event callback paths, stored records,
56
+ transaction boundaries, Agent ownership, and shutdown behavior are unchanged.
57
+ The existing API snapshot is not regenerated. It does not cover the `run_once`
58
+ singleton signature, which is checked explicitly along with real invocation,
59
+ creation-time context/Knowledge, and both forms of event listener.
60
+
61
+ The factory's exception propagates unchanged through the existing Agent
62
+ creation/ownership handling. Classified failures such as ConfigurationError
63
+ release the reservation; unclassified failures such as IOError retain the
64
+ existing fail-closed recovery-required reservation. Do not make every factory
65
+ failure retryable as a side effect of moving default selection. Existing
66
+ explicit Persistence injection works without invoking the factory.
67
+
68
+ This is source dependency inversion, not removal of the runtime construction
69
+ call: `Base` still invokes the bound factory when a default is needed. The
70
+ one-shot implementation still depends explicitly on Agent and Persistence.
71
+ Both selecting and invoking the one-shot composition remain above execution.
72
+ The private factory slot is immutable after boot; returned Persistence
73
+ instances remain separate, mutable storage instances.
74
+
75
+ No F1 uncertain-commit reconciliation, F4 recovery guarantee, external-effect
76
+ rollback, or exactly-once guarantee is added. The previous in-memory durability
77
+ limits remain. Runtime retains created Agents according to existing ownership
78
+ rules; moving `run_once` does not introduce new cleanup or lifetime behavior.
79
+
80
+ ## Dependency interpretation
81
+
82
+ Remove `agent -> persistence/api` and `agent/api -> persistence/api`.
83
+ Add explicit composition dependencies from `runtime_composition` to Agent and
84
+ Persistence and from `agent/composition` to Persistence. One-shot composition
85
+ also calls the injected Agent definition's `create` method; namespace declarations
86
+ and injected calls do not count as static constant-reference edges. The
87
+ `Tools::Agent` call to `Agent.run_once` now targets its actual definition in
88
+ `agent/composition`. Treat the new Agent composition directory as B1; it is
89
+ not a new Ruby namespace.
90
+ The internal Base-to-factory reference remains within Agent's directory.
91
+
92
+ The remaining reverse dependency `agent -> workflow/execution` is the separate
93
+ worker-input classification change (E). This change does not resolve all
94
+ directory/file cycles, Storage's domain-specific repository names, or the
95
+ Engine FSM terminal persistence responsibilities.
96
+
97
+ ## Verification
98
+
99
+ Cover fallback precedence, fresh instances and subclass construction, resets,
100
+ classified failure/retry and unclassified fail-closed behavior, supplied-factory
101
+ independence, normal/preloaded/eager
102
+ loading, one-shot isolation from global Persistence, argument/result/exception
103
+ identity, context/Knowledge forwarding, callback conflict ordering, and both
104
+ event listener forms. Run existing Agent ownership/event tests, default and
105
+ integration suites, style, API snapshot, RBS, and annotation gates. Verify
106
+ examples against the changed core without modifying their public call sites.