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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 636ce578204172927316a89f0f65a2bdfda49140822da98cd1eec9230d821105
4
- data.tar.gz: 8d776e0ab9bf2775af4e23f77b0b29968a3d4962f8fbd877d3fe9574a3efb306
3
+ metadata.gz: '095ac1376e77f34201dceeafec7dc114340aa2cae7cd8f1bb2bb7c2552e64e87'
4
+ data.tar.gz: 4d900c3bdc20e3fb0210046daffc4d78d1b990f118281c04037cc45beca2d699
5
5
  SHA512:
6
- metadata.gz: 81a2b295d5fb03d4cd03c6f3ceabca7b62f4e635fc5dd6ed3db31b1c5f59c6031fc38958895a1fc7b8e9fc75394a81181d51529ebb86f310e85cd2300a9395af
7
- data.tar.gz: f701f8fd0be60d655e9676c8cbdfec93606cca20769da90b7c67617c9d876cbde51bf18a42531b7cf70079aa12863af022acd21500fe40cff64c04d5a70df6ab
6
+ metadata.gz: 3108c48424885f72ea256e6c7cd7dfa8a7230647727f288f9fce840ee9309a51b523e64748663a7d1341e88b0c05cfe5432cb91a521039b16f28b0e2c14a4714
7
+ data.tar.gz: 51795e8c019cb92d8a17eadb864b726f9359ed1c963b06acfb52cb7440b99234755ad2c7e169a5afb8e5958ea45b99635d9035eea4d3c4ceb84ac4d554fdc814
data/CHANGELOG.md CHANGED
@@ -14,6 +14,77 @@ Release history for 0.14.0 and earlier is archived in
14
14
 
15
15
  ---
16
16
 
17
+ ## [0.27.0] - 2026-09-24
18
+
19
+ ### Changed
20
+
21
+ - Separate application Runtime reset and configuration replacement into
22
+ `runtime_composition/global_runtime.rb`. Configuration access no longer owns
23
+ Runtime lifecycle control; the reset API, shutdown ordering, timeout behavior,
24
+ and configuration defaults are unchanged. See
25
+ [ADR-039](docs/decisions/039-runtime-configuration-lifecycle-ownership.md).
26
+ - Group root implementations and entry-point exceptions by responsibility:
27
+ common definitions, Engine/FSM execution contracts, Recovery, Workflow,
28
+ Agent lifecycle contracts, LLM contracts, configuration, and feature APIs.
29
+ Preserve public constant names, API signatures, stored formats, and lifecycle
30
+ loading. Reserve the direct source root for version and namespace/loading
31
+ files; share the three equivalent immutable-copy helpers. See
32
+ [ADR-038](docs/decisions/038-responsibility-based-source-layout.md).
33
+ - Move the shared `Phronomy::Error` base exception to `common/error.rb` and
34
+ define ownership rules for general common definitions. The public constant,
35
+ subclass hierarchies, and rescue behavior are preserved. See
36
+ [ADR-037](docs/decisions/037-common-definition-ownership.md).
37
+ - Group Context Policy and hook contracts, values, Manifest representation,
38
+ and Plan validation under `agent/context_contract/`. Zeitwerk collapses the
39
+ directory so the existing `Phronomy::Agent` constant names, Policy/hook
40
+ protocols, and Manifest format remain unchanged. Agent execution and Context
41
+ assembly retain their existing responsibilities. See
42
+ [ADR-036](docs/decisions/036-context-contract-ownership.md).
43
+ - Move the internal `Phronomy::Agent::ToolExecutor` to
44
+ `Phronomy::Agent::Context::Capability::ToolExecutor`, alongside the default
45
+ Tool invocation contract, without an alias for the old internal constant.
46
+ Public Tool names, `call_async` arguments, execution modes, Runtime routing,
47
+ and Agent-owned authorization/result handling are preserved. See
48
+ [ADR-035](docs/decisions/035-tool-executor-capability-ownership.md).
49
+ - Move `Phronomy::Agent::HandoffRunner` to
50
+ `Phronomy::MultiAgent::HandoffRunner` without a compatibility alias on the
51
+ architecture refactoring branch. Handoff edges and Policy remain in Agent;
52
+ execution, persistence formats, cancellation and recovery behavior are unchanged.
53
+ See [the migration guide](docs/migrations/handoff-runner-multi-agent.md).
54
+
55
+ ---
56
+
57
+ ## [0.26.0] - 2026-09-14
58
+
59
+ ### Changed
60
+
61
+ - Rename the public completion type to `Phronomy::TaskResult`; remove `Task`
62
+ without a compatibility alias in this development release.
63
+ - Remove `Orchestrator#fan_out` and `#fan_out_async`. Use `dispatch_parallel`
64
+ for Agent-specific construction/knowledge/concurrency policy, or Execution
65
+ for application-defined JOB composition.
66
+ - Preserve cancelled states and original cancellation errors across result
67
+ transformations, independently of failures that carry CancellationError.
68
+
69
+ ### Added
70
+
71
+ - `TaskResult#flat_map`, `.all_settled`, and immutable input-order Outcome records.
72
+ - `Execution.run_async` / `.run` for shared runtime fan-out/fan-in, explicit
73
+ invocation context, whole-execution timeout/cancellation and partial snapshots.
74
+ - `Execution#observe` for scoped continuations of externally owned results.
75
+ - Optional `invocation_context:` on `Blocking.call_async`, with one-way combined
76
+ context and individual controls shared with Agent admission.
77
+
78
+ ### Fixed
79
+
80
+ - Dispose finished execution subscriptions and timers while retaining required
81
+ physical completion tracking. Orchestrator reuses the common Execution engine.
82
+
83
+ See [Result composition and Execution](docs/async-composition.md) for migration,
84
+ completion boundaries, cancellation and application responsibilities.
85
+
86
+ ---
87
+
17
88
  ## [0.25.0] - 2026-09-12
18
89
 
19
90
  ### Added
data/CONTRIBUTING.md CHANGED
@@ -98,16 +98,16 @@ Do not change Ruby visibility merely to make it match an `@api` annotation.
98
98
 
99
99
  ### Async completion boundary
100
100
 
101
- `Phronomy::Task` is the caller-facing completion handle. EventLoop/FSMSession and
101
+ `Phronomy::TaskResult` is the caller-facing completion handle. EventLoop/FSMSession and
102
102
  OffloadPool are execution/continuation mechanisms, not competing completion
103
103
  abstractions.
104
104
 
105
105
  Synchronous work that requires execution away from EventLoop must use an
106
106
  OffloadPool. Do not create production worker Threads in adapters, backends, or
107
107
  Tools to emulate asynchronous behavior. Logical waits between Phronomy
108
- lifecycles stay on EventLoop/FSMSession and settle a Task later.
108
+ lifecycles stay on EventLoop/FSMSession and settle a TaskResult later.
109
109
 
110
- The framework owns Task settlement (`complete`, `fail`, and framework-driven
110
+ The framework owns TaskResult settlement (`complete`, `fail`, and framework-driven
111
111
  cancellation). Application code should observe Tasks through `wait_result`,
112
112
  `on_complete`, `map`, and settlement state. Operation-wide cancellation of
113
113
  OffloadPool work is supplied through `CancellationToken`.
@@ -149,7 +149,7 @@ ambiguous. Do not renumber historical ADRs to repair that legacy collision.
149
149
 
150
150
  For Agent Context work, ADR-012 and ADR-013 define the current Journal,
151
151
  Manifest, Context Policy and persistent Knowledge model. ADR-010 defines the
152
- EventLoop/FSMSession, Task, and OffloadPool execution boundary. ADR-015 defines
152
+ EventLoop/FSMSession, TaskResult, and OffloadPool execution boundary. ADR-015 defines
153
153
  the Tool public façade, extension-SPI boundary, and RBS ownership rules.
154
154
 
155
155
  Current explanatory architecture starts at [`docs/architecture.md`](docs/architecture.md).
data/README.md CHANGED
@@ -22,8 +22,10 @@ tracking `main` directly.
22
22
  - **Multi-Agent Handoff** — semantic Source-to-Target responsibility transfer with policy-bounded Context projection and persisted active responsibility and exact Target recovery within one Persistence domain.
23
23
  - **EventLoop + FSMSession** — the framework control plane for logical lifecycle coordination.
24
24
  - **OffloadPool** — bounded operating-system-thread execution boundary for synchronous work that must not run on EventLoop.
25
- - **Task** — the common thread-free completion handle returned by Phronomy asynchronous APIs, including OffloadPool-backed work.
26
- - **Task.completed / Task.failed** — already-settled application results without starting execution.
25
+ - **TaskResult** — the common thread-free completion handle returned by Phronomy asynchronous APIs, including OffloadPool-backed work.
26
+ - **TaskResult.completed / TaskResult.failed** — already-settled application results without starting execution.
27
+ - **TaskResult.map / flat_map / all_settled** — result transformation, asynchronous chaining and ordered all-settled observation.
28
+ - **Execution.run_async / run** — start application JOBs and join their final results under a whole-execution timeout and cancellation scope.
27
29
  - **Blocking.call_async** — submits synchronous application work to the existing bounded OffloadPool without waiting for queue space.
28
30
  - **Journal / Context Policy / Manifest** — canonical history plus per-LLM-call context selection.
29
31
 
@@ -43,6 +45,10 @@ Then run:
43
45
  bundle install
44
46
  ```
45
47
 
48
+ This refactoring branch requires RubyLLM 2.0.x. See the
49
+ [RubyLLM 2 and token-ownership migration](docs/architecture/rubyllm-2-token-ownership.md)
50
+ for removed input-budget overrides and the in-flight Tool-manifest upgrade boundary.
51
+
46
52
  Configure RubyLLM with the provider credentials and transport policy required by
47
53
  your application. Phronomy does not add another LLM transport retry/timeout layer.
48
54
 
@@ -86,8 +92,8 @@ class. The legacy `Phronomy::Agent::Context::Capability::Base` constant remains
86
92
  valid for compatibility.
87
93
 
88
94
  For non-blocking top-level use, call `invoke_async` and keep the returned
89
- `Phronomy::Task`. Inside Phronomy lifecycle callbacks, do not block waiting for
90
- another Task; continue through explicit events instead.
95
+ `Phronomy::TaskResult`. Inside Phronomy lifecycle callbacks, do not block waiting for
96
+ another TaskResult; continue through explicit events instead.
91
97
 
92
98
  ```ruby
93
99
  task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks")
@@ -128,14 +134,14 @@ Runtime
128
134
  └─ EventLoop-driven timers
129
135
 
130
136
  EventLoop / FSMSession ─┐
131
- ├─> Task = completion handle
137
+ ├─> TaskResult = completion handle
132
138
  OffloadPool ────────────┘
133
139
  ```
134
140
 
135
141
  Logical waiting remains in EventLoop/FSMSession state. Synchronous work that
136
142
  would block EventLoop uses the bounded OffloadPool. OffloadPool-specific queue,
137
143
  worker, timeout, and abandonment state remains private runtime machinery; callers
138
- observe completion through `Phronomy::Task`. See
144
+ observe completion through `Phronomy::TaskResult`. See
139
145
  [Runtime and concurrency](docs/runtime-and-concurrency.md) for the detailed
140
146
  contracts, timeout/cancellation semantics, metrics, and callback rules.
141
147
 
@@ -144,7 +150,8 @@ contracts, timeout/cancellation semantics, metrics, and callback rules.
144
150
  - [Getting started](docs/getting-started.md) — installation, RubyLLM setup, Agent/Workflow basics, persistence, streaming.
145
151
  - [Features and API stability](docs/features.md) — public feature matrix and stability labels.
146
152
  - [Architecture](docs/architecture.md) — canonical current explanatory architecture entry and authority navigation.
147
- - [Runtime and concurrency](docs/runtime-and-concurrency.md) — EventLoop, FSMSession, Task, OffloadPool, cancellation, observability.
153
+ - [Runtime and concurrency](docs/runtime-and-concurrency.md) — EventLoop, FSMSession, TaskResult, OffloadPool, cancellation, observability.
154
+ - [Result composition and Execution](docs/async-composition.md) — application JOBs, map/flat_map, fan-in snapshots, context ownership and migration.
148
155
  - [MCP client](docs/mcp-client.md) — Model Context Protocol (MCP) integration and supported schema subset.
149
156
  - [Migration from 0.15-era APIs](docs/migrations/0.15.md).
150
157
  - [0.16 cleanup migration](docs/migrations/0.16.md).
data/VERIFY.sh CHANGED
@@ -21,9 +21,9 @@ required_files=(
21
21
  docs/decisions/024-event-loop-single-writer-agent-runtime.md
22
22
  docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md
23
23
  docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md
24
- lib/phronomy/workflow.rb
25
- lib/phronomy/workflow_context.rb
26
- lib/phronomy/workflow_runner.rb
24
+ lib/phronomy/workflow/execution/workflow.rb
25
+ lib/phronomy/workflow/execution/workflow_context.rb
26
+ lib/phronomy/workflow/execution/workflow_runner.rb
27
27
  lib/phronomy/engine/event_loop.rb
28
28
  lib/phronomy/engine/fsm_session.rb
29
29
  lib/phronomy/persistence/in_memory.rb
@@ -38,16 +38,16 @@ required_files=(
38
38
  spec/phronomy/persistence/initial_format_migration_spec.rb
39
39
  lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
40
40
  spec/phronomy/workflow_identity_contract_spec.rb
41
- lib/phronomy/invocation_context.rb
41
+ lib/phronomy/engine/invocation_context.rb
42
42
  lib/phronomy/agent/async_event_api.rb
43
43
  lib/phronomy/agent/execution_coordinator.rb
44
44
  lib/phronomy/agent/journal_record.rb
45
45
  lib/phronomy/agent/context_assembler.rb
46
- lib/phronomy/agent/context_policy.rb
47
- lib/phronomy/agent/context_policy_input.rb
46
+ lib/phronomy/agent/context_contract/context_policy.rb
47
+ lib/phronomy/agent/context_contract/context_policy_input.rb
48
48
  lib/phronomy/agent/context_policy_input_builder.rb
49
- lib/phronomy/agent/context_plan.rb
50
- lib/phronomy/agent/context_plan_validator.rb
49
+ lib/phronomy/agent/context_contract/context_plan.rb
50
+ lib/phronomy/agent/context_contract/context_plan_validator.rb
51
51
  lib/phronomy/agent/context_policies/default.rb
52
52
  spec/phronomy/agent/context_policy_architecture_regression_spec.rb
53
53
  lib/phronomy/agent/agent_invocation.rb
@@ -68,7 +68,7 @@ required_files=(
68
68
  spec/phronomy/workflow/admission_spec.rb
69
69
  spec/phronomy/workflow/live_signal_spec.rb
70
70
  spec/phronomy/workflow/transition_action_spec.rb
71
- lib/phronomy/generator_verifier.rb
71
+ lib/phronomy/generation/generator_verifier.rb
72
72
  spec/phronomy/generator_verifier_spec.rb
73
73
  spec/integration/subgraph_parallel_agent_tool_spec.rb
74
74
  lib/phronomy/tracing/base.rb
@@ -87,11 +87,11 @@ required_files=(
87
87
  spec/phronomy/fault_injection_advanced_spec.rb
88
88
  spec/phronomy/persistence_architecture_regression_spec.rb
89
89
  lib/phronomy/agent/base.rb
90
- lib/phronomy/agent_already_exists_error.rb
91
- lib/phronomy/agent_purged_error.rb
90
+ lib/phronomy/agent/lifecycle_contract/agent_already_exists_error.rb
91
+ lib/phronomy/agent/lifecycle_contract/agent_purged_error.rb
92
92
  lib/phronomy/engine/runtime/agent_ownership_registry.rb
93
93
  lib/phronomy/agent/agent_root.rb
94
- lib/phronomy/agent/llm_input_build_context.rb
94
+ lib/phronomy/agent/context_contract/llm_input_build_context.rb
95
95
  lib/phronomy/agent/shared_state.rb
96
96
  spec/phronomy/agent/base_spec.rb
97
97
  spec/phronomy/agent_spec.rb
@@ -119,9 +119,9 @@ done
119
119
 
120
120
  syntax_files=(
121
121
  spec/phronomy/architecture_governance_spec.rb
122
- lib/phronomy/workflow.rb
123
- lib/phronomy/workflow_context.rb
124
- lib/phronomy/workflow_runner.rb
122
+ lib/phronomy/workflow/execution/workflow.rb
123
+ lib/phronomy/workflow/execution/workflow_context.rb
124
+ lib/phronomy/workflow/execution/workflow_runner.rb
125
125
  lib/phronomy/engine/event_loop.rb
126
126
  lib/phronomy/engine/fsm_session.rb
127
127
  lib/phronomy/persistence/in_memory.rb
@@ -135,16 +135,16 @@ syntax_files=(
135
135
  spec/phronomy/persistence/initial_format_migration_spec.rb
136
136
  lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
137
137
  spec/phronomy/workflow_identity_contract_spec.rb
138
- lib/phronomy/invocation_context.rb
138
+ lib/phronomy/engine/invocation_context.rb
139
139
  lib/phronomy/agent/async_event_api.rb
140
140
  lib/phronomy/agent/execution_coordinator.rb
141
141
  lib/phronomy/agent/journal_record.rb
142
142
  lib/phronomy/agent/context_assembler.rb
143
- lib/phronomy/agent/context_policy.rb
144
- lib/phronomy/agent/context_policy_input.rb
143
+ lib/phronomy/agent/context_contract/context_policy.rb
144
+ lib/phronomy/agent/context_contract/context_policy_input.rb
145
145
  lib/phronomy/agent/context_policy_input_builder.rb
146
- lib/phronomy/agent/context_plan.rb
147
- lib/phronomy/agent/context_plan_validator.rb
146
+ lib/phronomy/agent/context_contract/context_plan.rb
147
+ lib/phronomy/agent/context_contract/context_plan_validator.rb
148
148
  lib/phronomy/agent/context_policies/default.rb
149
149
  spec/phronomy/agent/context_policy_architecture_regression_spec.rb
150
150
  lib/phronomy/agent/agent_invocation.rb
@@ -177,7 +177,7 @@ syntax_files=(
177
177
  spec/phronomy/workflow/admission_spec.rb
178
178
  spec/phronomy/workflow/live_signal_spec.rb
179
179
  spec/phronomy/workflow/fsm_session_spec.rb
180
- lib/phronomy/generator_verifier.rb
180
+ lib/phronomy/generation/generator_verifier.rb
181
181
  spec/phronomy/generator_verifier_spec.rb
182
182
  spec/integration/subgraph_parallel_agent_tool_spec.rb
183
183
  spec/phronomy/guarantee_model_spec.rb
@@ -197,11 +197,11 @@ syntax_files=(
197
197
  spec/phronomy/fault_injection_advanced_spec.rb
198
198
  spec/phronomy/persistence_architecture_regression_spec.rb
199
199
  lib/phronomy/agent/base.rb
200
- lib/phronomy/agent_already_exists_error.rb
201
- lib/phronomy/agent_purged_error.rb
200
+ lib/phronomy/agent/lifecycle_contract/agent_already_exists_error.rb
201
+ lib/phronomy/agent/lifecycle_contract/agent_purged_error.rb
202
202
  lib/phronomy/engine/runtime/agent_ownership_registry.rb
203
203
  lib/phronomy/agent/agent_root.rb
204
- lib/phronomy/agent/llm_input_build_context.rb
204
+ lib/phronomy/agent/context_contract/llm_input_build_context.rb
205
205
  lib/phronomy/agent/shared_state.rb
206
206
  lib/phronomy/agent/concerns/before_llm_input.rb
207
207
  lib/phronomy/testing/persistence_contract/an_agent_repository.rb
@@ -322,7 +322,7 @@ bundle exec rspec \
322
322
  spec/integration/multi_agent_handoff_followup_spec.rb
323
323
 
324
324
  echo "== ACS-06 / CG-07 durable format / record-oriented Persistence SPI =="
325
- ruby -c lib/phronomy/persistence.rb >/dev/null
325
+ ruby -c lib/phronomy/persistence/api/persistence.rb >/dev/null
326
326
  ruby -c lib/phronomy/persistence/durable_record.rb >/dev/null
327
327
  ruby -c lib/phronomy/persistence/durable_codec.rb >/dev/null
328
328
  ruby -c lib/phronomy/persistence/repository_facades.rb >/dev/null
@@ -467,7 +467,7 @@ for rel in [
467
467
  if fsm_new_has_id_kwarg(text):
468
468
  raise SystemExit(f"FAIL: {rel} still injects a domain/context ID as FSMSession id")
469
469
 
470
- workflow = pathlib.Path("lib/phronomy/workflow_runner.rb").read_text()
470
+ workflow = pathlib.Path("lib/phronomy/workflow/execution/workflow_runner.rb").read_text()
471
471
  event_loop = pathlib.Path("lib/phronomy/engine/event_loop.rb").read_text()
472
472
  fsm = pathlib.Path("lib/phronomy/engine/fsm_session.rb").read_text()
473
473
 
@@ -487,7 +487,7 @@ for required in ["workflow_terminal_persistence_result", ":persisting_terminal",
487
487
  PY
488
488
 
489
489
  echo "== ACS-13 Workflow admission / durable terminal barrier =="
490
- ruby -c lib/phronomy/workflow_runner.rb >/dev/null
490
+ ruby -c lib/phronomy/workflow/execution/workflow_runner.rb >/dev/null
491
491
  ruby -c lib/phronomy/engine/event_loop.rb >/dev/null
492
492
  ruby -c lib/phronomy/engine/fsm_session.rb >/dev/null
493
493
  ruby -c spec/phronomy/workflow/admission_spec.rb >/dev/null
@@ -5,11 +5,12 @@
5
5
  # Measures the per-invoke cost of the Phronomy::Agent::Base framework path
6
6
  # (context assembly, filter checks, before_llm_input hooks, response handling)
7
7
  # with a fully stubbed LLM. No network calls are made.
8
+ # Each sample invokes a fresh Agent once. Agent construction is outside the
9
+ # measured block; growing conversation history is a different workload.
8
10
  #
9
11
  # Scenarios:
10
12
  # 1. Minimal agent (no tools, no persistent Knowledge) — baseline framework overhead.
11
13
  # 2. Tool-aware agent with a registered stub Tool.
12
- # 3. Agent#stream setup latency (first-chunk time with stubbed stream).
13
14
 
14
15
  require "benchmark"
15
16
  require_relative "../lib/phronomy"
@@ -18,10 +19,12 @@ require_relative "../lib/phronomy"
18
19
  # Shared stubs
19
20
  # ---------------------------------------------------------------------------
20
21
 
21
- BenchAgentMessage = Struct.new(:role, :content, :tool_calls, :tokens) do
22
+ module BenchAgentMessage
22
23
  def self.assistant(content = "done")
23
- new(:assistant, content, nil,
24
- Struct.new(:input, :output, :cached, :cache_creation).new(5, 5, 0, 0))
24
+ RubyLLM::Message.new(
25
+ role: :assistant, content: content,
26
+ tokens: RubyLLM::Tokens.new(input: 5, output: 5, cache_read: 0, cache_write: 0)
27
+ )
25
28
  end
26
29
  end
27
30
 
@@ -34,22 +37,25 @@ class BenchStubChat
34
37
  @messages = []
35
38
  end
36
39
 
37
- def with_instructions(_) = self
38
- def with_tool(_) = self
40
+ def with_instructions(_, **_options) = self
41
+ def with_tools(*) = self
39
42
  def with_temperature(_) = self
40
43
  def with_cache_instructions(_) = self
41
44
  def with_output_schema(_) = self
42
- def on_tool_call(&) = self
43
- def before_tool_call(&) = self
44
45
  def last_message = @response
45
46
 
47
+ def after_message(&block)
48
+ @after_message = block
49
+ self
50
+ end
51
+
46
52
  def ask(_)
47
- @messages << @response
48
- @response
53
+ complete
49
54
  end
50
55
 
51
- def stream(*)
52
- yield @response.content if block_given?
56
+ def complete
57
+ @messages << @response
58
+ @after_message&.call(@response)
53
59
  @response
54
60
  end
55
61
  end
@@ -69,7 +75,6 @@ end
69
75
  # ---------------------------------------------------------------------------
70
76
 
71
77
  BENCH_RESP = BenchAgentMessage.assistant("benchmark complete")
72
- BENCH_RESP_CHAT = BenchStubChat.new(BENCH_RESP)
73
78
 
74
79
  bench_minimal_class = Class.new(Phronomy::Agent::Base) do
75
80
  agent_definition id: "bench-minimal", version: 1
@@ -86,22 +91,21 @@ bench_tool_class = Class.new(Phronomy::Agent::Base) do
86
91
  define_method(:build_chat) { |*| BenchStubChat.new(BENCH_RESP) }
87
92
  end
88
93
 
89
- BENCH_AGENT_MINIMAL = bench_minimal_class.new
90
- BENCH_AGENT_TOOLS = bench_tool_class.new
91
-
92
94
  AGENT_INVOKE_ITERATIONS = 200
95
+ BENCH_AGENTS_MINIMAL = Array.new(AGENT_INVOKE_ITERATIONS) { bench_minimal_class.new }.freeze
96
+ BENCH_AGENTS_TOOLS = Array.new(AGENT_INVOKE_ITERATIONS) { bench_tool_class.new }.freeze
93
97
 
94
98
  puts "=== bench_agent_invoke ==="
95
99
  Benchmark.bm(50) do |x|
96
- x.report("Agent#invoke — minimal (no tools), #{AGENT_INVOKE_ITERATIONS} iters") do
97
- AGENT_INVOKE_ITERATIONS.times do
98
- BENCH_AGENT_MINIMAL.invoke("ping")
100
+ x.report("Agent#invoke — fresh, no tools, #{AGENT_INVOKE_ITERATIONS} iters") do
101
+ BENCH_AGENTS_MINIMAL.each do |agent|
102
+ agent.invoke("ping")
99
103
  end
100
104
  end
101
105
 
102
- x.report("Agent#invoke — tool-aware, #{AGENT_INVOKE_ITERATIONS} iters") do
103
- AGENT_INVOKE_ITERATIONS.times do
104
- BENCH_AGENT_TOOLS.invoke("ping")
106
+ x.report("Agent#invoke — fresh, tool-aware, #{AGENT_INVOKE_ITERATIONS} iters") do
107
+ BENCH_AGENTS_TOOLS.each do |agent|
108
+ agent.invoke("ping")
105
109
  end
106
110
  end
107
111
  end
@@ -43,19 +43,18 @@ module BenchContextAssembler
43
43
  call_sequence: 2, call_mode: :complete,
44
44
  instruction: [], knowledge: knowledge, tools: [], conversation: conversation,
45
45
  token_budget: Phronomy::LlmContextWindow::TokenBudget.new(
46
- context_window: [item_count * 16, 4_096].max,
47
- max_output_tokens: 512
46
+ max_input_tokens: [item_count * 16, 4_096].max
48
47
  ),
49
48
  model_config: {}, previous_manifest: nil, metadata: {}
50
49
  )
51
50
  end
52
51
 
53
52
  def assembler_fixture
54
- persistence = Phronomy::Persistence::InMemory.new
53
+ persistence = Phronomy::Persistence.in_memory
55
54
  agent_class = Class.new(Phronomy::Agent::Base) do
56
55
  agent_definition id: "bench-manifest-context-assembler", version: 1
57
- model "local-model"
58
- context_window 16_384
56
+ # Resolve capabilities from RubyLLM's bundled registry; no provider call.
57
+ model "gpt-4o-mini"
59
58
  max_output_tokens 1_024
60
59
  instructions "Benchmark instruction"
61
60
  end
@@ -102,7 +102,7 @@ stub_agent_class = Class.new(Phronomy::Agent::Base) do
102
102
  end
103
103
 
104
104
  define_method(:invoke_async) do |input, **_kw|
105
- task = Phronomy::Task.deferred(name: "bench-stub")
105
+ task = Phronomy::TaskResult.deferred(name: "bench-stub")
106
106
  begin
107
107
  task.complete(invoke(input))
108
108
  rescue => error
@@ -0,0 +1,147 @@
1
+ # Agent Chat and explicit state ownership (R09, second slice)
2
+
3
+ ## Purpose and clients
4
+
5
+ Agent::Base is the declaration and lifecycle facade used by application Agent
6
+ subclasses. It also supplies internal hooks to normal execution, follow-up LLM
7
+ calls and recovery. Those callers need a Chat built from the selected or saved
8
+ model settings and populated with an already materialized projection. They do
9
+ not ask the Chat constructor to make Context Policy decisions again.
10
+
11
+ Applications create Agents and explicitly add knowledge, clear context/history,
12
+ reset context or close an idle Agent through Base. These operations append
13
+ Journal records and advance an AgentRoot together. The existing live root is
14
+ authoritative; an operation must not silently reload a newer root and retry.
15
+
16
+ Refactor 42 extracted ToolBinding and documented existing declaration rules.
17
+ Refactor 43 separates the remaining Chat construction and explicit state writes
18
+ while keeping the Base facade and live-state publication boundary.
19
+
20
+ ## Ownership and placement
21
+
22
+ | Owner | Responsibility |
23
+ |---|---|
24
+ | Base | Public declarations and lifecycle entry points, live-owner checks, selected class settings, operation-specific root changes, projection installation and publication of successful state writes. |
25
+ | RuntimeChatBuilder | RubyLLM Chat options and setters; provider-specific instruction cache representation. |
26
+ | StateWriter | Initial root/context/knowledge persistence and explicit idle-Agent Journal/root changes in one transaction. |
27
+ | ContextImporter / JournalRecord / AgentRoot | Existing history validation, record meanings and immutable root values. |
28
+ | ToolBinding / Orchestrator | Existing Tool decoration and invocation-specific subagent context. |
29
+ | ExecutionCoordinator and its workers | Existing execution-time writes and EventLoop-side publication, unchanged here. |
30
+
31
+ RuntimeChatBuilder and StateWriter live in agent/context_assembly, whose existing
32
+ collaborators already turn context inputs into runtime or durable records.
33
+ StateWriter is orchestration over ContextImporter, JournalRecord and AgentRoot;
34
+ it is not a low-level lifecycle primitive or a raw storage adapter. Placing it
35
+ in lifecycle would introduce lifecycle -> context_assembly and enlarge the
36
+ existing directory cycle. That placement was rejected during design validation.
37
+ No new directory, loader rule, mixin or namespace alias is needed.
38
+
39
+ There are two new internal collaborator classes, plus StateWriter's private
40
+ Update data value containing root and appended records. Neither collaborator
41
+ holds an Agent reference or invokes private Agent methods through send. The
42
+ mutation block passed to StateWriter is the existing operation-specific root
43
+ proposal, called at the same point inside the transaction.
44
+
45
+ ## Chat construction and installation
46
+
47
+ Base#build_chat selects its default model/provider/temperature/token settings
48
+ only when model_config is falsey. An explicit empty Hash remains an explicit
49
+ config. RuntimeChatBuilder then builds options, creates RubyLLM.chat, applies
50
+ temperature, optionally applies max_output_tokens and returns the original
51
+ Chat, irrespective of setter return values. Zero remains a valid setting.
52
+
53
+ Base#apply_instructions keeps its signature and delegates the provider detail.
54
+ Only a truthy cache flag with provider.to_s == "anthropic" wraps instructions
55
+ in Anthropic::Content(cache: true). All other values pass through unchanged;
56
+ the with_instructions return value is retained.
57
+
58
+ Base#_apply_runtime_projection_to_chat remains a short orchestration hook. It
59
+ installs system instructions, prepares and installs each Tool in order, then
60
+ appends the original message objects to the existing Chat messages. Keeping the
61
+ hook retains application/test overrides of apply_instructions and Orchestrator's
62
+ prepare_tool_class(invocation:) cooperation. Moving it into a builder receiving
63
+ the entire Agent would merely introduce callbacks back into Base.
64
+
65
+ AgentInvocationSessionBuilder and InvocationRestorer keep using the same hooks.
66
+ Saved config is authoritative during recovery; current class declarations do
67
+ not overwrite it. A failure stops installation at that operation, propagates
68
+ the same error and does not install later Tools/messages. No new Chat cache,
69
+ provider-independent SPI, LLM call or Tool execution path is introduced.
70
+
71
+ ## Initial persistence
72
+
73
+ Base resolves the Agent definition before calling StateWriter#create_root.
74
+ StateWriter constructs the initial root, then performs the existing sequence:
75
+
76
+ 1. Create the root in the transaction.
77
+ 2. Import/encode context records, followed by knowledge records, in order.
78
+ 3. If records exist, append them at position zero, advance both revisions to
79
+ one and save the root with expected_revision zero.
80
+ 4. Return the resulting root after the transaction call returns successfully.
81
+
82
+ Empty initial input does not append or advance revisions. String coercion of
83
+ knowledge, metadata normalization, imported text/JSON handling and invalid-format
84
+ errors remain unchanged. Base's initialize_owned_state still assigns the root,
85
+ reads the committed Journal and freezes its local record array. Hydration and
86
+ definition compatibility checks remain unchanged in Base.
87
+
88
+ ## Explicit mutation and publication
89
+
90
+ For add_knowledge and context/lifecycle changes, Base first checks live ownership
91
+ and captures agent_root. StateWriter takes that root, persistence and agent ID
92
+ explicitly. Each operation retains this transaction sequence:
93
+
94
+ 1. Assert durable execution idleness.
95
+ 2. Construct the content/state record and append with the captured Journal head.
96
+ 3. Build the next root; a context mutation's proposal block runs after append.
97
+ 4. Save with the captured expected Agent revision.
98
+ 5. After the transaction call returns, return Update(root, appended records).
99
+
100
+ Base applies the returned Journal records before replacing @root. StateWriter
101
+ never publishes live state and never reloads the mutable root or Journal.
102
+ add_knowledge still returns self; clear/reset/close still return the new root.
103
+ close! increments Agent revision while preserving context revision. Context
104
+ changes preserve the old generation in the event and the existing revision
105
+ fallback rule in the resulting root.
106
+
107
+ Block, content, append, CAS and commit failures escape without publication.
108
+ Failures inside a supported transaction roll back its durable writes. An error
109
+ is not wrapped, retried or turned into a false successful return. The transaction
110
+ method's own return value is ignored as before.
111
+
112
+ This is not a stronger transaction or concurrency contract. If a backend commits
113
+ then reports an unknown outcome, or local Journal publication fails after a
114
+ successful commit, durable and live state can differ just as before. There is
115
+ no new F1 reconciliation for these manual operations. If an application encloses
116
+ an Agent mutation in an outer Persistence transaction, returning from the inner
117
+ scope is not proof of the outer commit; this refactor adds no deferred-publication
118
+ or outer-rollback coordination. Application-owned outer transactions need a
119
+ separate design decision, not an implicit guarantee from this extraction.
120
+
121
+ The public lifecycle checks, purge ownership protocol, admission, hydration,
122
+ execution commits, SQL and stored formats are unchanged. Relocated private
123
+ record-building helpers are not retained as an extension API. Retained Chat,
124
+ Tool and root-proposal hooks keep their names and signatures.
125
+
126
+ ## Validation and completion rule
127
+
128
+ Thirty-seven additional behavioral examples run through Base against both the
129
+ unchanged Refactor 42 and this candidate. They cover saved/default/empty config,
130
+ provider options, setter and hook ordering, original objects/errors, import
131
+ formats, atomic rollback, idle checks, event/revision rules and publication.
132
+ The existing architecture guard now checks StateWriter as well as Base for
133
+ forbidden mutable-root/Journal reload and live-state publication paths.
134
+
135
+ Full core/integration/examples, real SQLite, public API/SPI, RBS, style, isolated
136
+ gem loading and independent package application are recorded in the release
137
+ evidence. New private helpers are distinguished from the unchanged public
138
+ contract and retained hook signatures. Static dependency analysis rejects new
139
+ cycle membership rather than merely checking unchanged edge counts.
140
+
141
+ Refactor 43 is applied and verified at core 5c4c0392; R09 is closed. Its Tool
142
+ binding and declaration rules were already verified in Refactor 42. Base remains
143
+ the public facade; keeping short orchestration methods there is intentional.
144
+ The DSL behavior itself remains unchanged. R10 is implemented in Refactor 44,
145
+ pending application verification. The published SVG is applied43-01. See the
146
+ [naming boundary](entry-action-and-team-wording.md) and the separate
147
+ [open Tool schema finding](tool-schema-recording-gap.md).