phronomy 0.26.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 (304) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +4 -0
  4. data/VERIFY.sh +27 -27
  5. data/benchmark/bench_agent_invoke.rb +26 -22
  6. data/benchmark/bench_context_assembler.rb +4 -5
  7. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  8. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  9. data/docs/architecture/agent-context.md +4 -2
  10. data/docs/architecture/agent-transition-ownership.md +89 -0
  11. data/docs/architecture/before-llm-input.md +6 -0
  12. data/docs/architecture/context-management.md +30 -0
  13. data/docs/architecture/context-preparation-steps.md +90 -0
  14. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  15. data/docs/architecture/execution-metadata-and-values.md +80 -0
  16. data/docs/architecture/generator-verifier-ownership.md +111 -0
  17. data/docs/architecture/multi-agent-handoff.md +8 -2
  18. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  19. data/docs/architecture/persistence.md +27 -4
  20. data/docs/architecture/refactoring-closure.md +334 -0
  21. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  22. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  23. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  24. data/docs/architecture/tracing.md +1 -1
  25. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  26. data/docs/architecture.md +209 -0
  27. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  28. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  29. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  30. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  31. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  32. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  33. data/docs/decisions/032-storage-backend-composition.md +80 -0
  34. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  35. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  36. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  37. data/docs/decisions/036-context-contract-ownership.md +86 -0
  38. data/docs/decisions/037-common-definition-ownership.md +62 -0
  39. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  40. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  41. data/docs/decisions/040-configuration-default-composition.md +94 -0
  42. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  43. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  44. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  45. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  46. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  47. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  48. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  49. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  50. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  51. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  52. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  53. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  54. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  55. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  56. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  57. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  58. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  59. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  60. data/docs/decisions/README.md +35 -6
  61. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  62. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  63. data/docs/features.md +9 -3
  64. data/docs/getting-started.md +4 -4
  65. data/docs/migrations/0.15.md +5 -0
  66. data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
  67. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  68. data/docs/migrations/neutral-storage-spi.md +43 -0
  69. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  70. data/docs/migrations/shared-state-multi-agent.md +44 -0
  71. data/docs/migrations/storage-backend-composition.md +117 -0
  72. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  73. data/docs/persistence-backends.md +162 -204
  74. data/docs/runtime-and-concurrency.md +117 -32
  75. data/lib/phronomy/agent/api/agent.rb +17 -0
  76. data/lib/phronomy/agent/async_event_api.rb +2 -2
  77. data/lib/phronomy/agent/base.rb +51 -241
  78. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  79. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  80. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  81. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  82. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  83. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  84. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  85. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  86. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  87. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  88. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  89. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  90. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  91. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  92. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  93. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  94. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  95. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
  96. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  97. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  98. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
  99. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  100. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  101. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  102. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  103. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  104. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  105. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  106. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  107. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  108. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
  109. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  110. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  111. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  112. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  113. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  114. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  115. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  116. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  117. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  118. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  119. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  120. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  121. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  122. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  123. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  124. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  125. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  126. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  127. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  128. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  129. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  130. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  131. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  132. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  133. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  134. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  135. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  136. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
  137. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  138. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
  139. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  140. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  141. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  142. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  143. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  144. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  145. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  146. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  147. data/lib/phronomy/common/configuration_error.rb +7 -0
  148. data/lib/phronomy/common/error.rb +5 -0
  149. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  150. data/lib/phronomy/common/values/serializable.rb +32 -0
  151. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  152. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  153. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  154. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  155. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  156. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
  157. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  158. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  159. data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
  160. data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
  161. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  162. data/lib/phronomy/engine/event_loop.rb +158 -624
  163. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  164. data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
  165. data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
  166. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  167. data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
  168. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  169. data/lib/phronomy/engine/fsm_session.rb +35 -28
  170. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
  171. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
  172. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
  173. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  174. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  175. data/lib/phronomy/engine/runtime.rb +80 -84
  176. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  177. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  178. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  179. data/lib/phronomy/engine/task_result.rb +4 -0
  180. data/lib/phronomy/engine/timeout_error.rb +7 -0
  181. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  182. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  183. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  184. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  185. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  186. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  187. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  188. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  189. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  190. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  191. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  192. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  193. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  194. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  195. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
  196. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  197. data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
  198. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  199. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  200. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  201. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  202. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  203. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  204. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  205. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  206. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  207. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  208. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  209. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  210. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  211. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  212. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  213. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  214. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  215. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  216. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  217. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  218. data/lib/phronomy/storage/backend.rb +101 -0
  219. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  220. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  221. data/lib/phronomy/storage/blobs.rb +31 -0
  222. data/lib/phronomy/storage/condition.rb +25 -0
  223. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  224. data/lib/phronomy/storage/conflict_error.rb +9 -0
  225. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  226. data/lib/phronomy/storage/entry.rb +33 -0
  227. data/lib/phronomy/storage/guard_ref.rb +13 -0
  228. data/lib/phronomy/storage/not_found_error.rb +9 -0
  229. data/lib/phronomy/storage/record_codec.rb +177 -0
  230. data/lib/phronomy/storage/records.rb +61 -0
  231. data/lib/phronomy/storage/resource.rb +126 -0
  232. data/lib/phronomy/storage/scope.rb +25 -0
  233. data/lib/phronomy/storage/serialization_error.rb +9 -0
  234. data/lib/phronomy/storage/streams.rb +44 -0
  235. data/lib/phronomy/storage/transaction_error.rb +10 -0
  236. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  237. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  238. data/lib/phronomy/storage/validation.rb +53 -0
  239. data/lib/phronomy/storage/view.rb +131 -0
  240. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  241. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  242. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  243. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  244. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  245. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  246. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  247. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  248. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  249. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  250. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  251. data/lib/phronomy/tools/agent.rb +1 -1
  252. data/lib/phronomy/version.rb +1 -1
  253. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
  254. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  255. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  256. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  257. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
  258. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  259. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  260. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  261. data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
  262. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  263. data/lib/phronomy.rb +52 -94
  264. data/scripts/api_snapshot.rb +1 -1
  265. data/scripts/storage_spi_snapshot.rb +36 -0
  266. data/sig/phronomy/agent.rbs +0 -1
  267. data/sig/phronomy/execution_receiver.rbs +34 -0
  268. data/sig/phronomy/handoff.rbs +4 -2
  269. data/sig/phronomy/multi_agent.rbs +18 -0
  270. data/sig/phronomy/persistence.rbs +7 -91
  271. data/sig/phronomy/storage.rbs +174 -0
  272. data/sig/phronomy/tool.rbs +10 -1
  273. metadata +230 -99
  274. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  275. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  276. data/lib/phronomy/agent/recovery_support.rb +0 -504
  277. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  278. data/lib/phronomy/agent/tool_executor.rb +0 -55
  279. data/lib/phronomy/generator_verifier.rb +0 -369
  280. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  281. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  282. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  283. data/lib/phronomy/persistence/in_memory.rb +0 -690
  284. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  285. data/lib/phronomy/persistence.rb +0 -276
  286. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  287. data/lib/phronomy/workflow_recovery.rb +0 -123
  288. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  289. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  290. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  291. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  292. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  293. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  294. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  295. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  296. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  297. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  298. /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
  299. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  300. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  301. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  302. /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
  303. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  304. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -0,0 +1,111 @@
1
+ # ADR-049: Initial Preparation Worker Ownership
2
+
3
+ **Status**: Accepted on the architecture refactoring branch
4
+ **Date**: 2026-09-22
5
+ **Refines**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md),
6
+ [042-feature-owned-execution-state](042-feature-owned-execution-state.md),
7
+ [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md)
8
+ and [048-dispatch-preparation-worker-ownership](048-dispatch-preparation-worker-ownership.md).
9
+
10
+ ## Problem
11
+
12
+ ExecutionCoordinator still owned initial durable admission, application filtering,
13
+ Context preparation, failure persistence and replay of saved preparation inputs.
14
+ Its ordinary start and preparing-recovery paths shared persistence work with
15
+ EventLoop admission, live state and completion delivery. Simply relocating that
16
+ work would leave long methods mixing representation details and control steps.
17
+
18
+ ## Decision
19
+
20
+ ### Preparation owns persistence; the execution owner owns live control
21
+
22
+ `Agent::InitialPreparation` owns `prepare(Command)` and `recover(RecoveryCommand)`.
23
+ Its Command and Result retain the fields of the former Coordinator-owned types.
24
+ Coordinator keeps their old constant paths as aliases. Their canonical Ruby names
25
+ change; these are internal process-local types, not saved or public API records.
26
+
27
+ Coordinator retains replay-eligibility capture, Runtime admission, live-owner
28
+ checks, Offload submission, completion messages, stale-result validation, live
29
+ Agent/Registry updates, session registration and Task/listener settlement.
30
+ Preparing recovery captures the execution, root and journal records in an explicit
31
+ RecoveryCommand. Result tasks and load completion handles stay on EventLoop.
32
+ The worker does not call back into Coordinator or initiate Provider/Tool dispatch.
33
+
34
+ The worker is marked `WorkerInputRestricted` and holds existing Agent services and
35
+ Persistence. It keeps operation state in local variables. This does not remove all
36
+ Agent service references or make arbitrary application input/config values deeply
37
+ immutable. Approval snapshot isolation remains a later stage.
38
+
39
+ ### Preserve admission outcomes and operation-specific failure boundaries
40
+
41
+ Input extraction precedes durable admission. An extraction failure returns
42
+ `not_established`. During admission, durable busy returns `recovery_required`;
43
+ the existing known-conflict/validation exception set returns `not_established`;
44
+ other exceptions return `outcome_unknown`. The execution owner preserves the
45
+ corresponding Runtime admission release or fail-closed behavior.
46
+
47
+ Durable admission still creates Execution and advances AgentRoot together, after
48
+ validating Team/Subagent/Handoff reservation ownership in the same transaction.
49
+ Splitting those ownership checks into named helpers changes no acceptance rule.
50
+
51
+ For admitted preparation the body reads as filter, stage input, prepare Context,
52
+ commit, and materialize. Hooks and ContextPolicy run outside the commit
53
+ transaction. Cancellation is checked before filtering, before assembly and after
54
+ Policy. The commit revalidates the Agent watermark after Policy returns.
55
+
56
+ The failure base advances from preparing Execution to active Execution only after
57
+ the active commit has returned successfully. A lost active-commit response leaves
58
+ the old revision as the failure base. If the active commit did occur, the existing
59
+ failure transaction conflicts and rolls back its Journal append; the worker does
60
+ not return an unconfirmed terminal result or add a new retry/readback policy.
61
+
62
+ A post-commit materialization error is terminalized from the known active
63
+ revision. Failure persistence atomically appends audit records, saves terminal
64
+ Execution and advances AgentRoot. A failure in that transaction, including lost
65
+ response, escapes to the execution owner; it is not converted into a confirmed
66
+ terminal Result. This initial-preparation contract intentionally differs from
67
+ DispatchPreparation's operation-specific uncertain-outcome reconciliation.
68
+
69
+ `Agent::ExecutionFailure` owns the existing pure error-to-status and status-to-audit
70
+ kind mappings. Both initial failure persistence and the remaining terminal worker
71
+ use it, avoiding duplicated classification rules. It owns no transaction, live
72
+ state or result delivery. Normal/Handoff terminal transaction behavior is unchanged.
73
+
74
+ ### Recovery consumes saved inputs without admitting another execution
75
+
76
+ The worker first checks the replayable marker, reads current input and rebuilds
77
+ config from the saved invocation mode, coordination services and optional canonical
78
+ Hash durable_context. The recovered config and durable context remain frozen.
79
+ Read failures or invalid durable context escape before admitted preparation;
80
+ they do not trigger a failure commit or a speculative retry.
81
+
82
+ Ordinary start and recovery use the same admitted-preparation operation and Result.
83
+ Recovery keeps the same execution identity and does not call durable admission.
84
+ Runtime-only approval/listener state is not reconstructed. Existing Coordinator
85
+ validation and recovery delivery remain authoritative.
86
+
87
+ ## Guarantees and verification
88
+
89
+ Using [018-durability-guarantees-and-failure-model](018-durability-guarantees-and-failure-model.md):
90
+
91
+ | Subject / property | Provider | Failure / boundary | Result |
92
+ | --- | --- | --- | --- |
93
+ | Same-process Agent starts: fail closed for uncertain/busy admission | Existing Runtime registry plus worker outcome classification | F0/F1/F4; before framework Provider/Tool X0 | YES, unchanged |
94
+ | Initial preparation: stale root/revision rejection | Persistence watermark/CAS and failure transaction rollback | F1/F2/F3; before framework Provider/Tool X0 | CONDITIONAL on conforming Persistence |
95
+ | Failure transition: Journal, Execution and Root commit together | Existing Persistence transaction | F0/F1; atomicity does not imply response certainty | CONDITIONAL on conforming backend; no new certainty guarantee |
96
+ | Preparing recovery: continue the same execution | Existing replay eligibility, saved inputs and owner validation | F4; application preparation may run again | CONDITIONAL on the existing replay-safe preparation contract |
97
+ | Arbitrary application or external effects: exactly once | No new exclusion/idempotency protocol | F1/F4 across application-defined X0 | NO new guarantee |
98
+
99
+ Behavioral tests cover transaction ordering, pre-admission extraction failure,
100
+ known admission failure, lost admission/active/failure commit responses,
101
+ post-commit materialization failure, cancellation and watermark changes after
102
+ Policy, blocked input and replay input restoration/validation/read failure.
103
+ Architecture guards include all new worker helpers and exclude control/delivery
104
+ dependencies. Existing normal execution, Team/Subagent/Handoff and recovery suites
105
+ cover the owner wiring and unchanged coordination checks.
106
+
107
+ ## Remaining work
108
+
109
+ Approval snapshot isolation and approval persistence, followed by normal/Handoff
110
+ outcome worker extraction, remain separate stages. This stage does not finish
111
+ Coordinator decomposition or remove all Agent dependency cycles.
@@ -0,0 +1,98 @@
1
+ # ADR-050: Approval Resume Snapshot and Commit Ownership
2
+
3
+ **Status**: Accepted on the architecture refactoring branch
4
+ **Date**: 2026-09-22
5
+ **Refines**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md),
6
+ [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md)
7
+ and [049-initial-preparation-worker-ownership](049-initial-preparation-worker-ownership.md).
8
+
9
+ ## Problem
10
+
11
+ Approval resume captured a Tool batch into a Coordinator Hash keyed by execution
12
+ ID before validating the current approval request. A worker later removed that
13
+ entry under a Mutex. Two queued requests could overwrite or consume each other's
14
+ snapshot even though the worker commands belonged to distinct operations.
15
+ The outer frozen Array also shared mutable String values with the invocation.
16
+ The command did not completely describe the recovery facts it would persist.
17
+
18
+ ## Decision
19
+
20
+ ### Validate and capture on EventLoop
21
+
22
+ ExecutionCoordinator validates the live Agent owner, suspended Execution and
23
+ approval request ID before capturing the invocation's canonical Tool snapshot.
24
+ It copies and recursively freezes that value tree with `Values::Immutable.copy`
25
+ and stores it directly in the operation's `tool_batch_snapshot` field. An absent
26
+ invocation gives nil; a present invocation with no children gives a frozen empty
27
+ Array. No shared approval snapshot Hash or Mutex remains in either owner.
28
+
29
+ This applies to canonical recovery facts only. Arbitrary application objects in
30
+ config, listener or callback state do not acquire a new deep-copy contract.
31
+ The general RecoverySupport snapshot builder keeps its existing contract.
32
+
33
+ Snapshot isolation was implemented and behaviorally verified before extracting
34
+ the persistence operation, so the concurrency fix can be reviewed independently
35
+ of the ownership change.
36
+
37
+ ### Persist the captured decision in an operation worker
38
+
39
+ `Agent::ApprovalResumeCommit#commit` reads as validate the approval target, stage
40
+ the captured recovery facts, then persist the decision. Its helpers own record
41
+ fields, metadata representation and repository calls. It holds only the Agent ID
42
+ and Persistence; all operation state is local. It is `WorkerInputRestricted`.
43
+
44
+ The existing transaction still writes approval decision Content, adds the
45
+ approval_decided record to Execution working records, saves active/resuming
46
+ Execution with its expected revision and advances AgentRoot with its expected
47
+ revision. Staging metadata preserves the original execution revision so the
48
+ durable transition advances it exactly once. A nil snapshot preserves existing
49
+ metadata; an empty snapshot replaces the previous Tool batch.
50
+
51
+ Coordinator retains Runtime admission, Offload submission, completion messages,
52
+ late-result validation, live-state replacement, completion waiters, tracing and
53
+ SessionRunner resumption. Rejection before Offload submission restores suspended
54
+ admission. A commit error keeps admission recovery_required. A late result for
55
+ an advanced or cancelled Execution fails only the approval observer and does not
56
+ start a session. Existing duplicate-approval acceptance and CAS rules are unchanged.
57
+
58
+ The worker does not read back uncertain results, retry a transaction, contact
59
+ Provider/Tool adapters or invoke application callbacks. A lost commit response
60
+ escapes as an error; the owner follows the existing recovery-required path.
61
+ Persistence atomicity must not be presented as certainty about that response.
62
+
63
+ ### Internal type compatibility
64
+
65
+ Command and Result are owned by ApprovalResumeCommit. Coordinator retains
66
+ `ResumeCommitCommand` and `ResumeCommitResult` as aliases to those exact classes.
67
+ Their canonical Ruby names change to `ApprovalResumeCommit::Command` and
68
+ `ApprovalResumeCommit::Result`. Command appends the required internal field
69
+ `tool_batch_snapshot`; Result retains its existing members. Internal positional
70
+ construction/reflection is consequently not unchanged. These types are neither
71
+ public API nor durable records. Saved schema and public approval API stay unchanged.
72
+
73
+ ## Guarantees and verification
74
+
75
+ Using [ADR-018](018-durability-guarantees-and-failure-model.md):
76
+
77
+ | Subject / property | Provider | Failure / boundary | Result |
78
+ | --- | --- | --- | --- |
79
+ | Queued approval operation retains its captured facts | EventLoop validation and independent immutable Command values | F0/F2/F3; before Provider/Tool X0 | YES for canonical Tool snapshot values |
80
+ | Approval transition is atomic (G5), stale writes are rejected (G8) | Existing Persistence transaction and Execution/Root CAS | F2; no framework X0 in this worker | CONDITIONAL on conforming backend |
81
+ | Unknown approval commit is not treated as confirmed resumption | Worker error propagation and owner recovery_required admission | F1; no new worker X0 | YES; no new commit certainty or readback guarantee |
82
+ | Restored approval waiting continues the same execution (G4) | Existing recovery reconstruction and validated resume path | F4; later Tool work may cross X0 | CONDITIONAL on existing recovery/integration contracts |
83
+ | Cross-process exclusion (G7), external duplicate prevention (G9), exactly once (G10) | No new protocol supplied by this change | F1/F2/F4 across X0 | NO new guarantee |
84
+
85
+ Tests cover mutation after capture, reversed worker order, stale and invalid
86
+ requests, rejected submission, empty/absent batches, successive approvals,
87
+ approval/denial persistence, Execution and Root conflicts, transaction response
88
+ loss and stale results after cancellation or a newer approval. Existing recovery
89
+ integration tests restore approval waiting and resume both approval and denial.
90
+ The unit fault injections are not a hard-process-loss or real external-service test.
91
+ Architecture guards inspect the worker helpers for live-control dependencies.
92
+
93
+ ## Remaining work
94
+
95
+ Normal terminal persistence and Handoff terminal persistence remain coupled in
96
+ ExecutionCoordinator and its subclass. They are the next extraction stage and
97
+ must be designed together. This change does not complete Coordinator decomposition
98
+ or eliminate Agent dependency cycles.
@@ -0,0 +1,151 @@
1
+ # ADR-051: Execution Outcome Worker Ownership
2
+
3
+ **Status**: Accepted on the architecture refactoring branch
4
+ **Date**: 2026-09-22
5
+ **Refines**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md),
6
+ [030-agent-handoff-domain-and-durable-responsibility](030-agent-handoff-domain-and-durable-responsibility.md),
7
+ [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md)
8
+ and [050-approval-resume-snapshot-and-commit-ownership](050-approval-resume-snapshot-and-commit-ownership.md).
9
+
10
+ ## Problem
11
+
12
+ ExecutionCoordinator owned both EventLoop execution control and Offload result
13
+ persistence. HandoffExecutionCoordinator overrode outcome selection and depended
14
+ on its parent's transaction helpers. Extracting only one side would preserve
15
+ that hidden dependency or accidentally change Handoff precedence. Moving the
16
+ long bodies unchanged would also leave persistence policy mixed with individual
17
+ record fields.
18
+
19
+ ## Decision
20
+
21
+ ### Select and persist outcomes in operation workers
22
+
23
+ `Agent::ExecutionOutcomeCommitter#commit_outcome` selects completion, failure,
24
+ approval suspension or owned-child waiting from a captured command. It handles
25
+ persistence exceptions through the existing operation-specific readback rules.
26
+ `Agent::HandoffOutcomeCommitter` specializes that selection and atomic Source
27
+ transfer, sharing ordinary completion, failure and suspension persistence.
28
+ Handoff selection retains its separate callback/error/suspension/transfer order;
29
+ it does not acquire the ordinary worker's child-coordination wait branch.
30
+
31
+ HandoffExecutionCoordinator retains its class identity and inheritance, but now
32
+ only selects the Handoff worker. This selection is internal, not a public SPI.
33
+ Coordinator captures input, submits the worker, validates the returned revision
34
+ and session, applies live state and delivers results. Worker code never calls
35
+ back into Coordinator. Command state and intermediate results remain local to
36
+ one operation; workers hold only Agent service context and Persistence.
37
+
38
+ Agent context is still required by the existing coordination hook, error
39
+ translation, saved Manifest reader and transcript materializer. This is not a
40
+ claim that all Agent references or dependency cycles have been removed.
41
+ `WorkerInputRestricted` marks both worker types; it is not a recursive validator
42
+ of arbitrary application-owned values.
43
+
44
+ ### Keep transactions visible in the purpose-level methods
45
+
46
+ `commit_completed` encodes completion records, appends Journal, saves Execution
47
+ and coordination, advances Root and materializes the caller-facing transcript
48
+ inside one transaction, then constructs the result. Record fields and repository
49
+ arguments belong to its helpers. Transcript materialization stays inside the
50
+ transaction so failure rolls back the transition.
51
+
52
+ `commit_failed_outcome` persists audit-only records and the translated failure.
53
+ The Context revision stays unchanged because none of those records is a Context
54
+ candidate. Completion and Handoff advance Context only if the appended records
55
+ contain a candidate. Suspension retains its records in Execution working state,
56
+ advances Root to suspended and does not append Journal or advance Context.
57
+
58
+ `commit_handed_off` validates Source routing and cancellation, persists projected
59
+ Context, transfers routing to a deterministic Target execution ID, and saves
60
+ Source Journal, Execution and Root in one transaction. Target definition lookup
61
+ uses the transaction view; the existing saved Manifest reader keeps its service
62
+ context. Target completion/failure stabilizes routing inside its own existing
63
+ terminal transaction. No Provider/Tool dispatch or application delivery moves
64
+ into these workers.
65
+
66
+ ### Preserve the distinct readback contracts
67
+
68
+ | Operation | Existing confirmation rule after an exception | Consequence |
69
+ | --- | --- | --- |
70
+ | Ordinary/Handoff terminal | Same Agent, terminal Execution, expected revision + 1; then load saved Root, Journal and result | Reuse the persisted outcome; do not write a second terminal transition |
71
+ | Child-coordination wait | Complete saved Execution payload equals the intended waiting record | Return that nonterminal wait; active status alone does not establish a match |
72
+ | Approval suspension | It is nonterminal, so terminal readback does not confirm it | Propagate uncertain completion to the owner |
73
+
74
+ Terminal confirmation is deliberately not changed to DispatchPreparation's
75
+ full intended-payload comparison. Missing, active, mismatched or unreadable
76
+ results do not authorize another write. Readback errors retain their existing
77
+ propagation behavior. Recovered success retains the existing smaller result
78
+ shape; recovered failures use `RecoverySupport.error_from_failure`, including
79
+ its generic Phronomy::Error representation. This extraction does not normalize
80
+ those results or introduce a stronger commit-certainty protocol.
81
+
82
+ ### Preserve EventLoop result authority and delivery
83
+
84
+ The owner still waits for physical work quiescence before terminal persistence.
85
+ A stale result changes no live state, admission or Task. Completed/failed/Handoff
86
+ results release ownership and deliver their existing events before settling
87
+ waiters. Suspension retains admission and leaves the original Task pending;
88
+ exact observers receive the existing approval-required failure. Child waiting
89
+ releases ownership and fails waiters with recovery required.
90
+
91
+ A worker error for ordinary execution retains recovery_required admission and
92
+ pending waiters. The existing coordination-metadata error path releases the
93
+ owner and fails waiters. Those branches are not merged into unconditional cleanup.
94
+ Live Handoff request/Manifest delivery handles remain outside the worker command.
95
+
96
+ ### Internal types
97
+
98
+ ExecutionOutcomeCommitter owns `Command`, `Outcome`, `TerminalView` and
99
+ `HandoffTerminalView`. Coordinator retains its previous constant paths as aliases
100
+ to the same classes. All four member lists stay unchanged; their canonical Ruby
101
+ names change. TerminalDelivery and TerminalCommitReady remain owner-local.
102
+ These types are internal and are not serialized by class name. Public API, saved
103
+ schemas, Agent/Handoff Coordinator identity and caller selection remain unchanged.
104
+
105
+ ## Guarantees and verification
106
+
107
+ Using [ADR-018](018-durability-guarantees-and-failure-model.md):
108
+
109
+ | Subject / property | Provider | Failure / boundary | Result |
110
+ | --- | --- | --- | --- |
111
+ | Terminal or Handoff transition atomicity (G5), stale write rejection (G8) | Existing transaction and repository CAS | F0/F2; no new framework X0 | CONDITIONAL on conforming Persistence backend |
112
+ | No second terminal write after uncertain response | Operation-specific worker readback and owner recovery path | F1; no new worker X0 | YES; no stronger outcome-certainty guarantee |
113
+ | Current execution alone may apply worker results | Existing EventLoop owner/revision/session guards | F2/F3 and delayed completion | YES within the same Runtime ownership contract |
114
+ | Logical continuation from saved Source/Target state (G4) | Existing Handoff recovery and exact Target identity | F4; later execution can cross X0 | CONDITIONAL on existing recovery contracts |
115
+ | Cross-process exclusion (G7), external duplicate prevention (G9), exactly once (G10) | No new protocol added | F1/F2/F4 across X0 | NO new guarantee |
116
+
117
+ Behavior tests exercise completion/rejection, failure categories, suspension,
118
+ transaction rollback, Root CAS, response loss and readback mismatches/failures,
119
+ child waiting, atomic Source transfer, routing conflict/cancellation and Target
120
+ completion/failure. Existing suites cover physical quiescence, stale results,
121
+ callback policy, approval recovery and durable Handoff recovery. Unit fault
122
+ injections do not represent real process loss or live external service failures.
123
+ Architecture guards now inspect the actual workers and disallow repository
124
+ reloads in Coordinator without the old terminal-helper exceptions.
125
+
126
+ ## Owner-control review (2026-09-22)
127
+
128
+ The fifth stage keeps one execution owner and makes its remaining result paths
129
+ read as validation, state application and continuation/delivery. Preparation
130
+ recovery separates failed-outcome settlement from session restart. Approval
131
+ resume separates committed-state installation and tracing from FSM entry.
132
+ Terminal results retain visible outcome selection while private methods handle
133
+ the distinct waiting, suspension, completion, Handoff and failure deliveries.
134
+ Only the identical execution/admission release is shared across terminal paths.
135
+ Initial preparation snapshot construction is named explicitly; start/resume
136
+ admission flags, submission flags and their rescue decisions remain together.
137
+
138
+ No transaction, authority condition, failure policy, public type or class owner
139
+ changes. Direct Ready delivery tests run on the real EventLoop/ExecutionRegistry
140
+ and check stale-result isolation, state-before-notification, notification-before-
141
+ settlement, fallback waiters, suspension, uncertainty and recovery/resume failure
142
+ boundaries. These tests also pass against the preceding implementation, documenting
143
+ preserved behavior rather than a new contract. F0/F1/F3 are injected locally;
144
+ no new X0 operation or stronger F4 guarantee is claimed.
145
+
146
+ ## Remaining work
147
+
148
+ The execution-owner decomposition and final readability review are implemented.
149
+ Tool restoration, SharedState, Storage domain responsibilities and Workflow
150
+ terminal ownership remain separate work items. Further owner splitting needs a
151
+ concrete responsibility or failure-boundary reason; file length alone is not one.
@@ -0,0 +1,92 @@
1
+ # ADR-052: Tool Invocation Restoration Ownership
2
+
3
+ **Status**: Accepted on the architecture refactoring branch
4
+ **Date**: 2026-09-22
5
+ **Refines**: [046-agent-responsibility-layout-and-shared-records](046-agent-responsibility-layout-and-shared-records.md)
6
+ and [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md).
7
+
8
+ ## Problem
9
+
10
+ InvocationRestorer decoded saved Tool batches and directly assigned
11
+ ToolInvocation's status, result, authorization decision and approval evidence.
12
+ Moving restoration into its own module in ADR-046 left that knowledge of another
13
+ object's representation intact. ADR-047 explicitly retained it as separate work.
14
+ A change to ToolInvocation's state representation therefore required Recovery to
15
+ track its instance variables as well as the saved format.
16
+
17
+ ## Decision
18
+
19
+ Keep the existing classes and introduce the internal
20
+ `ToolInvocation#restore_state!(status:, result: nil, approval_item: nil)` operation.
21
+ It is Ruby-public for the Recovery caller and classified `@api private`; it is
22
+ not an Application extension SPI. Its input is materialized semantic state,
23
+ not an Execution record or a persistence repository.
24
+
25
+ InvocationRestorer retains saved snapshot key normalization, Tool Call and Tool
26
+ lookup, construction of ordinary or missing-Tool invocations, and matching the
27
+ approval item by invocation ID. It converts the saved status to a Symbol and
28
+ passes the saved result and matched approval item to the newly constructed
29
+ ToolInvocation. No ToolInvocation instance variable is assigned by Recovery.
30
+ The separate AgentInvocation batch-ID assignment is outside this change.
31
+
32
+ ToolInvocation owns the supported-state dispatch and approval evidence copy:
33
+
34
+ | Saved state | Existing behavior retained |
35
+ | --- | --- |
36
+ | awaiting_approval | Validate arguments unless already terminal, restore require_approval and the waiting state |
37
+ | authorized | Validate arguments unless already terminal, restore allow and the authorized state |
38
+ | completed | Restore the saved result, including nil or false, and completed state |
39
+ | rejected | Restore rejection and its decision |
40
+ | failed | Restore the existing generic Tool preflight failure |
41
+ | cancelled | Restore cancellation |
42
+ | Other state | Raise ExecutionRehydrationRequiredError before applying approval evidence |
43
+
44
+ The entry method reads as saved-state application followed by saved approval
45
+ evidence application. Private methods contain each operation's implementation.
46
+ Evidence copying preserves the existing immutable facts and reason semantics,
47
+ including an explicit nil facts value. A missing approval item leaves constructor
48
+ defaults intact. Saved display evidence is not a new policy evaluation input.
49
+
50
+ The caller supplies a newly constructed invocation before session installation.
51
+ This is not a general rollback or arbitrary live-state replacement interface.
52
+ The existing EventLoop ownership and continuation validation remain unchanged.
53
+ The old internal `InvocationRestorer.restore_tool_snapshot!` entry is removed;
54
+ no forwarding compatibility wrapper is introduced for this private helper.
55
+
56
+ ## Compatibility and limits
57
+
58
+ No public API, RBS contract, saved schema, approval decision, external replay
59
+ eligibility, missing-Tool rule, or callback sequence changes. The existing
60
+ validation behavior is retained, including the handling of current Tool
61
+ validation errors and missing definitions; this refactor does not add a new
62
+ schema-migration or saved-state validation policy. It does not restore arbitrary
63
+ Application Tool instance variables.
64
+
65
+ Restoration itself does not run authorization policy, execute a Tool, acquire a
66
+ Runtime, perform persistence I/O or notify listeners. This does not suppress the
67
+ existing recovery installation notification of an outstanding approval request.
68
+ Load-time notification and later approval/rejection dispatch keep their existing
69
+ owners. External outcome-unknown resolution and framework operation recovery
70
+ remain with RecoveryCoordinator and the execution owner.
71
+
72
+ Under [018-durability-guarantees-and-failure-model](018-durability-guarantees-and-failure-model.md),
73
+ this change concerns operation-local reconstruction from already materialized
74
+ facts. Unsupported input is an F0 rejection without X0 dispatch by this operation.
75
+ Same logical execution resumption after F4 remains CONDITIONAL on the existing
76
+ confirmed saved state and operation-specific recovery contract. F1 outcome
77
+ resolution is unchanged. No new external-effect exactly-once guarantee follows.
78
+
79
+ ## Verification
80
+
81
+ Replace tests that mock another object's instance-variable writes with real
82
+ ToolInvocation behavior tests. Run identical assertions against the old owner
83
+ boundary and the new operation. Cover all six states, unsupported states, saved
84
+ results, missing definitions, immutable approval evidence, and dispatch gating
85
+ before and after approval. Existing restart tests exercise approval and rejection
86
+ through actual recovery installation and session continuation. Run the ordinary
87
+ and integration suites, API snapshot, annotations, RBS, examples and SQLite
88
+ persistence checks. No test count implies a stronger F4 or X0 guarantee.
89
+
90
+ SharedState coordination ownership, Storage domain responsibilities and Workflow
91
+ terminal persistence remain separate work. This decision completes only the Tool
92
+ restoration ownership item carried forward by ADR-047.
@@ -0,0 +1,71 @@
1
+ # ADR-053: SharedState Coordination Ownership
2
+
3
+ ## Status
4
+
5
+ Accepted on the architecture refactoring branch, 2026-09-22.
6
+ This resolves the deferred SharedState ownership decision in
7
+ [046-agent-responsibility-layout-and-shared-records](046-agent-responsibility-layout-and-shared-records.md).
8
+
9
+ ## Context
10
+
11
+ SharedState creates an invocation-local findings store, equips several Agent
12
+ definitions with read/write Tools, invokes members in declaration order, repeats
13
+ cycles until a configured limit, and aggregates the findings. It is not an
14
+ individual Agent's state container or execution coordinator. These responsibilities
15
+ belong to MultiAgent, alongside the other multi-Agent coordination patterns.
16
+
17
+ Its implementation also mixes cycle orchestration with individual member calls,
18
+ and researcher instrumentation with the bodies of injected Tool definitions.
19
+ Moving a file alone would leave those abstraction levels mixed.
20
+
21
+ ## Decision
22
+
23
+ 1. Move the implementation to `multi_agent/shared_state.rb`, exposing the
24
+ Experimental `Phronomy::MultiAgent::SharedState` and its nested `KnowledgeStore`.
25
+ Remove `Phronomy::Agent::SharedState` without an alias on this branch, following
26
+ the coordination ownership precedent in
27
+ [034-handoff-runner-coordination-ownership](034-handoff-runner-coordination-ownership.md).
28
+ Update examples, RBS and the [migration guide](../migrations/shared-state-multi-agent.md)
29
+ together. Ordinary consumers continue to `require "phronomy"`.
30
+ 2. Express invocation as termination validation, store creation, member
31
+ coordination and result aggregation. Express each cycle as ordered member
32
+ invocation followed by stopping decisions. Build the two injected Tool
33
+ definitions in named private methods. Keep these responsibilities in the same
34
+ class; no new execution manager or general coordination abstraction is needed.
35
+ 3. Preserve the DSL, method parameters, prompt text, Tool descriptions/schema,
36
+ cooperative execution mode, original Tool aliases, result shape and exceptions.
37
+ Members remain sequential; findings from an earlier member are visible to the
38
+ next member in the same cycle. `terminate_when` still takes precedence over
39
+ timeout after each complete cycle, and timeout does not interrupt a member.
40
+ 4. Preserve the generated Agent definition ID prefix
41
+ `Phronomy::Agent::SharedState::Instrumented/` and instrumentation version 1.
42
+ This string is semantic identity, not a Ruby constant lookup or old-name alias.
43
+ Namespace placement alone must not change the wrapped definition revision.
44
+
45
+ ## Consequences and limits
46
+
47
+ This is a breaking rename of an Experimental public API, including the nested
48
+ store class name. The Stable/Beta API snapshot does not enumerate SharedState;
49
+ an unchanged snapshot does not establish complete public API compatibility.
50
+
51
+ The store is still newly created for each invocation and is not durable shared
52
+ Agent state. The coordinator adds no resume, cancellation, asynchronous execution,
53
+ transaction, F4 or X0 guarantee. `invoke` still accepts `config:` without
54
+ forwarding it to the member calls. Revising that behavior is a separate change.
55
+
56
+ No persistence schema or saved-record migration is introduced. Individual Agent
57
+ persistence keeps its existing rules; this move does not make the in-memory
58
+ coordination lifetime resumable. The method extraction slightly increases source
59
+ length and does not eliminate unrelated dependency cycles.
60
+
61
+ Storage domain responsibilities and Workflow terminal ownership remain separate
62
+ work items. This decision does not reopen the completed ExecutionCoordinator split.
63
+
64
+ ## Verification obligations
65
+
66
+ Verify new namespace loading, absence of the old alias, repeated eager-load
67
+ identity and no Runtime startup during loading. Exercise existing coordination,
68
+ stopping, aggregation, Tool alias and definition identity tests. Check the full
69
+ and integration suites, offline examples, RBS, annotations, style and built gem
70
+ contents, including removal of the old implementation path. Record executed
71
+ results and untested environments separately in the delivery review.
@@ -0,0 +1,76 @@
1
+ # ADR-054: One Workflow Terminal Save Implementation
2
+
3
+ ## Status and scope
4
+
5
+ Accepted on the architecture refactoring branch, 2026-09-22.
6
+ Amends only the Workflow recovery prepend requirement in
7
+ [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md).
8
+ The durable barrier in
9
+ [026-workflow-runtime-admission-and-durable-terminal-barrier](026-workflow-runtime-admission-and-durable-terminal-barrier.md)
10
+ and the receiver ownership in
11
+ [042-feature-owned-execution-state](042-feature-owned-execution-state.md)
12
+ remain unchanged.
13
+
14
+ ## Problem
15
+
16
+ WorkflowRunner defines terminal save submission, but WorkflowRecovery defines
17
+ the same method and is prepended during application loading. It never calls
18
+ `super`. The active implementation reconciles uncertain save outcomes against
19
+ durable pre/post state; the shadowed implementation does not. Reading or editing
20
+ the Runner alone can therefore target code that is not actually used.
21
+
22
+ ## Decision
23
+
24
+ Move the currently active submission and F1 reconciliation into WorkflowRunner.
25
+ Delete the shadowed implementation, the private WorkflowRecovery module and its
26
+ explicit installation. Do not introduce a new class or compatibility alias for
27
+ the removed private override.
28
+
29
+ Express terminal save submission as admission marking, immutable command
30
+ construction, Offload submission and session-local result delivery. Named private
31
+ methods own saving/reconciliation and delivery separately. The existing Command
32
+ and Result Data classes retain their identity and members.
33
+
34
+ The worker saves once. Portable ConflictError, NotFoundError, SerializationError
35
+ and UnsupportedBackendError remain known failures without readback. Other save
36
+ errors use the existing authoritative snapshot comparison: exact post-state is
37
+ success, pre-state preserves the original failure, and conflict or failed
38
+ readback remains outcome-unknown. No automatic save retry is introduced.
39
+
40
+ The existing session sink receives `workflow_terminal_persistence_result`.
41
+ Offload completion errors still become outcome-unknown, and rejected sink
42
+ delivery retains its existing warning. Runner still marks admission and captures
43
+ the snapshot on EventLoop; workers only use that captured command for persistence.
44
+
45
+ ## Compatibility and guarantees
46
+
47
+ This is an internal ownership/readability change. Public Workflow/Persistence
48
+ APIs, Backend SPI, snapshots, revisions, error messages and the session event
49
+ protocol are unchanged. Removing the prepend can make WorkflowRunner load lazily
50
+ under ordinary `require "phronomy"`; first access and eager loading must retain
51
+ the same recovery behavior without starting Runtime.
52
+
53
+ F1 save certainty remains CONDITIONAL on authoritative readback matching the
54
+ existing expected pre-state or intended post-state rules. It is not established
55
+ merely by receiving an exception. F0 portable failures follow the existing known
56
+ failure path. Runtime release, success notification and Task settlement still
57
+ wait for EventLoop-owned session acceptance; uncertain outcomes keep the existing
58
+ recovery-required behavior. F4 readability depends on the backend retaining
59
+ confirmed data. X0 external effects are outside this save, and this change adds
60
+ no execution replay, distributed ownership or exactly-once guarantee.
61
+
62
+ FSMSession still owns interpretation of the Workflow terminal persistence
63
+ event. Moving that policy out of Engine is a separate next step; this decision
64
+ does not claim that the entire Workflow terminal ownership issue is resolved.
65
+ Storage's fixed repository slots and Agent watermark also remain separate work.
66
+
67
+ ## Verification
68
+
69
+ Characterize the effective submission path before and after the change, including
70
+ immutable snapshots, known errors, F1 post/pre/conflicting/unreadable outcomes,
71
+ single-save behavior and failed Offload completion delivery. Preserve existing
72
+ real-runtime tests for delayed save, stream barriers, admission release and
73
+ uncertain outcomes. Verify ordinary/eager loading without the override, full and
74
+ integration suites, API/RBS/annotations/style, offline examples and built gem
75
+ contents. Keep results from tests actually run distinct from unavailable live
76
+ Provider or database-server evidence.