phronomy 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -0,0 +1,153 @@
1
+ # Agent Chat and explicit state ownership (R09, second slice)
2
+
3
+ > Current boundary: r8 unit 6 supersedes the historical Chat/SDK ownership and
4
+ > adapter extension SPI below. Agent uses LLMAdapter values; the backend owns SDK
5
+ > construction/interception; Tool owns canonical schema validation. See
6
+ > `docs/architecture/r8-unit6.md` and `docs/migrations/r8-unit6.md`.
7
+
8
+
9
+ ## Purpose and clients
10
+
11
+ Agent::Base is the declaration and lifecycle facade used by application Agent
12
+ subclasses. It also supplies internal hooks to normal execution, follow-up LLM
13
+ calls and recovery. Those callers need a Chat built from the selected or saved
14
+ model settings and populated with an already materialized projection. They do
15
+ not ask the Chat constructor to make Context Policy decisions again.
16
+
17
+ Applications create Agents and explicitly add knowledge, clear context/history,
18
+ reset context or close an idle Agent through Base. These operations append
19
+ Journal records and advance an AgentRoot together. The existing live root is
20
+ authoritative; an operation must not silently reload a newer root and retry.
21
+
22
+ Refactor 42 extracted ToolBinding and documented existing declaration rules.
23
+ Refactor 43 separates the remaining Chat construction and explicit state writes
24
+ while keeping the Base facade and live-state publication boundary.
25
+
26
+ ## Ownership and placement
27
+
28
+ | Owner | Responsibility |
29
+ |---|---|
30
+ | 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. |
31
+ | RuntimeChatBuilder | RubyLLM Chat options and setters; provider-specific instruction cache representation. |
32
+ | StateWriter | Initial root/context/knowledge persistence and explicit idle-Agent Journal/root changes in one transaction. |
33
+ | ContextImporter / JournalRecord / AgentRoot | Existing history validation, record meanings and immutable root values. |
34
+ | ToolBinding / Orchestrator | Existing Tool decoration and invocation-specific subagent context. |
35
+ | ExecutionCoordinator and its workers | Existing execution-time writes and EventLoop-side publication, unchanged here. |
36
+
37
+ RuntimeChatBuilder and StateWriter live in agent/context_assembly, whose existing
38
+ collaborators already turn context inputs into runtime or durable records.
39
+ StateWriter is orchestration over ContextImporter, JournalRecord and AgentRoot;
40
+ it is not a low-level lifecycle primitive or a raw storage adapter. Placing it
41
+ in lifecycle would introduce lifecycle -> context_assembly and enlarge the
42
+ existing directory cycle. That placement was rejected during design validation.
43
+ No new directory, loader rule, mixin or namespace alias is needed.
44
+
45
+ There are two new internal collaborator classes, plus StateWriter's private
46
+ Update data value containing root and appended records. Neither collaborator
47
+ holds an Agent reference or invokes private Agent methods through send. The
48
+ mutation block passed to StateWriter is the existing operation-specific root
49
+ proposal, called at the same point inside the transaction.
50
+
51
+ ## Chat construction and installation
52
+
53
+ Base#build_chat selects its default model/provider/temperature/token settings
54
+ only when model_config is falsey. An explicit empty Hash remains an explicit
55
+ config. RuntimeChatBuilder then builds options, creates RubyLLM.chat, applies
56
+ temperature, optionally applies max_output_tokens and returns the original
57
+ Chat, irrespective of setter return values. Zero remains a valid setting.
58
+
59
+ Base#apply_instructions keeps its signature and delegates the provider detail.
60
+ Only a truthy cache flag with provider.to_s == "anthropic" wraps instructions
61
+ in Anthropic::Content(cache: true). All other values pass through unchanged;
62
+ the with_instructions return value is retained.
63
+
64
+ Base#_apply_runtime_projection_to_chat remains a short orchestration hook. It
65
+ installs system instructions, prepares and installs each Tool in order, then
66
+ appends the original message objects to the existing Chat messages. Keeping the
67
+ hook retains application/test overrides of apply_instructions and Orchestrator's
68
+ prepare_tool_class(invocation:) cooperation. Moving it into a builder receiving
69
+ the entire Agent would merely introduce callbacks back into Base.
70
+
71
+ InvocationActions and EngineSessionBuilder and InvocationRestorer keep using the same hooks.
72
+ Saved config is authoritative during recovery; current class declarations do
73
+ not overwrite it. A failure stops installation at that operation, propagates
74
+ the same error and does not install later Tools/messages. No new Chat cache,
75
+ provider-independent SPI, LLM call or Tool execution path is introduced.
76
+
77
+ ## Initial persistence
78
+
79
+ Base resolves the Agent definition before calling StateWriter#create_root.
80
+ StateWriter constructs the initial root, then performs the existing sequence:
81
+
82
+ 1. Create the root in the transaction.
83
+ 2. Import/encode context records, followed by knowledge records, in order.
84
+ 3. If records exist, append them at position zero, advance both revisions to
85
+ one and save the root with expected_revision zero.
86
+ 4. Return the resulting root after the transaction call returns successfully.
87
+
88
+ Empty initial input does not append or advance revisions. String coercion of
89
+ knowledge, metadata normalization, imported text/JSON handling and invalid-format
90
+ errors remain unchanged. Base's initialize_owned_state still assigns the root,
91
+ reads the committed Journal and freezes its local record array. Hydration and
92
+ definition compatibility checks remain unchanged in Base.
93
+
94
+ ## Explicit mutation and publication
95
+
96
+ For add_knowledge and context/lifecycle changes, Base first checks live ownership
97
+ and captures agent_root. StateWriter takes that root, persistence and agent ID
98
+ explicitly. Each operation retains this transaction sequence:
99
+
100
+ 1. Assert durable execution idleness.
101
+ 2. Construct the content/state record and append with the captured Journal head.
102
+ 3. Build the next root; a context mutation's proposal block runs after append.
103
+ 4. Save with the captured expected Agent revision.
104
+ 5. After the transaction call returns, return Update(root, appended records).
105
+
106
+ Base applies the returned Journal records before replacing @root. StateWriter
107
+ never publishes live state and never reloads the mutable root or Journal.
108
+ add_knowledge still returns self; clear/reset/close still return the new root.
109
+ close! increments Agent revision while preserving context revision. Context
110
+ changes preserve the old generation in the event and the existing revision
111
+ fallback rule in the resulting root.
112
+
113
+ Block, content, append, CAS and commit failures escape without publication.
114
+ Failures inside a supported transaction roll back its durable writes. An error
115
+ is not wrapped, retried or turned into a false successful return. The transaction
116
+ method's own return value is ignored as before.
117
+
118
+ This is not a stronger transaction or concurrency contract. If a backend commits
119
+ then reports an unknown outcome, or local Journal publication fails after a
120
+ successful commit, durable and live state can differ just as before. There is
121
+ no new F1 reconciliation for these manual operations. If an application encloses
122
+ an Agent mutation in an outer Persistence transaction, returning from the inner
123
+ scope is not proof of the outer commit; this refactor adds no deferred-publication
124
+ or outer-rollback coordination. Application-owned outer transactions need a
125
+ separate design decision, not an implicit guarantee from this extraction.
126
+
127
+ The public lifecycle checks, purge ownership protocol, admission, hydration,
128
+ execution commits, SQL and stored formats are unchanged. Relocated private
129
+ record-building helpers are not retained as an extension API. Retained Chat,
130
+ Tool and root-proposal hooks keep their names and signatures.
131
+
132
+ ## Validation and completion rule
133
+
134
+ Thirty-seven additional behavioral examples run through Base against both the
135
+ unchanged Refactor 42 and this candidate. They cover saved/default/empty config,
136
+ provider options, setter and hook ordering, original objects/errors, import
137
+ formats, atomic rollback, idle checks, event/revision rules and publication.
138
+ The existing architecture guard now checks StateWriter as well as Base for
139
+ forbidden mutable-root/Journal reload and live-state publication paths.
140
+
141
+ Full core/integration/examples, real SQLite, public API/SPI, RBS, style, isolated
142
+ gem loading and independent package application are recorded in the release
143
+ evidence. New private helpers are distinguished from the unchanged public
144
+ contract and retained hook signatures. Static dependency analysis rejects new
145
+ cycle membership rather than merely checking unchanged edge counts.
146
+
147
+ Refactor 43 is applied and verified at core 5c4c0392; R09 is closed. Its Tool
148
+ binding and declaration rules were already verified in Refactor 42. Base remains
149
+ the public facade; keeping short orchestration methods there is intentional.
150
+ The DSL behavior itself remains unchanged. R10 is implemented in Refactor 44,
151
+ pending application verification. The published SVG is applied43-01. See the
152
+ [naming boundary](entry-action-and-team-wording.md) and the separate
153
+ [open Tool schema finding](tool-schema-recording-gap.md).
@@ -0,0 +1,139 @@
1
+ # Agent configuration and Tool binding (R09, first slice)
2
+
3
+ ## Role and boundary
4
+
5
+ Agent::Base is the public declaration and lifecycle facade. Before tools are
6
+ advertised to an LLM or installed in a runtime chat, its prepare_tool_class hook
7
+ selects the Agent's alias and result filters for the original Tool class.
8
+ ToolDefinitionSet and runtime projection installation use that same hook.
9
+ MultiAgent::Orchestrator overrides it, calls super, then adds subagent context.
10
+
11
+ Previously the hook also generated both decorator subclasses and bridged custom
12
+ asynchronous results, including cancellation and physical completion. Refactor 42
13
+ extracts that mechanism into Agent::ToolBinding in agent/tool_execution. Base
14
+ retains the selection hook and its invocation keyword for Orchestrator cooperation.
15
+ ToolBinding takes an explicit Tool class, alias and filter list; it holds no Agent
16
+ reference and does not call private Agent methods back through send.
17
+
18
+ | Owner | Responsibility |
19
+ |---|---|
20
+ | Base / Filterable | Select the original class's alias and ordered class, instance and scoped filters. |
21
+ | ToolBinding | Construct alias/result-filter decorators, preserve effective Tool names, and bridge custom async results. |
22
+ | Capability::Base / ToolExecutor | Default Tool call protocol and physical execution. |
23
+ | ToolInvocation | Validation, authorization, execution supervision and state restoration. |
24
+ | Orchestrator | Capture subagent parent/config/knowledge context after Base preparation. |
25
+
26
+ There is one new internal class and one production file. ToolBinding is marked
27
+ @api private, like other Agent implementation collaborators; it is not a new
28
+ supported application API. No loader change, mixin, alias or public signature
29
+ change is introduced. Definitions, approval, durable schemas and stored Tool
30
+ state are unchanged.
31
+
32
+ ## Preparation and reading order
33
+
34
+ Base first returns non-Class values unchanged. For classes it constructs the
35
+ alias binding, then resolves filters, then prepares the filtered class. This
36
+ order preserves subclass creation before filter selection. With neither alias
37
+ nor filters the exact original class is returned. With an alias and no filters,
38
+ the alias subclass is returned without instantiating a Tool.
39
+
40
+ ToolBinding#prepare reads the effective name and the call_async method owner,
41
+ then defines the synchronous decorator and, only when needed, the custom async
42
+ decorator. filter_async_result registers physical and logical completion in that
43
+ order. propagate_failure and complete_filtered_result separate terminal outcomes.
44
+ The methods express one level below their operation names rather than leaving
45
+ the whole callback protocol inside Base.
46
+
47
+ The original Tool class remains the scoped-filter key even when an alias creates
48
+ a subclass. Each preparation captures a newly collected filter list; it does not
49
+ cache generated classes or deep-copy filter objects. Later registrations affect
50
+ later preparations. The synchronous call passes the original args/keyword values
51
+ to super and applies class, instance and scoped filters in that order, with the
52
+ effective name and original args. Filter exceptions propagate without ToolError
53
+ wrapping by this decorator.
54
+
55
+ ## Asynchronous contract retained
56
+
57
+ - If call_async is owned by Capability::Base, leave it inherited: its ordinary
58
+ execution path reaches the decorated call. A second async filter wrapper would
59
+ apply the same filters twice.
60
+ - Any other method owner, including an inherited custom implementation, receives
61
+ the custom async decorator. It forwards args/kwargs to super and wraps the
62
+ returned operation in PhysicalCompletionTask. It does not wait for completion.
63
+ - Preserve logical completed/failed/cancelled status, values and error identity.
64
+ Failure skips filters. A filter StandardError fails the derived result with
65
+ that same exception. A synchronous startup exception still escapes call_async.
66
+ - With on_physical_complete support, forward that signal independently of the
67
+ logical result. Logical cancellation/failure must not fabricate physical
68
+ completion while the source is still working. Physical completion can precede
69
+ the logical callback or already be true when the listener is registered.
70
+ - Without that signal, mark physical completion before settling the derived
71
+ result, including filter failure. Listener registration remains physical first,
72
+ logical second, so already-settled operations are handled by existing callbacks.
73
+ - Cancelling the derived result does not cancel the source. A later successful
74
+ source completion can still execute result filters, while its derived status
75
+ remains cancelled. This is existing behavior, not a new cancellation guarantee.
76
+
77
+ No retry, additional cancellation propagation, thread/pool policy or physical
78
+ completion guarantee is added. The source signal still describes source work,
79
+ not a new synchronization boundary around the filter callback itself.
80
+
81
+ ## Agent declaration inheritance as currently implemented
82
+
83
+ Do not infer one uniform inheritance rule from the word "configuration". The
84
+ following describes current compatibility behavior; Refactor 42 does not change
85
+ the DSL methods. Parent reads are live lookups, not snapshots taken at subclass
86
+ creation.
87
+
88
+ | Declaration / getter | Child without an own value | Own value / special behavior |
89
+ |---|---|---|
90
+ | model | Global configuration.default_model; does not read parent's model | Truthy setter, nil/false are reads. Global changes remain visible until an own value is set. |
91
+ | instructions | Parent instructions, including the same Proc or template object | Text or block replaces the own value. nil without a block is a read, not a clear. |
92
+ | provider | Parent provider | Truthy setter; nil/false read. |
93
+ | tools | Parent tools array by reference | A Hash replaces the own class list. An empty Hash is an explicit empty list. nil reads. |
94
+ | tool_aliases | Parent aliases merged with own aliases | Alias values are stringified; nil entries are omitted. A child nil alias or empty tools list does not erase inherited aliases. |
95
+ | context_policy | Parent policy object, ultimately ContextPolicies::Default.instance | Exactly one ContextPolicy object replaces it. nil is invalid, not reset. |
96
+ | temperature | nil; does not inherit | Truthy setter; zero is accepted; nil/false read. |
97
+ | max_iterations | 10; does not inherit | Truthy setter; zero is accepted. |
98
+ | cache_instructions | nil; does not inherit | nil reads, false is an explicit value. |
99
+ | max_output_tokens | nil; does not inherit | nil reads; positive Integer conversion. context_window moved to the RubyLLM registry in Refactor 45. |
100
+ | agent_definition | Raises until that class declares identity/revision | Each subclass requires its own definition; this is not inherited model configuration. |
101
+ | input/output/tool_result_filter registries | Empty class registry; do not inherit | Registrations append on that class. Instance filters are separate. |
102
+ | before_llm_input / _before_llm_input | nil class callback; does not inherit | Instance and global hook behavior remains separate. |
103
+
104
+ The generic Agent rule does not override specialised subclass DSL such as
105
+ Orchestrator's subagent registry. Tool/Capability declaration inheritance is also
106
+ a different contract, already covered by its configuration-inheritance tests.
107
+
108
+ The existing test description that said "normal class configuration still
109
+ inherits" referred only to instructions and was too broad. It now names the
110
+ actual checked rule. No new policy is inferred from that earlier wording.
111
+
112
+ For compatibility, retain these rules in this source refactor. Uniform parent
113
+ fallback, alias removal and copying inherited mutable values would be separate
114
+ behavior changes requiring an explicit migration decision. They can change
115
+ model/provider combinations, budgets, Tool names and application callbacks.
116
+
117
+ ## Verification and remaining R09 work
118
+
119
+ Thirty-five new contract examples run against both the unchanged Refactor 41 and
120
+ the candidate. They exercise selection/alias order, names, filter ordering,
121
+ forwarded object identity, default/custom async behavior, logical/physical
122
+ completion, cancellation and DSL inheritance. Existing Tool definition, approval,
123
+ Orchestrator and full integration suites cover the cooperation paths.
124
+ The release, API/SPI, RBS, style, isolated gem and examples gates are recorded in
125
+ the distribution. Candidate remote CI, live PostgreSQL, live LLM and performance
126
+ are not measured by this change.
127
+
128
+ Refactor 42 is applied and independently verified at core 4a57a3c2. This
129
+ completes the Tool binding and declaration-contract slice. Refactor 43 addresses
130
+ the original Chat construction and explicit state-mutation scope; see the
131
+ [Chat/state design](agent-chat-and-state-ownership.md). It preserves the Base
132
+ hooks and operation/publication order rather than changing DSL semantics.
133
+
134
+ Refactor 43 application was verified at core 5c4c0392; R09 is closed.
135
+ R10 is implemented in Refactor 44, pending application verification.
136
+ The published SVG is applied43-01. See the [naming boundary](entry-action-and-team-wording.md)
137
+ and the separate [open Tool schema finding](tool-schema-recording-gap.md).
138
+
139
+ Refactor 45 changes the token declarations as described in [the migration](rubyllm-2-token-ownership.md). Other Agent inheritance rules above remain unchanged. Tool schema/provider configuration follows the RubyLLM 2 inherited configuration contract.
@@ -84,8 +84,10 @@ See [Context Management](context-management.md) and
84
84
  ## 3. Live mutable ownership
85
85
 
86
86
  Within one Runtime/process, one mutable live Agent instance owns one `agent_id`.
87
- Loading or creating a second mutable live instance for the same `agent_id` is
88
- rejected.
87
+ `Agent::OwnershipRegistry` reserves that identity before materialization;
88
+ Runtime strongly retains the registry through the generic shutdown contract.
89
+ Repeated load returns the same live instance, while duplicate create is rejected.
90
+ See [ADR-041](../decisions/041-feature-owned-identity-registries.md).
89
91
 
90
92
  During active execution, EventLoop is the single writer of Phronomy-managed live
91
93
  Agent execution state. Blocking Persistence work and other synchronous work that
@@ -0,0 +1,13 @@
1
+ # Agent transition ownership (r8 unit 7)
2
+
3
+ InvocationTransitions owns both automatic and external transitions, including
4
+ ordered guard evaluation. InvocationActions owns Agent entry operations.
5
+ EngineSessionBuilder configures FSMSession using those definitions;
6
+ PhaseMachineBuilder adapts them to state_machines without defining progress rules.
7
+ ExecutionCoordinator and RecoveryCoordinator retain durable decisions and use
8
+ the Agent-owned ExecutionEnvironment port for execution connection.
9
+
10
+ See [r8 unit 7](r8-unit7.md) and [ADR-067](../decisions/067-agent-progress-and-engine-connection.md).
11
+ Existing event source validation, callback failure priority, short-circuiting,
12
+ Tool outcome priority, suspended/wait/terminal boundaries and session incarnation
13
+ rules are preserved. Agent's Tool child FSM is still a separate remaining task.
@@ -0,0 +1,152 @@
1
+ # Backend responsibilities and asynchronous execution clients
2
+
3
+ The diagram distinguishes responsibility **groups** from its display layers.
4
+ The earlier B1-B6 horizontal bands and topical columns are restored for
5
+ readability. `G14`, `G46`, `G47` and `G48` remain stable responsibility identifiers;
6
+ boundary permissions follow those responsibilities, not B/G numbers or positions.
7
+ Each source directory keeps its own module box and stable module ID. Restoring
8
+ the display does not undo the separation of backend contracts and execution.
9
+
10
+ The restored display puts separated Backend Contracts in B5, alongside but
11
+ distinct from Engine Internals and Execution Contracts. B4 holds Async Clients and separately grouped Implementations. Execution
12
+ operations and values now share G58; the former G59 service split is removed. Backend directories awaiting separation remain visibly mixed
13
+ in B4; their contracts move to B5 in their implemented phase. These are display
14
+ positions, not a shared architectural level or namespace.
15
+
16
+ | Group | Responsibility | Dependency rule |
17
+ |---|---|---|
18
+ | G14 Engine Internals | Runtime, workers, FSM and EventLoop | No feature-specific backend or client dependencies |
19
+ | G58 Execution Framework | Submission protocol, TaskResult, Execution, Blocking, controls and common rules | Only Execution and Common, including neutral settings; Engine implements the mechanism |
20
+ | G46 Async Clients | Execute synchronous backend operations through Execution | Use Backend and Execution contracts; no direct Engine or concrete backend selection |
21
+ | G47 Backend Contracts | Synchronous SPI, values, errors and shared rules | No Engine, Execution Services, Async Client or concrete implementation dependencies |
22
+ | G48 Backend Implementations | Implement the synchronous backend SPI | Depend on the corresponding Contracts; no Engine, Execution Services or client dependencies |
23
+ | G57 Content Service | Content IDs, canonical text/JSON and digest integrity over Storage | Uses neutral Storage and Common; no physical backend, Engine or domain orchestration dependencies |
24
+
25
+ Composition chooses concrete backends and supplies them to consumers. These
26
+ are source-ownership groups, not shared Ruby namespaces. Synchronous APIs can
27
+ be called directly. Diagram links represent source references; calls through
28
+ injected objects require separate runtime review.
29
+
30
+ ## Implemented phase: P1/P2/P3/P4 (`storage`)
31
+
32
+ P3 adds public VectorStore and Embeddings AsyncClients. Their synchronous
33
+ contracts and implementations have no Engine dependencies. See the
34
+ [public receiver migration](../migrations/vector-async-clients.md).
35
+
36
+ ### P2: LLM
37
+
38
+ - Install the AST analyzer, group annotations, SVG formatter and boundary gate
39
+ in `tools/architecture/`. The Architecture workflow checks the committed
40
+ phase and generates evidence for its actual checkout SHA.
41
+ - `LLMAdapter::Base` now defines synchronous generation plus chat construction,
42
+ configuration, message/tool-call values, identity and optional input budget.
43
+ RubyLLM-specific construction and error translation belong to its backend.
44
+ Adapter extensions must implement the new operations; no legacy wrapper remains.
45
+ - `LLMAdapter::AsyncClient` owns the internal `complete_async` and `stream_async`
46
+ operations in `llm_adapter/async/`. It is not a public extension API.
47
+ - `LLMAdapter::RubyLLM` lives in `llm_adapter/backends/` and still inherits Base.
48
+ Its public name, synchronous signatures and default configuration are unchanged.
49
+ - Zeitwerk collapses these two new directories. Neither `Phronomy::Async` nor
50
+ `LLMAdapter::Async` / `LLMAdapter::Backends` is introduced.
51
+ - Agent constructs the framework client around the configured synchronous
52
+ adapter. Custom adapters implement no async methods and need no pool logic.
53
+
54
+ The client preserves `on_full: :raise`, the original pool TaskResult, token
55
+ forwarding and per-chunk cancellation. Transport timeout/retry stays with the
56
+ adapter/provider. Construction starts no Runtime, and the default pool is
57
+ resolved again on each operation; an injected pool stays caller-owned. Only an
58
+ internal lightweight sink runs on the worker; application event callbacks
59
+ continue to run on EventLoop.
60
+
61
+ Use `require "phronomy"` and the public constants. The old implementation path
62
+ `phronomy/llm_adapter/ruby_llm` moves to
63
+ `phronomy/llm_adapter/backends/ruby_llm`; arbitrary partial-load paths are not the
64
+ public loading contract. No shim in the contract directory reintroduces an
65
+ implementation dependency. No public RBS or Stable/Beta API snapshot changes
66
+ are needed in this phase; both are verified unchanged.
67
+
68
+ ### P3: VectorStore and Embeddings
69
+
70
+ - `VectorStore::AsyncClient` owns public add/search/remove/clear async operations;
71
+ `VectorStore::Embeddings::AsyncClient` owns public `embed_async`.
72
+ - Backends implement only synchronous methods. The old `AsyncBackend` mixin
73
+ and inherited async methods are removed; application callers change receiver.
74
+ - InMemory, Pgvector, RedisSearch and RubyLLMEmbeddings move into `backends/`
75
+ directories. Their public constants, synchronous signatures and bodies stay
76
+ unchanged. Zeitwerk explicitly collapses the new `async/` and `backends/` paths.
77
+ - Synchronous RBS uses structural `_CancellationSignal`; async signatures keep
78
+ TaskResult and the execution token. The public API snapshot records the new
79
+ receivers and the intentional removals from Base/InMemory.
80
+ - All three original backend-to-Engine edges are removed. In the measured view,
81
+ M28/M29 join G47 Contracts in B5. M67/M68 are clients in B4 and M71/M72 are
82
+ separately grouped implementations. No pending Storage clients are drawn.
83
+
84
+ ### P4: Storage execution
85
+
86
+ - Public `Storage::AsyncClient#transaction_async` opens the raw backend transaction
87
+ on a worker and yields its scoped Storage::View. Backends stay synchronous.
88
+ - Private `Storage::AsyncClient.submit(pool:)` executes existing domain units
89
+ without adding a transaction, timeout or token. All 16 durable submission sites
90
+ are recorded in the [execution inventory](storage-execution-boundaries.md).
91
+ - M69 is an Async Client and M35 a concrete Storage backend in B4.
92
+ Existing `Storage::Backends` is not collapsed.
93
+ - See the [public API and transaction rules](../migrations/storage-async-client.md).
94
+
95
+ ### ContentStore service ownership
96
+
97
+ ContentStore is a content management service using Storage, not a peer physical
98
+ backend. Its API, canonicalization, StoredContents implementation and owned
99
+ StorageSchema live in `content_store/`. Base remains shared by StoredContents
100
+ and the Persistence exception facade. The unnecessary `backends/` subdivision
101
+ and Zeitwerk collapse are removed without a forwarding file or alias.
102
+
103
+ M10 belongs to G57 Content Service in the persistence column of display band B3.
104
+ M73 is retired, not reused. G47/G48 contain no ContentStore modules. The real
105
+ M10-to-Storage dependency remains in both the drawing and the full graph.
106
+ The boundary gate rejects reverse Storage-to-ContentStore dependencies and any
107
+ service path to a physical backend, Engine, client or domain orchestration.
108
+ Public constants, signatures, content IDs, exceptions and transaction behavior
109
+ remain unchanged. See [ADR-062](../decisions/062-content-store-service.md).
110
+
111
+ ## r8 unit 1 framework boundaries
112
+
113
+ G58/M76/M77 now owns the common Execution framework under `execution/`.
114
+ Engine owns FSM-specific values and the pool/timer/runtime binding, while
115
+ composition selects that binding. Tracing owns explicit observation. Context
116
+ and Tool now own their shared operations, not merely value/error declarations.
117
+ The former G59/M78 split is retired. See [r8 unit 1](r8-unit1.md) for the exact
118
+ implemented scope, API removals and incomplete Persistence/domain work.
119
+
120
+ The full architecture still contains existing cycles outside the checked scope.
121
+ The recorded graph is not an ideal final r8 diagram.
122
+
123
+ ## Pending phase
124
+
125
+ P5 completes the final cross-repository/API/docs review after application.
126
+ All retained P2-P4 responsibilities exist in source; the unnecessary M73 split
127
+ is superseded by G57 service ownership. The measured SVG still comes from
128
+ actual Ruby analysis; the target concept is a separate explanatory artifact.
129
+
130
+ ## Regenerate and review
131
+
132
+ See [tool instructions](../../tools/architecture/README.md). The normal command
133
+ reads `tools/architecture/phase.json`:
134
+
135
+ ```sh
136
+ python tools/architecture/refresh_diagram.py . tmp/architecture
137
+ ```
138
+
139
+ The output includes the matrix, source hashes, boundary report, module/file
140
+ cycles and the delta from baseline 84668606. Source/module/group changes require
141
+ matching annotation updates. No up/down rule or coordinate controls the gate.
142
+ Ruby loader, RBS, cancellation, admission, streaming and configuration tests
143
+ remain necessary: static analysis does not replace them.
144
+
145
+ An unpublished local candidate must use `--candidate`. Its SVG states that it
146
+ is unapplied and disables GitHub source links. Applied commits and CI output
147
+ use actual source links. CI artifacts are generated after the source commit;
148
+ there is no self-referential SVG commit hash or automatic commit/push.
149
+
150
+ Text panels are transparent and module outlines/group colors remain intact.
151
+ Arrows into M33/M41/M44 are hidden by presentation settings; their AST edges,
152
+ source evidence and matrix cells are preserved. Boundary rules inspect all edges.
@@ -28,13 +28,19 @@ candidates are accumulated in hook order.
28
28
 
29
29
  ## 3. Input and result
30
30
 
31
- Hooks receive immutable `Phronomy::Agent::LLMInputBuildContext` metadata. They do
31
+ Hooks receive immutable `Phronomy::Context::LLMInputBuildContext` metadata. They do
32
32
  not receive a mutable Provider chat/message array.
33
33
 
34
- A hook returns `Phronomy::Agent::LLMInputPatch` or `nil`.
34
+ A hook returns `Phronomy::Context::LLMInputPatch` or `nil`.
35
+
36
+ These two types live in the shared `agent/context_contract/` implementation
37
+ directory together with the Policy input and result contracts. Zeitwerk
38
+ collapses the directory, preserving their existing public `Phronomy::Agent`
39
+ names. Hook registration, invocation, and result handling are unchanged; see
40
+ [ADR-036](../decisions/036-context-contract-ownership.md).
35
41
 
36
42
  ```ruby
37
- Phronomy::Agent::LLMInputPatch.new(
43
+ Phronomy::Context::LLMInputPatch.new(
38
44
  model_config_patch: {temperature: 0.2},
39
45
  segment_candidates: [
40
46
  {
@@ -50,12 +50,42 @@ RubyLLMMaterializer
50
50
  The normative Journal/Manifest split is
51
51
  [ADR-012](../decisions/012-canonical-execution-log-and-context-policy.md).
52
52
 
53
+ ### Contract ownership and physical layout
54
+
55
+ The contracts shared by Agent execution, Context policies, and input hooks live
56
+ in `lib/phronomy/agent/context_contract/`:
57
+
58
+ | Files | Responsibility |
59
+ |---|---|
60
+ | `context_policy.rb`, `context_policy_input.rb` | Policy protocol, helper methods, and typed input material |
61
+ | `context_plan.rb`, `context_plan_validator.rb` | Policy result and its validity rules |
62
+ | `llm_input_manifest.rb` | Final logical input representation and its format validation |
63
+ | `llm_input_build_context.rb`, `llm_input_patch.rb` | Input metadata and returned customization for hooks |
64
+
65
+ Zeitwerk collapses this directory. The Ruby constants keep their existing
66
+ `Phronomy::Agent::*` names; there is no `ContextContract` namespace or new
67
+ registration mechanism. Applications continue to load `phronomy` and use the
68
+ documented constants. The previous individual implementation file paths are
69
+ not retained as forwarding files.
70
+
71
+ `ContextPolicies::Default` remains a concrete strategy, while
72
+ `ContextAssembler`, input builders, candidate resolution, Runtime connections,
73
+ and persistence transactions remain on the execution side. The Manifest
74
+ validates and converts record values; it does not write to a storage backend.
75
+ Its format version, keys, and validation rules are unchanged.
76
+
77
+ This ownership decision is defined by
78
+ [ADR-036](../decisions/036-context-contract-ownership.md). Physical containment
79
+ under `agent/` does not assign the contracts to the execution layer. The
80
+ concrete policies and the shared contracts can occupy distinct groups at the
81
+ same abstraction level.
82
+
53
83
  ## 2. Public Context Policy SPI
54
84
 
55
85
  Application code supplies an ordinary reusable Ruby strategy object:
56
86
 
57
87
  ```ruby
58
- class SearchContextPolicy < Phronomy::Agent::ContextPolicy
88
+ class SearchContextPolicy < Phronomy::Context::ContextPolicy
59
89
  def initialize(search:)
60
90
  @search = search
61
91
  end
@@ -0,0 +1,90 @@
1
+ # Context preparation steps
2
+
3
+ ## Role and scope
4
+
5
+ ContextAssembler prepares the immutable semantic input supplied to Application
6
+ ContextPolicy and later validates and stores its decision as an LLMInputManifest.
7
+ InitialPreparation and DispatchPreparation run preparation outside their commit
8
+ transactions, recheck durable state, then call finalize in the commit transaction.
9
+ The public boundary and those callers already have separate responsibilities.
10
+
11
+ R07 addresses a narrower readability problem inside prepare_initial and
12
+ prepare_followup: orchestration was mixed with individual item IDs, provenance
13
+ and metadata, and record filtering plus Hook/Handoff merging were duplicated.
14
+ The original proposal calls for private extraction before adding collaborators.
15
+ Refactor 40 follows that limit: one production file changes; no class is added.
16
+
17
+ ## Reading order and responsibilities
18
+
19
+ | Private operation | Responsibility |
20
+ |---|---|
21
+ | initial_instruction_items | Choose the initial Agent instruction and optional Handoff responsibility, in order. |
22
+ | agent_instruction_item | Build the required system instruction with its stable ID, estimate and Agent provenance. |
23
+ | handoff_instruction_item | Build the required user responsibility with its ID, estimate and Handoff origin. |
24
+ | retained_instruction_items | Read only retained base instructions from the previous Manifest, using the existing compatibility predicate and converter. |
25
+ | resolve_record_candidates | Filter working records by eligibility/current generation and resolve them with Journal candidates and caller-provided exclusions. |
26
+ | merge_context_candidates | Add Hook candidates, then Handoff candidates, using the existing creation, sorting and storage operations. |
27
+ | current_input_item | Build the initial required ask argument after all candidates, including ID, sequence, provenance and metadata. |
28
+
29
+ Public preparation keeps model/Tool settings, source loading and the sequence of
30
+ these operations visible. Record collection and candidate augmentation stay as
31
+ two steps: follow-up computes its next call sequence after record resolution,
32
+ exactly as before. No new options object, pipeline DSL, mutable preparation state
33
+ or cross-feature factory is introduced. Helpers remain private to the assembler.
34
+
35
+ ## Preserved contracts
36
+
37
+ - Initial instructions come from the Agent configuration plus optional Handoff
38
+ responsibility. Follow-up instructions come from the retained base Manifest.
39
+ Current Hook instructions are rebuilt; previous Hook/Policy-generated items
40
+ do not become permanent base instructions. Legacy origin handling is unchanged.
41
+ - Working records must remain context candidates in the active transcript
42
+ generation. Initial preparation excludes the current input record; follow-up
43
+ includes eligible working records without that exclusion.
44
+ - Record candidates are resolved before Hook and then Handoff augmentation. The
45
+ current-input sequence follows all candidates, including non-conversation ones.
46
+ IDs, source fields, trust metadata, estimates, required flags, ordering, frozen
47
+ values and delivery modes retain their existing values.
48
+ - Initial input content is loaded before instruction-item construction and
49
+ candidate resolution. Initial instruction callbacks and patch normalization
50
+ retain their order. Hook content writes still precede Handoff type validation;
51
+ moving that validation earlier would be a behavior change and is not included.
52
+ - Preparation is not side-effect-free: Hook/Handoff content may be stored before
53
+ Policy succeeds. This extraction neither adds rollback nor suppresses existing
54
+ exceptions. Missing-input, reserved-metadata, resolver and Policy failures keep
55
+ their ordering; collaborator exception objects propagate unchanged.
56
+ - Application Policy executes once during prepare; finalize does not invoke it.
57
+ Finalization, its validation/encoding/store helpers, assembly policy version 8,
58
+ schemas, public signatures and transaction-owning callers are unchanged.
59
+
60
+ The new helpers use the same constructor field evaluation and call ordering.
61
+ They do not cache or deduplicate reads, normalize values earlier, or move work
62
+ across the prepare/finalize boundary merely to shorten the source.
63
+
64
+ ## Verification and trade-off
65
+
66
+ Ten behavior examples cover initial item provenance/immutability, generation and
67
+ exclusion rules, content-write order, absent instructions/Handoff, follow-up
68
+ retention and current hooks, the prepare/finalize boundary and failure precedence.
69
+ The same examples pass on Refactor 39. Four independent baseline/candidate
70
+ scenario pairs compare complete Policy input, Prepared values, Manifest bytes and
71
+ content references, plus preparation-time content read/write order. The pairs
72
+ cover initial and follow-up calls with/without base instructions and Handoff.
73
+
74
+ Full core/integration suites, common examples and real SQLite, stable/beta API
75
+ and Storage SPI snapshots, RBS, style, annotations and isolated gem checks are
76
+ also run. Live PostgreSQL, live LLM, remote CI and performance are not exercised
77
+ for this candidate; prior CI is not evidence for the candidate's core.
78
+
79
+ prepare_initial is 103 -> 55 lines and prepare_followup 57 -> 47. The whole file
80
+ is 625 -> 672 lines because seven named private methods and explicit arguments
81
+ add structure. This is a readability and duplicated-procedure improvement, not a
82
+ net line-count reduction or a directory-cycle change. The methods' names give
83
+ readers a place to choose whether they need the detailed representation.
84
+
85
+ R07 was independently verified after application at core b3dfbc5a, tree
86
+ dcd9d1bf8efb1b488f9c2b3b1c4e99bdeda6096c. All six files and the full tree matched;
87
+ core/integration/examples/SQLite/API/type/gem and four paired scenarios passed.
88
+ R07 is complete. The published diagram is applied40-01. R08/D08 now has a Refactor
89
+ 41 candidate, with R09 and R10 remaining afterward. Their behavior and naming
90
+ decisions are not part of Context preparation extraction.