phronomy 0.22.0 → 0.23.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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +3 -4
  3. data/CHANGELOG.md +200 -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 +152 -0
  15. data/docs/architecture/persistence.md +175 -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 +82 -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 +112 -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/README.md +172 -0
  45. data/docs/features.md +31 -11
  46. data/docs/getting-started.md +77 -45
  47. data/docs/migrations/0.19.md +14 -7
  48. data/docs/migrations/0.22.md +390 -0
  49. data/docs/persistence-backends.md +88 -38
  50. data/docs/runtime-and-concurrency.md +227 -33
  51. data/examples/README.md +13 -0
  52. data/lib/phronomy/agent/agent_execution.rb +19 -15
  53. data/lib/phronomy/agent/agent_invocation.rb +288 -93
  54. data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
  55. data/lib/phronomy/agent/agent_root.rb +3 -3
  56. data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
  57. data/lib/phronomy/agent/async_event_api.rb +145 -72
  58. data/lib/phronomy/agent/base.rb +388 -181
  59. data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
  60. data/lib/phronomy/agent/context_assembler.rb +437 -178
  61. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  62. data/lib/phronomy/agent/context_plan.rb +18 -13
  63. data/lib/phronomy/agent/context_plan_validator.rb +246 -88
  64. data/lib/phronomy/agent/context_policies/default.rb +123 -34
  65. data/lib/phronomy/agent/context_policy.rb +109 -3
  66. data/lib/phronomy/agent/context_policy_input.rb +244 -0
  67. data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
  68. data/lib/phronomy/agent/execution_coordinator.rb +1975 -587
  69. data/lib/phronomy/agent/journal_record.rb +17 -4
  70. data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
  71. data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
  72. data/lib/phronomy/agent/llm_operation_result.rb +12 -7
  73. data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
  74. data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
  75. data/lib/phronomy/agent/recovery_coordinator/continuation.rb +271 -0
  76. data/lib/phronomy/agent/recovery_coordinator/installation.rb +427 -0
  77. data/lib/phronomy/agent/recovery_coordinator/resolution.rb +635 -0
  78. data/lib/phronomy/agent/recovery_coordinator.rb +211 -0
  79. data/lib/phronomy/agent/recovery_support.rb +512 -0
  80. data/lib/phronomy/agent/ruby_llm_materializer.rb +22 -13
  81. data/lib/phronomy/agent/selection/candidate.rb +53 -0
  82. data/lib/phronomy/agent/selection/constraint.rb +49 -0
  83. data/lib/phronomy/agent/shared_state.rb +38 -1
  84. data/lib/phronomy/agent/tool_approval_request.rb +33 -5
  85. data/lib/phronomy/agent/tool_definition_set.rb +49 -3
  86. data/lib/phronomy/agent/tool_invocation.rb +336 -102
  87. data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
  88. data/lib/phronomy/agent.rb +20 -2
  89. data/lib/phronomy/agent_already_exists_error.rb +5 -0
  90. data/lib/phronomy/agent_purged_error.rb +5 -0
  91. data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
  92. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
  93. data/lib/phronomy/engine/event_loop.rb +622 -63
  94. data/lib/phronomy/engine/fsm_session.rb +194 -21
  95. data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
  96. data/lib/phronomy/engine/runtime.rb +77 -20
  97. data/lib/phronomy/generator_verifier.rb +12 -14
  98. data/lib/phronomy/invocation_context.rb +9 -29
  99. data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
  100. data/lib/phronomy/multi_agent/coordination_state.rb +18 -0
  101. data/lib/phronomy/multi_agent/coordinator.rb +154 -0
  102. data/lib/phronomy/multi_agent/execution_coordinator.rb +116 -0
  103. data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
  104. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
  105. data/lib/phronomy/multi_agent/handoff.rb +24 -45
  106. data/lib/phronomy/multi_agent/handoff_capability_factory.rb +87 -0
  107. data/lib/phronomy/multi_agent/handoff_context.rb +95 -0
  108. data/lib/phronomy/multi_agent/handoff_policy.rb +137 -0
  109. data/lib/phronomy/multi_agent/handoff_projection.rb +191 -0
  110. data/lib/phronomy/multi_agent/handoff_request.rb +45 -0
  111. data/lib/phronomy/multi_agent/orchestrator.rb +12 -15
  112. data/lib/phronomy/multi_agent/runner.rb +98 -0
  113. data/lib/phronomy/persistence/durable_codec.rb +646 -0
  114. data/lib/phronomy/persistence/durable_record.rb +117 -0
  115. data/lib/phronomy/persistence/in_memory.rb +210 -134
  116. data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
  117. data/lib/phronomy/persistence/repository_facades.rb +316 -0
  118. data/lib/phronomy/persistence.rb +81 -41
  119. data/lib/phronomy/recovery.rb +186 -0
  120. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  121. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -1
  122. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
  123. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
  124. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  125. data/lib/phronomy/tracing/automatic.rb +176 -0
  126. data/lib/phronomy/tracing/base.rb +11 -2
  127. data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
  128. data/lib/phronomy/version.rb +1 -1
  129. data/lib/phronomy/workflow.rb +3 -6
  130. data/lib/phronomy/workflow_context.rb +14 -5
  131. data/lib/phronomy/workflow_recovery.rb +123 -0
  132. data/lib/phronomy/workflow_runner.rb +468 -256
  133. data/lib/phronomy.rb +6 -0
  134. data/scripts/api_snapshot.rb +12 -0
  135. data/sig/phronomy/agent.rbs +209 -7
  136. data/sig/phronomy/multi_agent.rbs +39 -0
  137. data/sig/phronomy/persistence.rbs +62 -4
  138. data/sig/phronomy/runtime.rbs +1 -4
  139. data/sig/phronomy/workflow.rbs +2 -2
  140. data/sig/phronomy.rbs +10 -0
  141. metadata +65 -17
  142. data/examples/workflows/agent_event_mapping.rb +0 -101
  143. data/examples/workflows/generic_task_event_mapping.rb +0 -66
  144. data/lib/phronomy/agent/activation_registry.rb +0 -28
  145. data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
  146. data/lib/phronomy/agent/context_candidate.rb +0 -47
  147. data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
  148. data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
  149. data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
  150. data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
  151. data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
  152. data/lib/phronomy/agent/context_policy_registry.rb +0 -46
  153. data/lib/phronomy/agent/context_request.rb +0 -35
  154. data/lib/phronomy/agent/context_selection_unit.rb +0 -38
  155. data/lib/phronomy/agent/derived_content_spec.rb +0 -34
  156. data/lib/phronomy/agent/runner.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce5a4ac39be13ef17ca1c7cd5d812b0ccdd8ea8523fdf2e59ee8f7e78e2830fa
4
- data.tar.gz: 3c943cd7b11ddd1bbe2e22fdd16f3855564361c3dba093d39f5f32d193850b5a
3
+ metadata.gz: ffed6b796d049192f4f782aea75353504c166b23c5b6e1f336ab73eb90bbeb54
4
+ data.tar.gz: 5655635e8ef6331496d0bb057dad602b2154cabae97ec87f4906123824b8c45d
5
5
  SHA512:
6
- metadata.gz: e41fcf5aee7eaed49f3ef13c7399c787c281232909b07d1fa2a2e14b4a4d443222280ead723e885faeea0534e9173c036e16e90e6af04de4a440f0a76020e2c1
7
- data.tar.gz: f0dbf8fa8947760cafbc530226a021b009acf3f0be37ddee08a5404087422b5508cd5df83ab50456b43fa8f1eb57a889f8a1974372d95b98edcad1752ddda562
6
+ metadata.gz: 242dcb66e87b5beaaa55e19541fc852821adccf971cad68c06d65bcf705352a670b67012fa8c706ea0263f69eb03793a9b67d0bc3d61df422d201b3e3b6aa1c2
7
+ data.tar.gz: 0b889a029056ad0a458a7318eacf0d3813072adf0f6b7ef8a448a02dd0a476b9146fcbdf90d4170ea3823058ba51a82aa4a0b7fb31e24f7e031404d9b7b27924
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::MultiAgent::HandoffPolicy
21
+ - Phronomy::MultiAgent::HandoffProjection
23
22
  - Phronomy::VectorStore::InMemory
data/CHANGELOG.md CHANGED
@@ -12,29 +12,200 @@ Release history for 0.14.0 and earlier is archived in
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ---
16
+
17
+ ## [0.23.0] - 2026-08-29
18
+
19
+ ### Four-category Context Policy SPI and transaction boundary (ACS-04)
20
+
21
+ #### Added
22
+
23
+ - Public immutable `ContextPolicyInput` typed values for `instruction`,
24
+ `knowledge`, `tools`, and indivisibly grouped `conversation`.
25
+ - Agent-class `context_policy <instance>` binding for reusable Application
26
+ Context strategies, with built-in Default fallback.
27
+ - Small ContextPolicy helpers for current-call generated instruction, Knowledge,
28
+ and conversation items.
29
+ - Framework trace boundary around ContextPolicy invocation without standard-trace
30
+ inclusion of full Policy input/Plan content.
31
+
32
+ #### Changed
33
+
34
+ - `ContextPlan` now directly carries ordered output material in the same four
35
+ semantic categories.
36
+ - Default Policy uses deterministic recent-conversation / stable-fit Knowledge
37
+ selection with an approximately 60/40 variable budget split and preserves the
38
+ effective Tool set.
39
+ - Custom Policy may select/reorder a subset of effective Tools; arbitrary
40
+ schema-only runtime Tool creation remains outside ACS-04 because durable
41
+ runtime Tool identity/wiring is not defined.
42
+ - ContextPolicy executes outside Phronomy Persistence transactions. The durable
43
+ base/revision is revalidated before the short Manifest/execution commit.
44
+ - Context assembly policy version is `8`.
45
+
46
+ #### Removed
47
+
48
+ - `ContextRequest`, `ContextPolicyDescriptor`, `ContextPolicyRegistry`, and
49
+ `DerivedContentSpec`.
50
+ - Intermediate selection-unit/policy-parts machinery superseded by the typed
51
+ four-category SPI: `Selection::Unit`, `Selection::Validator`,
52
+ `DependencyAwareUnitBuilder`, `RequiredContextResolver`,
53
+ `RecentFirstSelector`, and `TokenBudgetPacker`.
54
+ - Descriptor persistence/reconstruction and old `ContextPlan` fields
55
+ `selected_unit_ids`, `derived_contents`, `ordering_hints`, and
56
+ `policy_descriptor`.
57
+
58
+ ### Workflow Runtime admission and durable terminal barrier (ACS-13)
59
+
60
+ #### Added
61
+
62
+ - ADR-026 defining opaque process-local Workflow admission ownership,
63
+ admission-before-hydration ordering, and FSMSession-integrated terminal
64
+ persistence.
65
+ - Explicit Workflow terminal persistence outcomes: known success, known failure,
66
+ and fail-closed outcome uncertainty.
67
+
68
+ #### Changed
69
+
70
+ - Workflow admission ownership no longer reuses the future `fsm_session_id`.
71
+ EventLoop acquires a separate opaque owner token before durable load and binds
72
+ the concrete FSMSession routing ID only after hydration.
73
+ - Durable Workflow halt/completion now remains nonterminal while the final
74
+ snapshot is saved. The save result returns to the same FSMSession, and only a
75
+ known-successful result permits `HALTED` / `COMPLETED`, admission release, and
76
+ caller Task settlement.
77
+ - Portable known Persistence failures follow the Workflow error path. An
78
+ arbitrary storage/transport error whose commit outcome is not established is
79
+ treated as outcome-unknown: the success barrier stays closed and the
80
+ `workflow_instance_id` admission remains fail-closed/recovery-required.
81
+ - Workflow persistence result handling is backend-topology neutral; the FSM does
82
+ not depend on local/network/database transport details.
83
+
84
+ ### Process-local Agent ownership and Runtime admission (ACS-12)
85
+
86
+ #### Added
87
+
88
+ - ADR-025 defining one mutable live Agent owner per `agent_id` per Runtime and
89
+ EventLoop-owned same-process top-level execution admission.
90
+ - `Agent::Base.get(agent_id)` for process-local live-owner lookup without
91
+ Persistence I/O.
92
+ - `AgentAlreadyExistsError` for create/new identity conflicts and
93
+ `AgentPurgedError` for stale references after successful purge.
94
+
95
+ #### Changed
96
+
97
+ - Repeated `Agent.load(agent_id, persistence:)` returns the exact existing live
98
+ Agent object; a durable-only Agent is hydrated once, and a missing durable
99
+ Agent remains a strict `Persistence::NotFoundError`.
100
+ - `new` / `create` mean creation only and reject an identity that is already
101
+ live or already durable instead of materializing a second mutable owner.
102
+ - EventLoop acquires the same-Agent top-level execution slot before initial
103
+ Offload/Persistence work. Suspension retains that slot; known durable terminal
104
+ completion releases it; uncertain durable outcomes remain fail-closed.
105
+ - Persistence `executions.create_active` / atomic admission remains the durable
106
+ second line of integrity defense rather than the primary same-process lock.
107
+ - Runtime-lifetime Agent ownership is released on clean Runtime shutdown.
108
+ Successful `purge!` is the explicit earlier destruction boundary and makes
109
+ the stale Agent object permanently unusable before the identity can be reused.
110
+
111
+ ### EventLoop single-writer Agent Runtime (ACS-11)
112
+
113
+ #### Added
114
+
115
+ - ADR-024 defining EventLoop as the single writer of Phronomy-managed live
116
+ Agent execution state and operation-specific Offload command/result apply.
117
+ - Runtime read-only Agent execution-owner lookup for approval/live-owner
118
+ routing without exposing mutable execution internals.
119
+ - Provider result authority using current FSMSession/FSM state plus semantic
120
+ `llm_call_id`; stale Provider results are consumed without advancing the FSM.
121
+
122
+ #### Changed
123
+
124
+ - Agent initial preparation, follow-up Manifest preparation, approval resume,
125
+ and terminal durable commits now return values from OffloadPool and apply
126
+ committed live-state advances only on EventLoop.
127
+ - AgentInvocation owns FSM-local uncommitted Provider/Tool/runtime facts.
128
+ - Tool authorization captures Agent identity and Tool description data before
129
+ worker execution; the authorization worker receives no live Agent, Tool, or
130
+ ToolInvocation reference. Application-owned approval/facts/requirement
131
+ callables remain explicit behavior handles.
132
+ - `ApprovalEvaluationRequest` is value-only: Agent identity is exposed as
133
+ `agent_id`, `agent_definition_id`, and `agent_definition_version`; Tool
134
+ identity/description is exposed through `tool_name` / `tool_schema`.
135
+ - Tool authorization/execution outcomes carry semantic `tool_invocation_id`
136
+ and are applied by the Tool FSMSession.
137
+
138
+ #### Removed
139
+
140
+ - `AgentExecutionActivation`, `Agent::ActivationRegistry`,
141
+ `Runtime#__agent_activations`, and the `phronomy_activation` config bridge.
142
+ - Live-object accessors `ApprovalEvaluationRequest#agent` and
143
+ `ApprovalEvaluationRequest#tool`; approval policies use value identity and
144
+ Tool-description fields instead.
145
+
146
+
147
+ ### Semantic Multi-Agent Handoff and shared Selection
148
+
149
+ #### Added
150
+
151
+ - `Phronomy::MultiAgent::HandoffPolicy` with required, forbidden, and selectable
152
+ transfer rules for current request, history, Knowledge, and Tool exchanges.
153
+ - `Phronomy::MultiAgent::Runner.new(main_agent:, handoffs:)` as the public
154
+ semantic Handoff coordinator.
155
+ - Typed private Handoff request/context/provenance values, explicit
156
+ `AgentExecution` `:handed_off` terminal semantics, and Runtime/EventLoop-owned
157
+ active-Agent coordination.
158
+ - Shared `Phronomy::Agent::Selection::Candidate`, `Unit`, `Constraint`, and
159
+ validation machinery for Context and Handoff selection.
160
+ - ADR-016 and the 0.22 migration guide for the semantic Handoff clean break.
161
+
162
+ #### Changed
163
+
164
+ - Handoff is now an explicit Source-to-Target responsibility transfer rather
165
+ than sentinel Tool-result routing. Generated Handoff Tool names are private
166
+ transport details only.
167
+ - Handoff Context is projected from the effective Source Manifest, materialized
168
+ immutably with provenance, and may cross Agents backed by different Persistence
169
+ adapters without adopting Source material into Target Journal/Knowledge.
170
+ - Target Context Policy remains the final per-LLM-call selection authority;
171
+ transferred material enters Target assembly as selectable Context candidates.
172
+ - The active Target persists across user turns and Runner-facade recreation while
173
+ the same main Agent instance and Runtime remain alive. Runtime/process reset
174
+ intentionally does not restore active-Agent continuation.
175
+ - Context assembly policy version is now `7` for the shared Selection and
176
+ Handoff-Context contract.
177
+
178
+ #### Removed
179
+
180
+ - `Phronomy::Agent::Runner`, the `agents:` / `routes:` Runner API, sentinel-map
181
+ routing, and Agent-owned `_add_handoff_tool` / `_handoff_tools` mutation.
182
+ - `Phronomy::Agent::ContextCandidate` and
183
+ `Phronomy::Agent::ContextSelectionUnit`; internal callers use the shared
184
+ `Agent::Selection` model without compatibility aliases.
185
+
15
186
  ### Unified Persistence and durable-state ownership
16
187
 
17
188
  #### Added
18
189
 
19
190
  - `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.
191
+ - Runtime-local Agent execution ownership outside Persistence; ACS-11 now places the mutable live-state authority on EventLoop.
21
192
  - `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`.
193
+ - 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
194
  - ADR-014 and the 0.19 migration guide for the unified durable-state architecture.
24
195
 
25
196
  #### Changed
26
197
 
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.
198
+ - 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
199
  - `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
200
  - 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.
201
+ - 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`.
202
+ - 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.
203
+ - Workflow `workflow_instance_id` is distinct from one concrete Runtime FSMSession identity; generic application `session_id` is not a Phronomy core domain identity.
33
204
 
34
205
  #### Removed
35
206
 
36
207
  - `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.
208
+ - `Persistence#activations`; live Agent execution state is Runtime-only. The transitional ActivationRegistry is subsequently removed by ACS-11.
38
209
  - 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
210
 
40
211
  ### Public API façade and typed contracts
@@ -59,6 +230,25 @@ Release history for 0.14.0 and earlier is archived in
59
230
  return `Task`.
60
231
  - `InvocationContext` construction is classified consistently with its documented
61
232
  Beta application API.
233
+ - Generic Agent invocation identity is removed: `InvocationContext` no longer
234
+ exposes `thread_id` / `session_id`, Agent invocation APIs no longer accept
235
+ `thread_id:`, and no replacement generic correlation identity is introduced.
236
+ - The canonical `JournalRecord` representation no longer contains
237
+ `correlation_id`; legacy durable Hashes containing the removed key remain
238
+ readable without an eager data rewrite.
239
+ - Tool approval notification/policy parent identity now uses canonical Agent
240
+ `execution_id` instead of `agent_invocation_id`; new suspended-execution
241
+ approval data uses the same parent identity.
242
+ - Legacy embedded suspended approval hashes remain readable by deriving the
243
+ logical parent from their enclosing Agent execution; historical
244
+ content-addressed audit bodies are not rewritten.
245
+ - Agent, Tool, and Multi-Agent concrete `FSMSession` instances no longer reuse
246
+ domain/context IDs as EventLoop routing targets. Async callbacks use
247
+ session-local event sinks, and Provider completion is routed to the owning
248
+ FSMSession before EventLoop validates/applies `llm_call_id`-bound result
249
+ state. Workflow retains its pre-load admission ordering through a private
250
+ single-use FSMSession identity reservation; opaque Workflow admission
251
+ ownership remains ACS-13 work.
62
252
  - OutputParser `parse` is classified as the public subclass extension point that
63
253
  concrete parsers implement.
64
254
 
@@ -133,8 +323,8 @@ Release history for 0.14.0 and earlier is archived in
133
323
  returns a completion handle immediately.
134
324
  - `Tool#call_async` for ordinary cooperative Tools no longer consumes a
135
325
  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
326
+ - Framework-owned short in-memory Tools (TeamCoordinator queue controls and
327
+ SharedState store access) explicitly declare
138
328
  `execution_mode :cooperative` instead of using the blocking-I/O default.
139
329
  - MultiAgent fan-out uses a FanOut FSMSession rather than per-child OS Threads.
140
330
  - `TimerQueue` is driven by EventLoop and owns no Thread.
@@ -172,7 +362,7 @@ Release history for 0.14.0 and earlier is archived in
172
362
  transcript/Knowledge lifecycle semantics.
173
363
  - Active Context tests, integration fixtures, benchmarks, mutation subjects,
174
364
  design documents, and API snapshots now describe the canonical
175
- Journal -> ContextCandidate -> Context Policy -> Manifest architecture.
365
+ Journal -> Selection::Candidate -> Context Policy -> Manifest architecture.
176
366
 
177
367
  #### Removed
178
368
 
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::MultiAgent::HandoffPolicy`
229
+ - `Phronomy::MultiAgent::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 Runtime-local active-Agent continuity.
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).