phronomy 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +4 -0
  4. data/VERIFY.sh +27 -27
  5. data/benchmark/bench_agent_invoke.rb +26 -22
  6. data/benchmark/bench_context_assembler.rb +4 -5
  7. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  8. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  9. data/docs/architecture/agent-context.md +4 -2
  10. data/docs/architecture/agent-transition-ownership.md +89 -0
  11. data/docs/architecture/before-llm-input.md +6 -0
  12. data/docs/architecture/context-management.md +30 -0
  13. data/docs/architecture/context-preparation-steps.md +90 -0
  14. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  15. data/docs/architecture/execution-metadata-and-values.md +80 -0
  16. data/docs/architecture/generator-verifier-ownership.md +111 -0
  17. data/docs/architecture/multi-agent-handoff.md +8 -2
  18. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  19. data/docs/architecture/persistence.md +27 -4
  20. data/docs/architecture/refactoring-closure.md +334 -0
  21. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  22. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  23. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  24. data/docs/architecture/tracing.md +1 -1
  25. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  26. data/docs/architecture.md +209 -0
  27. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  28. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  29. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  30. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  31. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  32. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  33. data/docs/decisions/032-storage-backend-composition.md +80 -0
  34. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  35. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  36. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  37. data/docs/decisions/036-context-contract-ownership.md +86 -0
  38. data/docs/decisions/037-common-definition-ownership.md +62 -0
  39. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  40. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  41. data/docs/decisions/040-configuration-default-composition.md +94 -0
  42. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  43. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  44. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  45. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  46. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  47. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  48. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  49. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  50. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  51. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  52. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  53. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  54. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  55. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  56. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  57. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  58. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  59. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  60. data/docs/decisions/README.md +35 -6
  61. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  62. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  63. data/docs/features.md +9 -3
  64. data/docs/getting-started.md +4 -4
  65. data/docs/migrations/0.15.md +5 -0
  66. data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
  67. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  68. data/docs/migrations/neutral-storage-spi.md +43 -0
  69. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  70. data/docs/migrations/shared-state-multi-agent.md +44 -0
  71. data/docs/migrations/storage-backend-composition.md +117 -0
  72. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  73. data/docs/persistence-backends.md +162 -204
  74. data/docs/runtime-and-concurrency.md +117 -32
  75. data/lib/phronomy/agent/api/agent.rb +17 -0
  76. data/lib/phronomy/agent/async_event_api.rb +2 -2
  77. data/lib/phronomy/agent/base.rb +51 -241
  78. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  79. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  80. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  81. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  82. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  83. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  84. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  85. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  86. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  87. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  88. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  89. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  90. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  91. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  92. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  93. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  94. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  95. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
  96. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  97. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  98. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
  99. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  100. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  101. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  102. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  103. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  104. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  105. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  106. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  107. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  108. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
  109. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  110. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  111. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  112. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  113. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  114. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  115. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  116. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  117. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  118. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  119. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  120. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  121. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  122. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  123. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  124. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  125. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  126. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  127. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  128. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  129. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  130. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  131. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  132. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  133. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  134. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  135. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  136. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
  137. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  138. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
  139. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  140. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  141. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  142. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  143. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  144. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  145. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  146. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  147. data/lib/phronomy/common/configuration_error.rb +7 -0
  148. data/lib/phronomy/common/error.rb +5 -0
  149. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  150. data/lib/phronomy/common/values/serializable.rb +32 -0
  151. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  152. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  153. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  154. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  155. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  156. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
  157. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  158. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  159. data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
  160. data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
  161. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  162. data/lib/phronomy/engine/event_loop.rb +158 -624
  163. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  164. data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
  165. data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
  166. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  167. data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
  168. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  169. data/lib/phronomy/engine/fsm_session.rb +35 -28
  170. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
  171. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
  172. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
  173. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  174. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  175. data/lib/phronomy/engine/runtime.rb +80 -84
  176. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  177. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  178. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  179. data/lib/phronomy/engine/task_result.rb +4 -0
  180. data/lib/phronomy/engine/timeout_error.rb +7 -0
  181. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  182. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  183. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  184. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  185. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  186. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  187. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  188. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  189. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  190. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  191. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  192. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  193. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  194. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  195. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
  196. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  197. data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
  198. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  199. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  200. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  201. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  202. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  203. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  204. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  205. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  206. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  207. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  208. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  209. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  210. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  211. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  212. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  213. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  214. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  215. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  216. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  217. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  218. data/lib/phronomy/storage/backend.rb +101 -0
  219. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  220. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  221. data/lib/phronomy/storage/blobs.rb +31 -0
  222. data/lib/phronomy/storage/condition.rb +25 -0
  223. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  224. data/lib/phronomy/storage/conflict_error.rb +9 -0
  225. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  226. data/lib/phronomy/storage/entry.rb +33 -0
  227. data/lib/phronomy/storage/guard_ref.rb +13 -0
  228. data/lib/phronomy/storage/not_found_error.rb +9 -0
  229. data/lib/phronomy/storage/record_codec.rb +177 -0
  230. data/lib/phronomy/storage/records.rb +61 -0
  231. data/lib/phronomy/storage/resource.rb +126 -0
  232. data/lib/phronomy/storage/scope.rb +25 -0
  233. data/lib/phronomy/storage/serialization_error.rb +9 -0
  234. data/lib/phronomy/storage/streams.rb +44 -0
  235. data/lib/phronomy/storage/transaction_error.rb +10 -0
  236. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  237. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  238. data/lib/phronomy/storage/validation.rb +53 -0
  239. data/lib/phronomy/storage/view.rb +131 -0
  240. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  241. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  242. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  243. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  244. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  245. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  246. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  247. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  248. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  249. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  250. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  251. data/lib/phronomy/tools/agent.rb +1 -1
  252. data/lib/phronomy/version.rb +1 -1
  253. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
  254. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  255. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  256. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  257. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
  258. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  259. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  260. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  261. data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
  262. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  263. data/lib/phronomy.rb +52 -94
  264. data/scripts/api_snapshot.rb +1 -1
  265. data/scripts/storage_spi_snapshot.rb +36 -0
  266. data/sig/phronomy/agent.rbs +0 -1
  267. data/sig/phronomy/execution_receiver.rbs +34 -0
  268. data/sig/phronomy/handoff.rbs +4 -2
  269. data/sig/phronomy/multi_agent.rbs +18 -0
  270. data/sig/phronomy/persistence.rbs +7 -91
  271. data/sig/phronomy/storage.rbs +174 -0
  272. data/sig/phronomy/tool.rbs +10 -1
  273. metadata +230 -99
  274. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  275. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  276. data/lib/phronomy/agent/recovery_support.rb +0 -504
  277. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  278. data/lib/phronomy/agent/tool_executor.rb +0 -55
  279. data/lib/phronomy/generator_verifier.rb +0 -369
  280. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  281. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  282. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  283. data/lib/phronomy/persistence/in_memory.rb +0 -690
  284. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  285. data/lib/phronomy/persistence.rb +0 -276
  286. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  287. data/lib/phronomy/workflow_recovery.rb +0 -123
  288. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  289. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  290. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  291. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  292. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  293. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  294. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  295. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  296. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  297. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  298. /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
  299. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  300. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  301. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  302. /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
  303. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  304. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Compare-and-write records and declared equality indexes.
6
+ # @api public
7
+ class Records
8
+ UNCHECKED = Object.new.freeze
9
+ attr_reader :resource
10
+ def initialize(view, resource) = (@view, @resource = view, resource)
11
+
12
+ # @api public
13
+ def insert(key:, revision:, attributes:, record:)
14
+ @view.validate!
15
+ entry = Entry::Record.new(key: key, revision: revision, attributes: resource.validate_attributes(attributes), record: record)
16
+ @view.perform(:insert_record, resource, entry: entry)
17
+ end
18
+
19
+ # @api public
20
+ def read(key)
21
+ @view.validate!
22
+ @view.perform(:read_record, resource, key: Validation.key(key))
23
+ end
24
+
25
+ # @api public
26
+ def fetch(key)
27
+ read(key) || raise(NotFoundError, "record not found: #{resource.id}/#{key}")
28
+ end
29
+
30
+ # @api public
31
+ def replace(key:, expected_revision:, next_revision:, attributes:, record:, expected_attributes: {})
32
+ @view.validate!
33
+ expected = Validation.revision(expected_revision)
34
+ entry = Entry::Record.new(key: key, revision: next_revision, attributes: resource.validate_attributes(attributes), record: record)
35
+ raise ConflictError, "replace must advance revision exactly once" unless next_revision == expected + 1
36
+ expected_values = resource.validate_attributes(expected_attributes, partial: true)
37
+ @view.perform(:replace_record, resource, entry: entry, expected_revision: expected, expected_attributes: expected_values)
38
+ end
39
+
40
+ # @api public
41
+ def delete(key:, expected_revision: UNCHECKED)
42
+ @view.validate!
43
+ expected = expected_revision.equal?(UNCHECKED) ? UNCHECKED : Validation.revision(expected_revision)
44
+ @view.perform(:delete_record, resource, key: Validation.key(key), expected_revision: expected)
45
+ end
46
+
47
+ # @api public
48
+ def scan(index:, equals:, after: nil, limit: nil)
49
+ @view.validate!
50
+ values = resource.index_values(index, equals)
51
+ @view.perform(:scan_records, resource, equals: values, after: after.nil? ? nil : Validation.key(after), limit: Validation.limit(limit))
52
+ end
53
+
54
+ # @api public
55
+ def delete_matching(index:, equals:)
56
+ @view.validate!
57
+ @view.perform(:delete_matching_records, resource, equals: resource.index_values(index, equals))
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Immutable logical schema. Physical drivers receive a separate mapping.
6
+ # @api public
7
+ class Resource
8
+ TYPES = [:string, :integer, :boolean, :nullable_string, :nullable_integer, :nullable_boolean].freeze
9
+ attr_reader :id, :kind, :attributes, :immutable_attributes, :indexes, :unique, :guard
10
+
11
+ # Normalize a schema object or ID before constructing guards and conditions.
12
+ # @api private
13
+ def self.normalize_reference(value)
14
+ value.is_a?(Resource) ? value : Validation.key(value)
15
+ end
16
+
17
+ # @api public
18
+ def initialize(id:, kind:, attributes: {}, immutable_attributes: [], indexes: {}, unique: [], guard: nil)
19
+ @id = Validation.key(id)
20
+ @kind = kind
21
+ @attributes = Validation.immutable(attributes)
22
+ @immutable_attributes = Validation.immutable(immutable_attributes)
23
+ @indexes = Validation.immutable(indexes)
24
+ @unique = Validation.immutable(unique)
25
+ @guard = Validation.immutable(guard)
26
+ validate_shape!
27
+ validate_indexes!
28
+ validate_guard!
29
+ validate_unique!
30
+ freeze
31
+ end
32
+
33
+ # @api public
34
+ def validate_attributes(values, partial: false)
35
+ valid = values.is_a?(Hash) && (values.keys - attributes.keys).empty? && (partial || values.keys.sort == attributes.keys.sort)
36
+ unless valid
37
+ raise ArgumentError, "attributes do not match #{id}"
38
+ end
39
+ values = values.to_h do |name, value|
40
+ type = attributes.fetch(name)
41
+ if type.to_s.start_with?("nullable_") && value.nil?
42
+ [name, nil]
43
+ else
44
+ [name, typed_value(name, type, value)]
45
+ end
46
+ end
47
+ if guard && values.key?(guard[:via])
48
+ values[guard[:via]] = Validation.key(values.fetch(guard[:via]))
49
+ end
50
+ Validation.attributes(values)
51
+ end
52
+
53
+ # @api public
54
+ def index_values(index, equals)
55
+ fields = indexes.fetch(index) { raise ArgumentError, "unknown index: #{index}" }
56
+ raise ArgumentError, "index attributes must match exactly" unless equals.is_a?(Hash) && equals.keys.sort == fields.sort
57
+ validate_attributes(equals, partial: true)
58
+ end
59
+
60
+ private
61
+
62
+ def validate_shape!
63
+ raise ArgumentError, "unsupported resource kind" unless [:records, :streams, :blobs].include?(kind)
64
+ unless attributes.is_a?(Hash) && attributes.keys.all? { |key| key.is_a?(Symbol) } && attributes.values.all? { |type| TYPES.include?(type) }
65
+ raise ArgumentError, "invalid attribute declarations"
66
+ end
67
+ unless field_list?(immutable_attributes, empty: true) && indexes.is_a?(Hash) && unique.is_a?(Array)
68
+ raise ArgumentError, "invalid resource declarations"
69
+ end
70
+ if kind != :records && (!immutable_attributes.empty? || !indexes.empty? || !unique.empty?)
71
+ raise ArgumentError, "only records declare indexes, uniqueness and immutable attributes"
72
+ end
73
+ raise ArgumentError, "streams have no indexed attributes" if kind == :streams && !attributes.empty?
74
+ end
75
+
76
+ def validate_indexes!
77
+ unless indexes.all? { |name, fields| name.is_a?(Symbol) && field_list?(fields) }
78
+ raise ArgumentError, "indexes need names and known distinct fields"
79
+ end
80
+ end
81
+
82
+ def validate_unique!
83
+ names = unique.map do |constraint|
84
+ unless constraint.is_a?(Hash) && constraint.keys.sort == [:fields, :name, :where] &&
85
+ constraint[:name].is_a?(Symbol) && field_list?(constraint[:fields])
86
+ raise ArgumentError, "invalid unique constraint"
87
+ end
88
+ validate_attributes(constraint.fetch(:where), partial: true)
89
+ constraint[:name]
90
+ end
91
+ raise ArgumentError, "unique constraints need distinct names" unless names.uniq == names
92
+ end
93
+
94
+ def validate_guard!
95
+ return unless guard
96
+ unless guard.is_a?(Hash) && guard.keys.sort == [:resource, :via] && guard[:resource].is_a?(String)
97
+ raise ArgumentError, "invalid guard declaration"
98
+ end
99
+ Validation.key(guard[:resource])
100
+ allowed = case kind
101
+ when :records then [:key, *attributes.select { |_, type| type == :string }.keys]
102
+ when :streams then [:stream]
103
+ else []
104
+ end
105
+ raise ArgumentError, "invalid guard key" unless allowed.include?(guard[:via])
106
+ if kind == :records && guard[:via] != :key && !immutable_attributes.include?(guard[:via])
107
+ raise ArgumentError, "guard attributes must be immutable"
108
+ end
109
+ end
110
+
111
+ def field_list?(values, empty: false)
112
+ values.is_a?(Array) && (empty || !values.empty?) && values.uniq == values && (values - attributes.keys).empty?
113
+ end
114
+
115
+ def typed_value(name, type, value)
116
+ valid = case type.to_s.delete_prefix("nullable_")
117
+ when "string" then value.is_a?(String) && value.valid_encoding? && !value.include?("\0")
118
+ when "integer" then value.is_a?(Integer)
119
+ when "boolean" then value.equal?(true) || value.equal?(false)
120
+ end
121
+ raise ArgumentError, "invalid #{id}.#{name}" unless valid
122
+ value.is_a?(String) ? value.encode(Encoding::UTF_8) : value
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # One synchronous transaction lifetime, independent of physical rollback.
6
+ # @api private
7
+ class Scope
8
+ def initialize
9
+ @thread = Thread.current
10
+ @active = true
11
+ @failed = false
12
+ end
13
+
14
+ def check!
15
+ raise TransactionError, "transaction belongs to another execution context" unless @thread.equal?(Thread.current)
16
+ raise TransactionError, "transaction scope is closed" unless @active
17
+ raise TransactionError, "transaction scope has failed; leave it before continuing" if @failed
18
+ true
19
+ end
20
+
21
+ def fail! = @failed = true
22
+ def close! = @active = false
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A value cannot be represented by the storage record contract.
6
+ # @api public
7
+ class SerializationError < Phronomy::Error; end
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Atomic ordered append with a per-stream head and entry identity.
6
+ # @api public
7
+ class Streams
8
+ attr_reader :resource
9
+ def initialize(view, resource) = (@view, @resource = view, resource)
10
+
11
+ # @api public
12
+ def append(stream:, expected_head:, entries:)
13
+ @view.validate!
14
+ key = Validation.key(stream)
15
+ expected = Validation.revision(expected_head)
16
+ raise ArgumentError, "entries must be an Array" unless entries.is_a?(Array)
17
+ values = entries.map do |entry|
18
+ raise SerializationError, "expected Entry::Append" unless entry.is_a?(Entry::Append)
19
+ Entry::Append.new(id: entry.id, record: entry.record)
20
+ end
21
+ raise ConflictError, "duplicate stream entry identity" unless values.map(&:id).uniq.length == values.length
22
+ @view.perform(:append_stream, resource, stream: key, expected_head: expected, entries: values.freeze)
23
+ end
24
+
25
+ # @api public
26
+ def read(stream:, after: 0, limit: nil)
27
+ @view.validate!
28
+ @view.perform(:read_stream, resource, stream: Validation.key(stream), after: Validation.revision(after), limit: Validation.limit(limit))
29
+ end
30
+
31
+ # @api public
32
+ def head(stream:)
33
+ @view.validate!
34
+ @view.perform(:stream_head, resource, stream: Validation.key(stream))
35
+ end
36
+
37
+ # @api public
38
+ def delete(stream:)
39
+ @view.validate!
40
+ @view.perform(:delete_stream, resource, stream: Validation.key(stream))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A neutral storage contract failure.
6
+ # @api public
7
+ class TransactionError < Phronomy::Error
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A neutral storage contract failure.
6
+ # @api public
7
+ class UniqueConstraintError < ConflictError
8
+ attr_reader :resource, :constraint
9
+ # @api public
10
+ def initialize(resource:, constraint:)
11
+ @resource = resource.id
12
+ @constraint = constraint
13
+ super("unique constraint #{resource.id}.#{constraint} failed")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A backend does not provide a required storage capability.
6
+ # @api public
7
+ class UnsupportedBackendError < Phronomy::Error; end
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Input validation shared by storage handles; no physical work happens here.
6
+ # @api private
7
+ module Validation
8
+ module_function
9
+
10
+ def key(value)
11
+ raise ArgumentError, "key must be a non-empty String" unless value.is_a?(String) && !value.empty?
12
+ result = value.encode(Encoding::UTF_8)
13
+ raise ArgumentError, "key must be valid UTF-8" unless result.valid_encoding? && !result.include?("\0")
14
+ result.dup.freeze
15
+ rescue EncodingError
16
+ raise ArgumentError, "key must be valid UTF-8"
17
+ end
18
+
19
+ def revision(value)
20
+ raise ArgumentError, "revision must be a non-negative Integer" unless value.is_a?(Integer) && value >= 0
21
+ value
22
+ end
23
+
24
+ def limit(value)
25
+ return nil if value.nil?
26
+ raise ArgumentError, "limit must be a positive Integer" unless value.is_a?(Integer) && value.positive?
27
+ value
28
+ end
29
+
30
+ def record(value)
31
+ raise SerializationError, "expected Storage::DurableRecord" unless value.is_a?(DurableRecord)
32
+ value.copy
33
+ end
34
+
35
+ def attributes(value)
36
+ unless value.is_a?(Hash) && value.all? { |name, item| name.is_a?(Symbol) && [String, Integer, TrueClass, FalseClass, NilClass].any? { |type| item.is_a?(type) } }
37
+ raise ArgumentError, "attributes must contain named scalar values"
38
+ end
39
+ immutable(value)
40
+ end
41
+
42
+ def immutable(value)
43
+ case value
44
+ when Hash then value.to_h { |k, v| [immutable(k), immutable(v)] }.freeze
45
+ when Array then value.map { |v| immutable(v) }.freeze
46
+ when String then value.dup.freeze
47
+ when Symbol, Integer, TrueClass, FalseClass, NilClass then value
48
+ else raise ArgumentError, "declarations must contain immutable scalar values"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Root handles route to the current scope; bound handles cannot outlive it.
6
+ # @api public
7
+ class View
8
+ def initialize(backend, scope: nil, context: nil)
9
+ @backend, @scope, @context = backend, scope, context
10
+ @handles = {}
11
+ end
12
+
13
+ # @api public
14
+ def records(resource) = handle(resource, :records, Records)
15
+ # @api public
16
+ def streams(resource) = handle(resource, :streams, Streams)
17
+ # @api public
18
+ def blobs(resource) = handle(resource, :blobs, Blobs)
19
+
20
+ # Acquire stable anchors before evaluating conditions in the same view.
21
+ # @api public
22
+ def check!(guards:, conditions:)
23
+ validate!
24
+ raise ArgumentError, "guards and conditions must be arrays" unless guards.is_a?(Array) && conditions.is_a?(Array)
25
+ guards.each do |guard|
26
+ raise ArgumentError, "expected GuardRef" unless guard.is_a?(GuardRef)
27
+ resolve(guard.resource, :records)
28
+ end
29
+ conditions.each do |condition|
30
+ case condition
31
+ when Condition::RevisionIs then resolve(condition.resource, :records)
32
+ when Condition::StreamHeadIs then resolve(condition.resource, :streams)
33
+ when Condition::NoRows then resolve(condition.resource, :records).index_values(condition.index, condition.equals)
34
+ else raise ArgumentError, "unsupported condition"
35
+ end
36
+ require_guard!(condition, guards)
37
+ end
38
+ atomic do |bound|
39
+ guards.uniq.sort_by { |g| [resolve(g.resource, :records).id.b, g.key.b] }.each do |guard|
40
+ bound.perform(:lock_guard, resolve(guard.resource, :records), key: guard.key)
41
+ end
42
+ conditions.each do |condition|
43
+ matched = case condition
44
+ when Condition::RevisionIs then bound.records(condition.resource).read(condition.key)&.revision == condition.expected
45
+ when Condition::StreamHeadIs then bound.streams(condition.resource).head(stream: condition.stream) == condition.expected
46
+ when Condition::NoRows then bound.records(condition.resource).scan(index: condition.index, equals: condition.equals, limit: 1).empty?
47
+ end
48
+ raise ConditionFailedError, condition unless matched
49
+ end
50
+ true
51
+ end
52
+ end
53
+
54
+ # Keeps domain encoding/response validation inside the physical transaction.
55
+ # @api private
56
+ def atomic
57
+ validate!
58
+ unless @scope
59
+ current = @backend.current_view
60
+ return current.atomic { |bound| yield bound } if current
61
+ return @backend.transaction { |bound| bound.atomic { yield bound } }
62
+ end
63
+ completed = false
64
+ begin
65
+ result = yield self
66
+ @scope.check!
67
+ completed = true
68
+ result
69
+ ensure
70
+ @scope.fail! unless completed
71
+ end
72
+ end
73
+
74
+ # @api private
75
+ def perform(operation, resource, **arguments)
76
+ atomic { |bound| bound.dispatch(operation, resource, **arguments) }
77
+ end
78
+
79
+ # @api private
80
+ def dispatch(operation, resource, **arguments)
81
+ validate!
82
+ @backend.execute(@context, operation, resource, **arguments)
83
+ end
84
+
85
+ # @api private
86
+ def validate!
87
+ @scope&.check!
88
+ true
89
+ end
90
+
91
+ private
92
+
93
+ def require_guard!(condition, guards)
94
+ resource = @backend.resources.fetch(condition.resource.is_a?(Resource) ? condition.resource.id : condition.resource)
95
+ guard = resource.guard
96
+ return unless guard
97
+ key = case condition
98
+ when Condition::RevisionIs
99
+ if guard[:via] == :key
100
+ condition.key
101
+ else
102
+ raise ArgumentError, "revision checks require a key-based guard"
103
+ end
104
+ when Condition::StreamHeadIs then condition.stream
105
+ when Condition::NoRows
106
+ condition.equals.fetch(guard[:via]) { raise ArgumentError, "condition must constrain its guard attribute" }
107
+ end
108
+ covered = guards.any? do |candidate|
109
+ resolve(candidate.resource, :records).id == guard[:resource] && candidate.key == key
110
+ end
111
+ raise ArgumentError, "condition requires its declared guard" unless covered
112
+ end
113
+
114
+ def resolve(resource, kind)
115
+ id = resource.is_a?(Resource) ? resource.id : resource
116
+ registered = @backend.resources[id]
117
+ valid = registered && registered.kind == kind && (!resource.is_a?(Resource) || registered.equal?(resource))
118
+ unless valid
119
+ raise UnsupportedBackendError, "unregistered or mismatched #{kind} resource: #{id}"
120
+ end
121
+ registered
122
+ end
123
+
124
+ def handle(resource, kind, type)
125
+ validate!
126
+ registered = resolve(resource, kind)
127
+ @handles[registered.id] ||= type.new(self, registered)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -21,7 +21,7 @@ RSpec.shared_examples "a persistence content store" do
21
21
  it "raises NotFoundError for a missing content_id" do
22
22
  expect do
23
23
  content_store.fetch("sha256:#{"0" * 64}")
24
- end.to raise_error(Phronomy::Persistence::NotFoundError)
24
+ end.to raise_error(Phronomy::Storage::NotFoundError)
25
25
  end
26
26
 
27
27
  it "isolates durable bytes from mutation of fetched values" do
@@ -51,7 +51,7 @@ RSpec.shared_examples "a Journal repository" do
51
51
  expected_position: 1,
52
52
  records: [record]
53
53
  )
54
- end.to raise_error(Phronomy::Persistence::ConflictError)
54
+ end.to raise_error(Phronomy::Storage::ConflictError)
55
55
  end
56
56
 
57
57
  it "rejects a duplicate record_id already present in the Journal" do
@@ -76,7 +76,7 @@ RSpec.shared_examples "a Journal repository" do
76
76
  expected_position: 1,
77
77
  records: [duplicate]
78
78
  )
79
- end.to raise_error(Phronomy::Persistence::ConflictError)
79
+ end.to raise_error(Phronomy::Storage::ConflictError)
80
80
  end
81
81
 
82
82
  it "rejects duplicate record_ids within one append" do
@@ -94,7 +94,7 @@ RSpec.shared_examples "a Journal repository" do
94
94
  expected_position: 0,
95
95
  records: records
96
96
  )
97
- end.to raise_error(Phronomy::Persistence::ConflictError)
97
+ end.to raise_error(Phronomy::Storage::ConflictError)
98
98
  end
99
99
 
100
100
  it "rejects records belonging to another Agent" do
@@ -106,7 +106,7 @@ RSpec.shared_examples "a Journal repository" do
106
106
  expected_position: 0,
107
107
  records: [record]
108
108
  )
109
- end.to raise_error(Phronomy::Persistence::SerializationError)
109
+ end.to raise_error(Phronomy::Storage::SerializationError)
110
110
  end
111
111
 
112
112
  it "reads records in sequence order and supports after/limit" do
@@ -3,6 +3,9 @@
3
3
  require "securerandom"
4
4
 
5
5
  RSpec.shared_examples "a Persistence backend" do
6
+ include_examples "neutral storage primitives"
7
+ include_examples "storage transaction boundaries"
8
+
6
9
  let(:backend_agent_root) do
7
10
  Phronomy::Agent::AgentRoot.create(
8
11
  agent_id: "backend-agent-#{SecureRandom.uuid}",
@@ -23,8 +26,8 @@ RSpec.shared_examples "a Persistence backend" do
23
26
  end
24
27
 
25
28
  it "advertises every required capability" do
26
- Phronomy::Persistence::REQUIRED_CAPABILITIES.each do |name, required_value|
27
- expect(persistence.capabilities[name]).to eq(required_value)
29
+ Phronomy::Storage::Backend::REQUIRED_CAPABILITIES.each do |name, required_value|
30
+ expect(persistence.backend.capabilities[name]).to eq(required_value)
28
31
  end
29
32
  end
30
33
 
@@ -129,11 +132,11 @@ RSpec.shared_examples "a Persistence backend" do
129
132
  expect(persistence.contents.exist?(content_id)).to be(false)
130
133
  expect do
131
134
  persistence.agents.load(root.agent_id)
132
- end.to raise_error(Phronomy::Persistence::NotFoundError)
135
+ end.to raise_error(Phronomy::Storage::NotFoundError)
133
136
  expect(persistence.journals.head(root.agent_id)).to eq(0)
134
137
  expect do
135
138
  persistence.executions.load(execution.execution_id)
136
- end.to raise_error(Phronomy::Persistence::NotFoundError)
139
+ end.to raise_error(Phronomy::Storage::NotFoundError)
137
140
  expect(persistence.workflow_states.load(workflow_id)).to be_nil
138
141
  end
139
142
 
@@ -162,7 +165,7 @@ RSpec.shared_examples "a Persistence backend" do
162
165
  agent_revision: 0,
163
166
  journal_position: 0
164
167
  )
165
- end.to raise_error(Phronomy::Persistence::ConflictError)
168
+ end.to raise_error(Phronomy::Storage::ConflictError)
166
169
  end
167
170
 
168
171
  it "raises ConflictError when the durable Journal position has advanced" do
@@ -188,7 +191,7 @@ RSpec.shared_examples "a Persistence backend" do
188
191
  agent_revision: 0,
189
192
  journal_position: 0
190
193
  )
191
- end.to raise_error(Phronomy::Persistence::ConflictError)
194
+ end.to raise_error(Phronomy::Storage::ConflictError)
192
195
  end
193
196
 
194
197
  it "rolls back earlier writes when a watermark precondition fails" do
@@ -209,7 +212,7 @@ RSpec.shared_examples "a Persistence backend" do
209
212
  journal_position: 0
210
213
  )
211
214
  end
212
- end.to raise_error(Phronomy::Persistence::ConflictError)
215
+ end.to raise_error(Phronomy::Storage::ConflictError)
213
216
 
214
217
  expect(persistence.contents.exist?(temporary_content_id)).to be(false)
215
218
  end
@@ -54,7 +54,7 @@ RSpec.shared_examples "a workflow state repository" do
54
54
  expected_revision: nil,
55
55
  snapshot: {fields: {value: 2}, phase: "__end__"}
56
56
  )
57
- end.to raise_error(Phronomy::Persistence::ConflictError)
57
+ end.to raise_error(Phronomy::Storage::ConflictError)
58
58
  end
59
59
 
60
60
  it "returns a snapshot representation accepted by WorkflowRunner" do
@@ -111,7 +111,7 @@ RSpec.shared_examples "a workflow state repository" do
111
111
 
112
112
  expect do
113
113
  repository.delete(workflow_instance_id, expected_revision: 99)
114
- end.to raise_error(Phronomy::Persistence::ConflictError)
114
+ end.to raise_error(Phronomy::Storage::ConflictError)
115
115
 
116
116
  repository.delete(workflow_instance_id, expected_revision: 1)
117
117
  expect(repository.load(workflow_instance_id)).to be_nil
@@ -22,13 +22,13 @@ RSpec.shared_examples "an Agent repository" do
22
22
 
23
23
  expect do
24
24
  agent_repository.create(agent_root)
25
- end.to raise_error(Phronomy::Persistence::ConflictError)
25
+ end.to raise_error(Phronomy::Storage::ConflictError)
26
26
  end
27
27
 
28
28
  it "raises NotFoundError for a missing Agent" do
29
29
  expect do
30
30
  agent_repository.load("missing-#{SecureRandom.uuid}")
31
- end.to raise_error(Phronomy::Persistence::NotFoundError)
31
+ end.to raise_error(Phronomy::Storage::NotFoundError)
32
32
  end
33
33
 
34
34
  it "saves only at the expected revision" do
@@ -54,7 +54,7 @@ RSpec.shared_examples "an Agent repository" do
54
54
 
55
55
  expect do
56
56
  agent_repository.save(agent_root.agent_id, expected_revision: 0, root: stale)
57
- end.to raise_error(Phronomy::Persistence::ConflictError)
57
+ end.to raise_error(Phronomy::Storage::ConflictError)
58
58
  end
59
59
 
60
60
  it "rejects an Agent identity mismatch" do
@@ -71,7 +71,7 @@ RSpec.shared_examples "an Agent repository" do
71
71
  expected_revision: 0,
72
72
  root: other
73
73
  )
74
- end.to raise_error(Phronomy::Persistence::SerializationError)
74
+ end.to raise_error(Phronomy::Storage::SerializationError)
75
75
  end
76
76
 
77
77
  it "requires revision to advance exactly once" do
@@ -84,7 +84,7 @@ RSpec.shared_examples "an Agent repository" do
84
84
  expected_revision: 0,
85
85
  root: skipped
86
86
  )
87
- end.to raise_error(Phronomy::Persistence::ConflictError)
87
+ end.to raise_error(Phronomy::Storage::ConflictError)
88
88
  end
89
89
 
90
90
  it "deletes idempotently" do
@@ -94,6 +94,6 @@ RSpec.shared_examples "an Agent repository" do
94
94
 
95
95
  expect do
96
96
  agent_repository.load(agent_root.agent_id)
97
- end.to raise_error(Phronomy::Persistence::NotFoundError)
97
+ end.to raise_error(Phronomy::Storage::NotFoundError)
98
98
  end
99
99
  end