phronomy 0.22.0 → 0.24.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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +3 -4
  3. data/CHANGELOG.md +215 -10
  4. data/CONTRIBUTING.md +81 -9
  5. data/README.md +15 -6
  6. data/VERIFY.sh +587 -0
  7. data/benchmark/bench_agent_invoke.rb +2 -2
  8. data/benchmark/bench_context_assembler.rb +39 -68
  9. data/benchmark/bench_regression.rb +2 -2
  10. data/docs/architecture/agent-context.md +174 -0
  11. data/docs/architecture/before-llm-input.md +78 -0
  12. data/docs/architecture/context-management.md +232 -0
  13. data/docs/architecture/knowledge-and-rag.md +130 -0
  14. data/docs/architecture/multi-agent-handoff.md +147 -0
  15. data/docs/architecture/persistence.md +186 -0
  16. data/docs/architecture/removed/agent-context.md +72 -0
  17. data/docs/architecture/security-boundaries.md +173 -0
  18. data/docs/architecture/tracing.md +194 -0
  19. data/docs/architecture.md +89 -0
  20. data/docs/archive/design/archived/04_api_design.md +507 -0
  21. data/docs/archive/design/archived/09_guardrails.md +186 -0
  22. data/docs/archive/design/archived/17_rails_integration.md +175 -0
  23. data/docs/archive/design/historical/00_design_philosophy.md +122 -0
  24. data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
  25. data/docs/archive/design/historical/06_design_decisions.md +143 -0
  26. data/docs/changelog/0.14-and-earlier.md +1 -1
  27. data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
  28. data/docs/decisions/002-workflow-context-immutability.md +26 -1
  29. data/docs/decisions/006-no-built-in-guardrails.md +2 -1
  30. data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
  31. data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
  32. data/docs/decisions/016-semantic-multi-agent-handoff.md +114 -0
  33. data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
  34. data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
  35. data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
  36. data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
  37. data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
  38. data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
  39. data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
  40. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
  41. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
  42. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
  43. data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
  44. data/docs/decisions/028-preparing-recovery-replay-contract.md +106 -0
  45. data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +220 -0
  46. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +235 -0
  47. data/docs/decisions/031-durable-multi-agent-coordination.md +301 -0
  48. data/docs/decisions/README.md +176 -0
  49. data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +33 -0
  50. data/docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md +191 -0
  51. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +862 -0
  52. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +106 -0
  53. data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +179 -0
  54. data/docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md +302 -0
  55. data/docs/features.md +65 -11
  56. data/docs/getting-started.md +77 -45
  57. data/docs/migrations/0.19.md +14 -7
  58. data/docs/migrations/0.22.md +390 -0
  59. data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
  60. data/docs/persistence-backends.md +129 -40
  61. data/docs/runtime-and-concurrency.md +227 -33
  62. data/examples/README.md +13 -0
  63. data/lib/phronomy/agent/agent_execution.rb +21 -17
  64. data/lib/phronomy/agent/agent_invocation.rb +288 -93
  65. data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
  66. data/lib/phronomy/agent/agent_root.rb +3 -3
  67. data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
  68. data/lib/phronomy/agent/async_event_api.rb +162 -72
  69. data/lib/phronomy/agent/base.rb +416 -181
  70. data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
  71. data/lib/phronomy/agent/context_assembler.rb +437 -178
  72. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  73. data/lib/phronomy/agent/context_plan.rb +18 -13
  74. data/lib/phronomy/agent/context_plan_validator.rb +246 -88
  75. data/lib/phronomy/agent/context_policies/default.rb +123 -34
  76. data/lib/phronomy/agent/context_policy.rb +109 -3
  77. data/lib/phronomy/agent/context_policy_input.rb +244 -0
  78. data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
  79. data/lib/phronomy/agent/exact_execution.rb +153 -0
  80. data/lib/phronomy/agent/execution_cancellation.rb +25 -0
  81. data/lib/phronomy/agent/execution_coordinator.rb +2417 -572
  82. data/lib/phronomy/agent/handoff.rb +43 -0
  83. data/lib/phronomy/agent/handoff_capability_factory.rb +45 -0
  84. data/lib/phronomy/agent/handoff_context.rb +120 -0
  85. data/lib/phronomy/agent/handoff_execution_coordinator.rb +143 -0
  86. data/lib/phronomy/agent/handoff_policy.rb +143 -0
  87. data/lib/phronomy/agent/handoff_projection.rb +207 -0
  88. data/lib/phronomy/agent/handoff_request.rb +45 -0
  89. data/lib/phronomy/agent/handoff_runner.rb +178 -0
  90. data/lib/phronomy/agent/handoff_state.rb +43 -0
  91. data/lib/phronomy/agent/journal_record.rb +17 -4
  92. data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
  93. data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
  94. data/lib/phronomy/agent/llm_operation_result.rb +12 -7
  95. data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
  96. data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
  97. data/lib/phronomy/agent/recovery_coordinator/continuation.rb +174 -0
  98. data/lib/phronomy/agent/recovery_coordinator/installation.rb +381 -0
  99. data/lib/phronomy/agent/recovery_coordinator/resolution.rb +511 -0
  100. data/lib/phronomy/agent/recovery_coordinator.rb +217 -0
  101. data/lib/phronomy/agent/recovery_support.rb +504 -0
  102. data/lib/phronomy/agent/ruby_llm_materializer.rb +16 -13
  103. data/lib/phronomy/agent/selection/candidate.rb +53 -0
  104. data/lib/phronomy/agent/selection/constraint.rb +49 -0
  105. data/lib/phronomy/agent/shared_state.rb +38 -1
  106. data/lib/phronomy/agent/tool_approval_request.rb +33 -5
  107. data/lib/phronomy/agent/tool_definition_set.rb +49 -3
  108. data/lib/phronomy/agent/tool_invocation.rb +339 -103
  109. data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
  110. data/lib/phronomy/agent.rb +20 -2
  111. data/lib/phronomy/agent_already_exists_error.rb +5 -0
  112. data/lib/phronomy/agent_purged_error.rb +5 -0
  113. data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
  114. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
  115. data/lib/phronomy/engine/event_loop.rb +622 -63
  116. data/lib/phronomy/engine/fsm_session.rb +194 -21
  117. data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
  118. data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
  119. data/lib/phronomy/engine/runtime.rb +92 -20
  120. data/lib/phronomy/generator_verifier.rb +12 -14
  121. data/lib/phronomy/invocation_context.rb +9 -29
  122. data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
  123. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
  124. data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
  125. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
  126. data/lib/phronomy/multi_agent/orchestrator.rb +71 -26
  127. data/lib/phronomy/multi_agent/team_coordinator.rb +473 -125
  128. data/lib/phronomy/multi_agent/team_execution.rb +44 -0
  129. data/lib/phronomy/multi_agent/team_root.rb +41 -0
  130. data/lib/phronomy/persistence/durable_codec.rb +706 -0
  131. data/lib/phronomy/persistence/durable_record.rb +117 -0
  132. data/lib/phronomy/persistence/in_memory.rb +471 -133
  133. data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
  134. data/lib/phronomy/persistence/repository_facades.rb +535 -0
  135. data/lib/phronomy/persistence.rb +174 -40
  136. data/lib/phronomy/recovery.rb +186 -0
  137. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  138. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +2 -1
  139. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
  140. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
  141. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  142. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +137 -0
  143. data/lib/phronomy/testing/persistence_contract.rb +5 -0
  144. data/lib/phronomy/tools/agent.rb +1 -1
  145. data/lib/phronomy/tracing/automatic.rb +176 -0
  146. data/lib/phronomy/tracing/base.rb +11 -2
  147. data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
  148. data/lib/phronomy/version.rb +1 -1
  149. data/lib/phronomy/workflow.rb +3 -6
  150. data/lib/phronomy/workflow_context.rb +14 -5
  151. data/lib/phronomy/workflow_recovery.rb +123 -0
  152. data/lib/phronomy/workflow_runner.rb +468 -256
  153. data/lib/phronomy.rb +6 -0
  154. data/scripts/api_snapshot.rb +12 -0
  155. data/sig/phronomy/agent.rbs +209 -7
  156. data/sig/phronomy/handoff.rbs +41 -0
  157. data/sig/phronomy/multi_agent.rbs +35 -0
  158. data/sig/phronomy/persistence.rbs +123 -4
  159. data/sig/phronomy/runtime.rbs +1 -4
  160. data/sig/phronomy/workflow.rbs +2 -2
  161. data/sig/phronomy.rbs +10 -0
  162. metadata +84 -18
  163. data/examples/workflows/agent_event_mapping.rb +0 -101
  164. data/examples/workflows/generic_task_event_mapping.rb +0 -66
  165. data/lib/phronomy/agent/activation_registry.rb +0 -28
  166. data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
  167. data/lib/phronomy/agent/context_candidate.rb +0 -47
  168. data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
  169. data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
  170. data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
  171. data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
  172. data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
  173. data/lib/phronomy/agent/context_policy_registry.rb +0 -46
  174. data/lib/phronomy/agent/context_request.rb +0 -35
  175. data/lib/phronomy/agent/context_selection_unit.rb +0 -38
  176. data/lib/phronomy/agent/derived_content_spec.rb +0 -34
  177. data/lib/phronomy/agent/runner.rb +0 -97
  178. data/lib/phronomy/multi_agent/handoff.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce5a4ac39be13ef17ca1c7cd5d812b0ccdd8ea8523fdf2e59ee8f7e78e2830fa
4
- data.tar.gz: 3c943cd7b11ddd1bbe2e22fdd16f3855564361c3dba093d39f5f32d193850b5a
3
+ metadata.gz: 1c8712213b18692b6bf9531110ffc38fa0f9d069509bf85b3424c5b77a76eb8e
4
+ data.tar.gz: c63a2cc058ad50b80306bcadfd7a99304f216ab02375272e43ab6cf5c38cb451
5
5
  SHA512:
6
- metadata.gz: e41fcf5aee7eaed49f3ef13c7399c787c281232909b07d1fa2a2e14b4a4d443222280ead723e885faeea0534e9173c036e16e90e6af04de4a440f0a76020e2c1
7
- data.tar.gz: f0dbf8fa8947760cafbc530226a021b009acf3f0be37ddee08a5404087422b5508cd5df83ab50456b43fa8f1eb57a889f8a1974372d95b98edcad1752ddda562
6
+ metadata.gz: 64de5d992461ceaccc2d579bbd6b3046b8ce711ca65f68b14cfb9f9fe45d724c2db0646ef547fe7d6a4502a7ca6657b9330d33898e1f24e694418fe9e41c324d
7
+ data.tar.gz: 507d16006d00d4304db2d1e2e67a61e605dbabfc190dc7cdbbf198e17e4648fc5a0767904f503e2da826f7744859c78ec99f7585732b45ace6251e1296cb274a
data/.mutant.yml CHANGED
@@ -15,9 +15,8 @@ matcher:
15
15
  - Phronomy::LlmContextWindow::TokenBudget
16
16
  - Phronomy::Agent::ContextAssembler
17
17
  - Phronomy::Agent::ContextPolicies::Default
18
- - Phronomy::Agent::ContextParts::UnitBuilders::DependencyAwareUnitBuilder
19
- - Phronomy::Agent::ContextParts::Requirements::RequiredContextResolver
20
- - Phronomy::Agent::ContextParts::Selectors::RecentFirstSelector
21
- - Phronomy::Agent::ContextParts::Budget::TokenBudgetPacker
18
+ - Phronomy::Agent::ContextPolicyInputBuilder
22
19
  - Phronomy::Agent::ContextPlanValidator
20
+ - Phronomy::Agent::HandoffPolicy
21
+ - Phronomy::Agent::HandoffProjection
23
22
  - Phronomy::VectorStore::InMemory
data/CHANGELOG.md CHANGED
@@ -12,29 +12,215 @@ Release history for 0.14.0 and earlier is archived in
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ### Fixed
16
+
17
+ - Run recovered output filtering inside the ordinary Agent FSM so filter
18
+ exceptions commit a failed execution and release admission, including after
19
+ another restart. Preserve explicit filter blocking as the `blocked` status.
20
+ - Propagate a coordinator's blocked outcome, and a worker's blocked outcome
21
+ under `on_error: :raise`, to Team failure. Preserve skipped worker errors and
22
+ committed failure precedence over later cancellation across restart.
23
+ - Keep Recovery content materialization, approval restoration reads, and F1
24
+ resolution readback off EventLoop. Apply prepared invocation state only on
25
+ EventLoop, reject stale results, and retain confirmed resolution facts when
26
+ later content reads fail.
27
+ - Avoid reading Orchestrator Knowledge again while constructing invocation-owned
28
+ Tools; those children inherit the Knowledge already saved with their slots.
29
+
30
+ ---
31
+
32
+ ## [0.23.0] - 2026-08-29
33
+
34
+ ### Four-category Context Policy SPI and transaction boundary (ACS-04)
35
+
36
+ #### Added
37
+
38
+ - Public immutable `ContextPolicyInput` typed values for `instruction`,
39
+ `knowledge`, `tools`, and indivisibly grouped `conversation`.
40
+ - Agent-class `context_policy <instance>` binding for reusable Application
41
+ Context strategies, with built-in Default fallback.
42
+ - Small ContextPolicy helpers for current-call generated instruction, Knowledge,
43
+ and conversation items.
44
+ - Framework trace boundary around ContextPolicy invocation without standard-trace
45
+ inclusion of full Policy input/Plan content.
46
+
47
+ #### Changed
48
+
49
+ - `ContextPlan` now directly carries ordered output material in the same four
50
+ semantic categories.
51
+ - Default Policy uses deterministic recent-conversation / stable-fit Knowledge
52
+ selection with an approximately 60/40 variable budget split and preserves the
53
+ effective Tool set.
54
+ - Custom Policy may select/reorder a subset of effective Tools; arbitrary
55
+ schema-only runtime Tool creation remains outside ACS-04 because durable
56
+ runtime Tool identity/wiring is not defined.
57
+ - ContextPolicy executes outside Phronomy Persistence transactions. The durable
58
+ base/revision is revalidated before the short Manifest/execution commit.
59
+ - Context assembly policy version is `8`.
60
+
61
+ #### Removed
62
+
63
+ - `ContextRequest`, `ContextPolicyDescriptor`, `ContextPolicyRegistry`, and
64
+ `DerivedContentSpec`.
65
+ - Intermediate selection-unit/policy-parts machinery superseded by the typed
66
+ four-category SPI: `Selection::Unit`, `Selection::Validator`,
67
+ `DependencyAwareUnitBuilder`, `RequiredContextResolver`,
68
+ `RecentFirstSelector`, and `TokenBudgetPacker`.
69
+ - Descriptor persistence/reconstruction and old `ContextPlan` fields
70
+ `selected_unit_ids`, `derived_contents`, `ordering_hints`, and
71
+ `policy_descriptor`.
72
+
73
+ ### Workflow Runtime admission and durable terminal barrier (ACS-13)
74
+
75
+ #### Added
76
+
77
+ - ADR-026 defining opaque process-local Workflow admission ownership,
78
+ admission-before-hydration ordering, and FSMSession-integrated terminal
79
+ persistence.
80
+ - Explicit Workflow terminal persistence outcomes: known success, known failure,
81
+ and fail-closed outcome uncertainty.
82
+
83
+ #### Changed
84
+
85
+ - Workflow admission ownership no longer reuses the future `fsm_session_id`.
86
+ EventLoop acquires a separate opaque owner token before durable load and binds
87
+ the concrete FSMSession routing ID only after hydration.
88
+ - Durable Workflow halt/completion now remains nonterminal while the final
89
+ snapshot is saved. The save result returns to the same FSMSession, and only a
90
+ known-successful result permits `HALTED` / `COMPLETED`, admission release, and
91
+ caller Task settlement.
92
+ - Portable known Persistence failures follow the Workflow error path. An
93
+ arbitrary storage/transport error whose commit outcome is not established is
94
+ treated as outcome-unknown: the success barrier stays closed and the
95
+ `workflow_instance_id` admission remains fail-closed/recovery-required.
96
+ - Workflow persistence result handling is backend-topology neutral; the FSM does
97
+ not depend on local/network/database transport details.
98
+
99
+ ### Process-local Agent ownership and Runtime admission (ACS-12)
100
+
101
+ #### Added
102
+
103
+ - ADR-025 defining one mutable live Agent owner per `agent_id` per Runtime and
104
+ EventLoop-owned same-process top-level execution admission.
105
+ - `Agent::Base.get(agent_id)` for process-local live-owner lookup without
106
+ Persistence I/O.
107
+ - `AgentAlreadyExistsError` for create/new identity conflicts and
108
+ `AgentPurgedError` for stale references after successful purge.
109
+
110
+ #### Changed
111
+
112
+ - Repeated `Agent.load(agent_id, persistence:)` returns the exact existing live
113
+ Agent object; a durable-only Agent is hydrated once, and a missing durable
114
+ Agent remains a strict `Persistence::NotFoundError`.
115
+ - `new` / `create` mean creation only and reject an identity that is already
116
+ live or already durable instead of materializing a second mutable owner.
117
+ - EventLoop acquires the same-Agent top-level execution slot before initial
118
+ Offload/Persistence work. Suspension retains that slot; known durable terminal
119
+ completion releases it; uncertain durable outcomes remain fail-closed.
120
+ - Persistence `executions.create_active` / atomic admission remains the durable
121
+ second line of integrity defense rather than the primary same-process lock.
122
+ - Runtime-lifetime Agent ownership is released on clean Runtime shutdown.
123
+ Successful `purge!` is the explicit earlier destruction boundary and makes
124
+ the stale Agent object permanently unusable before the identity can be reused.
125
+
126
+ ### EventLoop single-writer Agent Runtime (ACS-11)
127
+
128
+ #### Added
129
+
130
+ - ADR-024 defining EventLoop as the single writer of Phronomy-managed live
131
+ Agent execution state and operation-specific Offload command/result apply.
132
+ - Runtime read-only Agent execution-owner lookup for approval/live-owner
133
+ routing without exposing mutable execution internals.
134
+ - Provider result authority using current FSMSession/FSM state plus semantic
135
+ `llm_call_id`; stale Provider results are consumed without advancing the FSM.
136
+
137
+ #### Changed
138
+
139
+ - Agent initial preparation, follow-up Manifest preparation, approval resume,
140
+ and terminal durable commits now return values from OffloadPool and apply
141
+ committed live-state advances only on EventLoop.
142
+ - AgentInvocation owns FSM-local uncommitted Provider/Tool/runtime facts.
143
+ - Tool authorization captures Agent identity and Tool description data before
144
+ worker execution; the authorization worker receives no live Agent, Tool, or
145
+ ToolInvocation reference. Application-owned approval/facts/requirement
146
+ callables remain explicit behavior handles.
147
+ - `ApprovalEvaluationRequest` is value-only: Agent identity is exposed as
148
+ `agent_id`, `agent_definition_id`, and `agent_definition_version`; Tool
149
+ identity/description is exposed through `tool_name` / `tool_schema`.
150
+ - Tool authorization/execution outcomes carry semantic `tool_invocation_id`
151
+ and are applied by the Tool FSMSession.
152
+
153
+ #### Removed
154
+
155
+ - `AgentExecutionActivation`, `Agent::ActivationRegistry`,
156
+ `Runtime#__agent_activations`, and the `phronomy_activation` config bridge.
157
+ - Live-object accessors `ApprovalEvaluationRequest#agent` and
158
+ `ApprovalEvaluationRequest#tool`; approval policies use value identity and
159
+ Tool-description fields instead.
160
+
161
+
162
+ ### Semantic Multi-Agent Handoff and shared Selection
163
+
164
+ #### Added
165
+
166
+ - `Phronomy::MultiAgent::HandoffPolicy` with required, forbidden, and selectable
167
+ transfer rules for current request, history, Knowledge, and Tool exchanges.
168
+ - `Phronomy::MultiAgent::Runner.new(main_agent:, handoffs:)` as the public
169
+ semantic Handoff coordinator.
170
+ - Typed private Handoff request/context/provenance values, explicit
171
+ `AgentExecution` `:handed_off` terminal semantics, and Runtime/EventLoop-owned
172
+ active-Agent coordination.
173
+ - Shared `Phronomy::Agent::Selection::Candidate`, `Unit`, `Constraint`, and
174
+ validation machinery for Context and Handoff selection.
175
+ - ADR-016 and the 0.22 migration guide for the semantic Handoff clean break.
176
+
177
+ #### Changed
178
+
179
+ - Handoff is now an explicit Source-to-Target responsibility transfer rather
180
+ than sentinel Tool-result routing. Generated Handoff Tool names are private
181
+ transport details only.
182
+ - Handoff Context is projected from the effective Source Manifest, materialized
183
+ immutably with provenance, and may cross Agents backed by different Persistence
184
+ adapters without adopting Source material into Target Journal/Knowledge.
185
+ - Target Context Policy remains the final per-LLM-call selection authority;
186
+ transferred material enters Target assembly as selectable Context candidates.
187
+ - The active Target persists across user turns and Runner-facade recreation while
188
+ the same main Agent instance and Runtime remain alive. Runtime/process reset
189
+ intentionally does not restore active-Agent continuation.
190
+ - Context assembly policy version is now `7` for the shared Selection and
191
+ Handoff-Context contract.
192
+
193
+ #### Removed
194
+
195
+ - `Phronomy::Agent::Runner`, the `agents:` / `routes:` Runner API, sentinel-map
196
+ routing, and Agent-owned `_add_handoff_tool` / `_handoff_tools` mutation.
197
+ - `Phronomy::Agent::ContextCandidate` and
198
+ `Phronomy::Agent::ContextSelectionUnit`; internal callers use the shared
199
+ `Agent::Selection` model without compatibility aliases.
200
+
15
201
  ### Unified Persistence and durable-state ownership
16
202
 
17
203
  #### Added
18
204
 
19
205
  - `Persistence#workflow_states` with optimistic revision checks for durable Workflow snapshots.
20
- - Runtime-local Agent Activation ownership; live `AgentExecutionActivation` values are no longer Persistence repositories.
206
+ - Runtime-local Agent execution ownership outside Persistence; ACS-11 now places the mutable live-state authority on EventLoop.
21
207
  - `Agent::Base.live_for_execution(execution_id)` for resolving the current process's live owner Agent without reloading Agent or Execution state from Persistence.
22
- - Owner-aware Workflow admission keyed by durable `thread_id` and owned by a per-execution internal `fsm_session_id`.
208
+ - Owner-aware Workflow admission keyed by durable `workflow_instance_id`; ACS-13 now uses a Runtime-only opaque owner token that is separate from concrete `fsm_session_id` routing.
23
209
  - ADR-014 and the 0.19 migration guide for the unified durable-state architecture.
24
210
 
25
211
  #### Changed
26
212
 
27
- - Live Agent instances now remain the authoritative logical-state owners after hydration. Context Policy and follow-up Manifest preparation use the Agent-local root, Journal view, and Activation state instead of reloading mutable Agent state for freshness.
213
+ - Live Agent instances remain authoritative for AgentRoot/Journal state after hydration, while EventLoop owns active execution-state mutation. Context Policy and follow-up Manifest preparation use those local views instead of reloading mutable Agent state for freshness.
28
214
  - `Phronomy.configuration.persistence` is the global durable backend for Workflows and for Agent `new`/`create` calls that do not explicitly inject another Persistence instance.
29
215
  - Agent durable writes use optimistic revision/Journal-position guardrails; conflicting external writes fail instead of being silently reloaded or merged.
30
- - Approval suspension/resume preserves the same live Agent/Activation/AgentInvocation. Approval remains an Agent-instance operation; callers with only an `execution_id` resolve the live owner with `Agent::Base.live_for_execution` (or the expected concrete Agent class) before calling `agent.approve` / `agent.approve_async`.
31
- - Workflow durable I/O runs outside EventLoop through OffloadPool, while `thread_id` admission remains owned until terminal/halted snapshot persistence completes inside the current Runtime. Workflow admission is process-local; optimistic revisions detect stale commits across processes but do not prevent duplicate execution or undo already-performed external side effects.
32
- - Workflow `thread_id`, Runtime `fsm_session_id`, and application `session_id` now have distinct responsibilities.
216
+ - Approval suspension/resume preserves the same process-local Agent/AgentInvocation owner. Approval remains an Agent-instance operation; callers with only an `execution_id` resolve the EventLoop-backed live owner with `Agent::Base.live_for_execution` (or the expected concrete Agent class) before calling `agent.approve` / `agent.approve_async`.
217
+ - Workflow durable I/O runs outside EventLoop through OffloadPool. Terminal/halted snapshot persistence now completes inside the owning FSMSession lifecycle; only a known-successful save permits normal terminalization and admission release. Workflow admission remains process-local; optimistic revisions detect stale commits across processes but do not prevent duplicate execution or undo already-performed external side effects.
218
+ - Workflow `workflow_instance_id` is distinct from one concrete Runtime FSMSession identity; generic application `session_id` is not a Phronomy core domain identity.
33
219
 
34
220
  #### Removed
35
221
 
36
222
  - `Phronomy::StateStore`, `StateStore::InMemory`, `Workflow.define(..., state_store:)`, `Configuration#state_store`, and per-invocation `config[:state_store]`.
37
- - `Persistence#activations`; ActivationRegistry is transient Runtime state.
223
+ - `Persistence#activations`; live Agent execution state is Runtime-only. The transitional ActivationRegistry is subsequently removed by ACS-11.
38
224
  - Class-level `Agent::Base.approve` / `Agent::Base.approve_async` routing APIs and their caller-supplied `persistence:` argument; approval execution now goes through the resolved live Agent instance.
39
225
 
40
226
  ### Public API façade and typed contracts
@@ -59,6 +245,25 @@ Release history for 0.14.0 and earlier is archived in
59
245
  return `Task`.
60
246
  - `InvocationContext` construction is classified consistently with its documented
61
247
  Beta application API.
248
+ - Generic Agent invocation identity is removed: `InvocationContext` no longer
249
+ exposes `thread_id` / `session_id`, Agent invocation APIs no longer accept
250
+ `thread_id:`, and no replacement generic correlation identity is introduced.
251
+ - The canonical `JournalRecord` representation no longer contains
252
+ `correlation_id`; legacy durable Hashes containing the removed key remain
253
+ readable without an eager data rewrite.
254
+ - Tool approval notification/policy parent identity now uses canonical Agent
255
+ `execution_id` instead of `agent_invocation_id`; new suspended-execution
256
+ approval data uses the same parent identity.
257
+ - Legacy embedded suspended approval hashes remain readable by deriving the
258
+ logical parent from their enclosing Agent execution; historical
259
+ content-addressed audit bodies are not rewritten.
260
+ - Agent, Tool, and Multi-Agent concrete `FSMSession` instances no longer reuse
261
+ domain/context IDs as EventLoop routing targets. Async callbacks use
262
+ session-local event sinks, and Provider completion is routed to the owning
263
+ FSMSession before EventLoop validates/applies `llm_call_id`-bound result
264
+ state. Workflow retains its pre-load admission ordering through a private
265
+ single-use FSMSession identity reservation; opaque Workflow admission
266
+ ownership remains ACS-13 work.
62
267
  - OutputParser `parse` is classified as the public subclass extension point that
63
268
  concrete parsers implement.
64
269
 
@@ -133,8 +338,8 @@ Release history for 0.14.0 and earlier is archived in
133
338
  returns a completion handle immediately.
134
339
  - `Tool#call_async` for ordinary cooperative Tools no longer consumes a
135
340
  OffloadPool worker. `:offloaded` remains the worker-pool route.
136
- - Framework-owned short in-memory Tools (handoff sentinels, TeamCoordinator
137
- queue controls, and SharedState store access) explicitly declare
341
+ - Framework-owned short in-memory Tools (TeamCoordinator queue controls and
342
+ SharedState store access) explicitly declare
138
343
  `execution_mode :cooperative` instead of using the blocking-I/O default.
139
344
  - MultiAgent fan-out uses a FanOut FSMSession rather than per-child OS Threads.
140
345
  - `TimerQueue` is driven by EventLoop and owns no Thread.
@@ -172,7 +377,7 @@ Release history for 0.14.0 and earlier is archived in
172
377
  transcript/Knowledge lifecycle semantics.
173
378
  - Active Context tests, integration fixtures, benchmarks, mutation subjects,
174
379
  design documents, and API snapshots now describe the canonical
175
- Journal -> ContextCandidate -> Context Policy -> Manifest architecture.
380
+ Journal -> Selection::Candidate -> Context Policy -> Manifest architecture.
176
381
 
177
382
  #### Removed
178
383
 
data/CONTRIBUTING.md CHANGED
@@ -114,17 +114,85 @@ OffloadPool work is supplied through `CancellationToken`.
114
114
 
115
115
  ---
116
116
 
117
- ## Architecture Decision Records
118
-
119
- Key design decisions are documented as ADRs in
120
- [docs/decisions/](docs/decisions/). Read these before making significant changes
121
- to the threading model, persistence/context authority, or public API shape.
117
+ ## Architecture Authority and Decision Records
118
+
119
+ Phronomy has **no universal artifact priority**. Different artifacts answer
120
+ different questions. The normative repository rule is
121
+ [`017-design-authority-and-adr-governance`](docs/decisions/017-design-authority-and-adr-governance.md);
122
+ the complete ADR status/supersession index is
123
+ [`docs/decisions/README.md`](docs/decisions/README.md).
124
+
125
+ Use these authority boundaries:
126
+
127
+ - Accepted, non-superseded ADRs define architecture intent.
128
+ - Source/runtime behavior defines current implementation reality; it does not
129
+ silently amend an ADR.
130
+ - Public API and extension-SPI contracts are composite: runtime behavior,
131
+ `@api` classification, formal API documentation, and explicit
132
+ compatibility/contract tests all participate.
133
+ - RBS does not create a contract. It types a contract already established by
134
+ those sources.
135
+ - Ordinary implementation tests are regression evidence. Only tests explicitly
136
+ maintained as architecture guards or compatibility/contract tests have that
137
+ stronger role.
138
+ - Historical, Archived, and Superseded material is non-normative for current
139
+ architecture.
140
+
141
+ When architecture intent, public contract, and implementation reality disagree,
142
+ treat the discrepancy as an **architecture inconsistency**. Do not choose a
143
+ winner merely because one artifact is newer. Record the conflict and resolve it
144
+ explicitly according to the process in the ADR index.
145
+
146
+ ADR references should use a file link or the canonical filename key. This is
147
+ required for the two legacy `011` decisions because bare `ADR-011` is
148
+ ambiguous. Do not renumber historical ADRs to repair that legacy collision.
122
149
 
123
150
  For Agent Context work, ADR-012 and ADR-013 define the current Journal,
124
151
  Manifest, Context Policy and persistent Knowledge model. ADR-010 defines the
125
152
  EventLoop/FSMSession, Task, and OffloadPool execution boundary. ADR-015 defines
126
153
  the Tool public façade, extension-SPI boundary, and RBS ownership rules.
127
154
 
155
+ Current explanatory architecture starts at [`docs/architecture.md`](docs/architecture.md).
156
+ Non-current design snapshots live under `docs/archive/design/` and are
157
+ non-normative. Do not treat archived/historical material as a current contract
158
+ merely because it remains searchable in the repository.
159
+
160
+ ### Durability, recovery, and failure vocabulary
161
+
162
+ Architecture-sensitive durability, concurrency, recovery, cancellation, and
163
+ external-effect changes must use
164
+ [`018-durability-guarantees-and-failure-model`](docs/decisions/018-durability-guarantees-and-failure-model.md).
165
+
166
+ Do not write a bare claim such as "durable", "recoverable", "safe", or
167
+ "exactly once". State:
168
+
169
+ 1. the guarantee subject;
170
+ 2. the exact guarantee property (for example durable-state restart
171
+ readability, execution resumption, stale durable-transition conflict
172
+ detection, or duplicate external-side-effect prevention);
173
+ 3. the component/contract that provides it;
174
+ 4. the applicable F0-F4 failure class(es);
175
+ 5. whether X0 External Effect Boundary is crossed; and
176
+ 6. whether the architecture result is YES, CONDITIONAL, or NO, including the
177
+ condition for every CONDITIONAL guarantee.
178
+
179
+ In particular:
180
+
181
+ - F0 operation failure and F1 outcome uncertainty are distinct dimensions and
182
+ may co-occur; an F0 result does not prove durable/external outcome certainty.
183
+ - durable-transition atomicity is not commit-outcome certainty.
184
+ - optimistic conflict detection is not cross-process execution exclusion.
185
+ - process/runtime loss (F4) does not imply confirmed durable state was lost.
186
+ - X0 external side effects are not automatically atomic with Persistence.
187
+ - semantic IDs do not by themselves provide duplicate prevention.
188
+ - arbitrary external exactly-once execution is not an unconditional Phronomy
189
+ guarantee.
190
+
191
+ Fault-injection and recovery tests should state which failure class/boundary
192
+ they exercise and must not imply stronger guarantees than the test proves.
193
+ F0-F4/X0 are architecture/test vocabulary, not a required public exception
194
+ hierarchy.
195
+
128
196
  ---
129
197
 
130
198
  ## Mutation Testing
@@ -155,13 +223,17 @@ The authoritative subject list is `.mutant.yml`. It currently includes:
155
223
  - `Phronomy::LlmContextWindow::TokenBudget`
156
224
  - `Phronomy::Agent::ContextAssembler`
157
225
  - `Phronomy::Agent::ContextPolicies::Default`
158
- - `Phronomy::Agent::ContextParts::UnitBuilders::DependencyAwareUnitBuilder`
159
- - `Phronomy::Agent::ContextParts::Requirements::RequiredContextResolver`
160
- - `Phronomy::Agent::ContextParts::Selectors::RecentFirstSelector`
161
- - `Phronomy::Agent::ContextParts::Budget::TokenBudgetPacker`
226
+ - `Phronomy::Agent::ContextPolicyInputBuilder`
162
227
  - `Phronomy::Agent::ContextPlanValidator`
228
+ - `Phronomy::Agent::HandoffPolicy`
229
+ - `Phronomy::Agent::HandoffProjection`
163
230
  - `Phronomy::VectorStore::InMemory`
164
231
 
232
+ The nightly mutation matrix mirrors this authoritative list so each subject can
233
+ run in an isolated job with its own timeout. The regular test suite includes a
234
+ configuration-consistency guard that fails if the nightly subject set diverges
235
+ from `.mutant.yml`.
236
+
165
237
  The Tool mutation subject intentionally uses
166
238
  `Phronomy::Agent::Context::Capability::Base`, which is the implementation
167
239
  canonical name returned by the single Class object's runtime `Class#name`.
data/README.md CHANGED
@@ -19,6 +19,7 @@ tracking `main` directly.
19
19
  - **Persistence** — unified durable backend for Agent state and Workflow `workflow_states`.
20
20
  - **Workflow** — state-machine-driven application workflow with explicit events and wait states.
21
21
  - **Tool / Capability** — callable application capability exposed to an Agent; application-defined Tools subclass `Phronomy::Tool::Base`.
22
+ - **Multi-Agent Handoff** — semantic Source-to-Target responsibility transfer with policy-bounded Context projection and persisted active responsibility and exact Target recovery within one Persistence domain.
22
23
  - **EventLoop + FSMSession** — the framework control plane for logical lifecycle coordination.
23
24
  - **OffloadPool** — bounded operating-system-thread execution boundary for synchronous work that must not run on EventLoop.
24
25
  - **Task** — the common thread-free completion handle returned by Phronomy asynchronous APIs, including OffloadPool-backed work.
@@ -91,17 +92,23 @@ task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks")
91
92
  result = task.wait_result # top-level/external caller only
92
93
  ```
93
94
 
94
- A block listener receives Agent lifecycle events and is equivalent to `on_event:`:
95
+ Agent lifecycle events are bound to one live Agent Runtime incarnation.
96
+ Register `on_event:` (or the equivalent construction block) when the Agent
97
+ is created or loaded, then invoke it without a per-call listener:
95
98
 
96
99
  ```ruby
97
- task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks") do |event|
98
- puts event.type # :done, :error, :tool_call, :tool_result, etc.
99
- end
100
+ agent = ResearchAgent.new(
101
+ on_event: ->(event) {
102
+ puts event.type # :done, :error, :tool_call, :tool_result, etc.
103
+ }
104
+ )
100
105
 
101
- # on_event: keyword form is also accepted and behaves identically
102
- task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks", on_event: listener)
106
+ task = agent.invoke_async("Research Ruby AI frameworks")
103
107
  ```
104
108
 
109
+ The same listener registration is available on `create` and `load`.
110
+ Public per-invocation `on_event:` / listener blocks are removed.
111
+
105
112
  ## Runtime model
106
113
 
107
114
  Phronomy uses one completion model with two execution mechanisms:
@@ -134,11 +141,13 @@ contracts, timeout/cancellation semantics, metrics, and callback rules.
134
141
 
135
142
  - [Getting started](docs/getting-started.md) — installation, RubyLLM setup, Agent/Workflow basics, persistence, streaming.
136
143
  - [Features and API stability](docs/features.md) — public feature matrix and stability labels.
144
+ - [Architecture](docs/architecture.md) — canonical current explanatory architecture entry and authority navigation.
137
145
  - [Runtime and concurrency](docs/runtime-and-concurrency.md) — EventLoop, FSMSession, Task, OffloadPool, cancellation, observability.
138
146
  - [MCP client](docs/mcp-client.md) — Model Context Protocol (MCP) integration and supported schema subset.
139
147
  - [Migration from 0.15-era APIs](docs/migrations/0.15.md).
140
148
  - [0.16 cleanup migration](docs/migrations/0.16.md).
141
149
  - [0.19 unified Persistence migration](docs/migrations/0.19.md).
150
+ - [0.22 semantic Multi-Agent Handoff migration](docs/migrations/0.22.md).
142
151
  - [Architecture Decision Records](docs/decisions/) — design rationale and superseding decisions.
143
152
  - [CHANGELOG](CHANGELOG.md) — current development and recent release history.
144
153
  - [Changelog archive: 0.14.0 and earlier](docs/changelog/0.14-and-earlier.md).