phronomy 0.23.0 → 0.24.1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +2 -2
  3. data/CHANGELOG.md +18 -0
  4. data/CONTRIBUTING.md +2 -2
  5. data/README.md +1 -1
  6. data/docs/architecture/multi-agent-handoff.md +35 -40
  7. data/docs/architecture/persistence.md +19 -8
  8. data/docs/architecture.md +8 -1
  9. data/docs/decisions/016-semantic-multi-agent-handoff.md +3 -1
  10. data/docs/decisions/028-preparing-recovery-replay-contract.md +106 -0
  11. data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +220 -0
  12. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +235 -0
  13. data/docs/decisions/031-durable-multi-agent-coordination.md +301 -0
  14. data/docs/decisions/README.md +5 -1
  15. data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +33 -0
  16. data/docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md +191 -0
  17. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +862 -0
  18. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +106 -0
  19. data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +179 -0
  20. data/docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md +302 -0
  21. data/docs/features.md +35 -1
  22. data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
  23. data/docs/persistence-backends.md +42 -3
  24. data/lib/phronomy/agent/agent_execution.rb +2 -2
  25. data/lib/phronomy/agent/agent_invocation.rb +1 -1
  26. data/lib/phronomy/agent/async_event_api.rb +18 -1
  27. data/lib/phronomy/agent/base.rb +28 -0
  28. data/lib/phronomy/agent/context_assembler.rb +1 -1
  29. data/lib/phronomy/agent/exact_execution.rb +153 -0
  30. data/lib/phronomy/agent/execution_cancellation.rb +25 -0
  31. data/lib/phronomy/agent/execution_coordinator.rb +484 -27
  32. data/lib/phronomy/{multi_agent → agent}/handoff.rb +4 -4
  33. data/lib/phronomy/{multi_agent → agent}/handoff_capability_factory.rb +3 -45
  34. data/lib/phronomy/{multi_agent → agent}/handoff_context.rb +26 -1
  35. data/lib/phronomy/{multi_agent/execution_coordinator.rb → agent/handoff_execution_coordinator.rb} +32 -5
  36. data/lib/phronomy/{multi_agent → agent}/handoff_policy.rb +7 -1
  37. data/lib/phronomy/{multi_agent → agent}/handoff_projection.rb +18 -2
  38. data/lib/phronomy/{multi_agent → agent}/handoff_request.rb +2 -2
  39. data/lib/phronomy/agent/handoff_runner.rb +178 -0
  40. data/lib/phronomy/agent/handoff_state.rb +43 -0
  41. data/lib/phronomy/agent/recovery_coordinator/continuation.rb +114 -211
  42. data/lib/phronomy/agent/recovery_coordinator/installation.rb +84 -130
  43. data/lib/phronomy/agent/recovery_coordinator/resolution.rb +76 -200
  44. data/lib/phronomy/agent/recovery_coordinator.rb +11 -5
  45. data/lib/phronomy/agent/recovery_support.rb +15 -23
  46. data/lib/phronomy/agent/ruby_llm_materializer.rb +2 -8
  47. data/lib/phronomy/agent/tool_invocation.rb +4 -2
  48. data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
  49. data/lib/phronomy/engine/runtime.rb +16 -1
  50. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
  51. data/lib/phronomy/multi_agent/orchestrator.rb +59 -11
  52. data/lib/phronomy/multi_agent/team_coordinator.rb +473 -125
  53. data/lib/phronomy/multi_agent/team_execution.rb +44 -0
  54. data/lib/phronomy/multi_agent/team_root.rb +41 -0
  55. data/lib/phronomy/persistence/durable_codec.rb +60 -0
  56. data/lib/phronomy/persistence/in_memory.rb +264 -2
  57. data/lib/phronomy/persistence/repository_facades.rb +221 -2
  58. data/lib/phronomy/persistence.rb +95 -1
  59. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -0
  60. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +137 -0
  61. data/lib/phronomy/testing/persistence_contract.rb +5 -0
  62. data/lib/phronomy/tools/agent.rb +1 -1
  63. data/lib/phronomy/version.rb +1 -1
  64. data/scripts/api_snapshot.rb +3 -3
  65. data/sig/phronomy/handoff.rbs +41 -0
  66. data/sig/phronomy/multi_agent.rbs +28 -32
  67. data/sig/phronomy/persistence.rbs +64 -3
  68. metadata +44 -12
  69. data/lib/phronomy/multi_agent/coordination_state.rb +0 -18
  70. data/lib/phronomy/multi_agent/coordinator.rb +0 -154
  71. data/lib/phronomy/multi_agent/runner.rb +0 -98
@@ -0,0 +1,191 @@
1
+ # Durable continuation decision ownership
2
+
3
+ Date: 2026-09-07
4
+ Baseline: `c383b64b26e702291e288476e0978641a1c1512f`
5
+
6
+ ## Problem
7
+
8
+ Recovery previously chose its next action twice: once using transient
9
+ `ResolutionResult.continuation` after a factual resolution, and again using
10
+ phase-specific restart branches. A mixed Provider response exposed the gap:
11
+ `recovery_tools_completed` could still have framework calls pending, but the
12
+ restart branch started a Provider follow-up without executing them.
13
+
14
+ Team also interpreted a child result immediately and independently reread its
15
+ saved error references on restart. Cancelled Agent executions carry an error
16
+ reference too. Interpreting the reference as failure changed Team cancellation
17
+ into failure. Earlier code could lose a saved failure after a crash between
18
+ child-result recording and Team terminalization.
19
+
20
+ ## Decision ownership
21
+
22
+ `RecoveryCoordinator::Continuation#recovery_action` derives the next action
23
+ from the saved AgentExecution and the current compatible framework wiring.
24
+ Both factual resolution apply (including F1 readback) and automatic restart
25
+ continuation call `continue_recovery_on_event_loop`. Invocation reconstruction,
26
+ Provider output materialization, pending framework calls, session registration
27
+ and terminal failure handling are shared.
28
+
29
+ `ResolutionResult` carries only the committed execution. It does not carry a
30
+ second continuation label or a transient copy of the failure. The existing
31
+ execution metadata remains the authority after both F1 and F4.
32
+
33
+ | Saved facts | Continuation |
34
+ | --- | --- |
35
+ | Authorized, replayable framework batch with external facts already settled | Reconstruct the existing Tool sessions and exact owned operations |
36
+ | `recovery_provider_completed`, framework calls pending | Restore the saved framework calls and enter normal Tool dispatch |
37
+ | `recovery_provider_completed`, no framework calls pending | Apply output filtering and finish through the Agent terminal barrier |
38
+ | `recovery_tools_completed`, framework calls pending | Restore the saved framework calls and enter normal Tool dispatch |
39
+ | `recovery_tools_completed`, no framework calls pending | Start the Provider follow-up through normal preparation |
40
+ | `recovery_resolved_failed` | Materialize the saved failure and use the Agent terminal barrier |
41
+ | Unresolved external Provider/Tool fact | Deliver the existing factual-resolution request |
42
+
43
+ Initial preparation and approval rejection keep their operation-specific entry
44
+ points. Approval waiting remains suspended. Classification still decides whether
45
+ an execution can be installed automatically; it does not implement an alternative
46
+ continuation for the resolved states above.
47
+
48
+ ## Team decisions
49
+
50
+ `TeamCoordinator#next_run_action` derives a decision from durable Team records.
51
+ Normal execution records the child outcome and returns to this decision, using
52
+ the same path as `resume`. It does not separately terminalize from a local
53
+ `outcome[:error]` value.
54
+
55
+ Decision order:
56
+
57
+ 1. Reconcile the coordinator or an existing reserved assignment through exact
58
+ Agent execution identity and the existing cancellation/recovery machinery.
59
+ 2. Preserve a coordinator `failed` or `blocked` outcome, or either worker
60
+ outcome when `on_error: :raise` is saved.
61
+ 3. Honor the saved cancellation request or a child's explicit `cancelled` state.
62
+ 4. Reserve unassigned work, or aggregate when no work remains.
63
+
64
+ `error_ref` contains diagnostic material; the child `state` defines whether its
65
+ outcome is failure or cancellation. An Agent filter's explicit `blocked` status
66
+ is a Team failure with its diagnostic error, while approval `rejected` remains
67
+ an ordinary result rather than an exception. A failed or blocked worker under
68
+ `on_error: :skip` retains its original assignment state for aggregation. A later Team cancellation
69
+ does not rewrite that assignment. A non-skipped committed failure remains a
70
+ Team failure even if cancellation is subsequently requested.
71
+
72
+ A cancelled, absent reservation is retained without starting a child. An already
73
+ admitted child whose external outcome requires factual resolution keeps the Team
74
+ active and discoverable, including across another restart. Cancellation does not
75
+ supply that missing external fact.
76
+
77
+ ## Responsibility and compatibility
78
+
79
+ - Public APIs, existing persisted statuses/phases and repository schemas remain
80
+ unchanged. No new persisted continuation object or migration is introduced.
81
+ - Framework-owned child execution identities, Tool batches and Team assignments
82
+ remain framework recovery responsibilities.
83
+ - Unknown external Provider/Tool effects still use the existing application
84
+ factual-resolution contract. Resolution does not redispatch the external effect.
85
+ - Scheduler and aggregation remain replay-safe application calculations.
86
+ - Runtime observers and callbacks do not acquire durable delivery guarantees.
87
+ - Agent, Handoff, Team and Workflow retain the domains established by ADR-029,
88
+ ADR-030 and ADR-031. This change does not add another execution engine.
89
+ - Records already terminalized incorrectly by an earlier implementation are not
90
+ rewritten automatically. They require separate, case-specific investigation.
91
+
92
+ This refactor addresses duplicated Recovery continuation and Team outcome
93
+ interpretation. It is not a claim that every state transition across the full
94
+ framework has been model-checked. In particular, ordinary execution versus
95
+ Handoff terminal-commit organization remains outside this change.
96
+
97
+ ## Regression coverage
98
+
99
+ ### Output filtering and blocked child outcomes follow-up
100
+
101
+ The review of main `d938426faf267b5f229f2819b631edf4bcfbc58b` found two gaps.
102
+ Recovery applied output filtering before installing the ordinary FSM session;
103
+ an ordinary filter exception therefore bypassed the terminal barrier and left
104
+ the execution active. Team's centralized failure decision recognized `failed`
105
+ but omitted the separate `blocked` Agent terminal state.
106
+
107
+ Resolved output now enters the normal FSM at `calling_llm` with an
108
+ `llm_completed` event. It does not invoke the Provider again. Output filtering,
109
+ explicit blocking, exceptions, terminal persistence, and admission release
110
+ follow the same path as an ordinary invocation. Prepared Recovery content
111
+ still crosses the existing Offload/EventLoop boundary; this change introduces
112
+ no Persistence reads on EventLoop.
113
+
114
+ `recovery_output_filter_spec.rb` covers 18 combinations: ordinary execution,
115
+ explicit factual resolution, and restart after resolution; output transformation,
116
+ explicit blocking, and a raised exception; invoke and stream modes. Streaming
117
+ uses a minimal text SSE response. The tests assert durable status, exact-once
118
+ filter application within each execution attempt, no Provider replay during
119
+ Recovery, and successful subsequent invocation on the same Agent. Filters
120
+ remain replay-safe application functions, not exactly-once external effects.
121
+
122
+ `team_blocked_outcome_spec.rb` covers four coordinator/worker and raise/skip
123
+ combinations. Each resumes snapshots before and after Team records the child
124
+ outcome, with and without a subsequent cancellation request. The child retains
125
+ `blocked`; the Team fails for a blocked coordinator or non-skipped worker, and
126
+ aggregation receives the error for a skipped worker. A later cancellation
127
+ cannot replace a non-skipped committed failure.
128
+
129
+ ### Persistence I/O boundary follow-up
130
+
131
+ Recovery follows ADR-014/024's prepare/apply boundary. `prepare_plan` reads the
132
+ restart inputs at the existing synchronous load boundary, outside EventLoop.
133
+ Resolution commit, bounded F1 readback, and subsequent content materialization
134
+ run in the existing resolution OffloadPool operation. Approval restoration also
135
+ receives its assistant message as prepared material. Invocation and Tool state,
136
+ chat wiring, output filtering, and session registration remain on EventLoop.
137
+
138
+ `RecoveryMaterial` and `ResolutionPreparation` are private, operation-local
139
+ results. They are not persisted and carry no alternative continuation decision.
140
+ Prepared messages transfer to the new invocation; there is no shared content
141
+ cache or new durable identity. The current saved execution still determines
142
+ the continuation for both resolution and restart.
143
+
144
+ If content materialization fails after a confirmed resolution commit, EventLoop
145
+ retains the confirmed execution and fails the observer without inventing a
146
+ semantic failure or replaying the Provider/Tool. Restart can prepare the same
147
+ saved execution again. If F1 readback itself fails or returns conflicting facts,
148
+ the observer fails and no continuation starts. A late preparation result must
149
+ still match its captured live execution and owner before it can be applied.
150
+ Callbacks post back to the originating Runtime; shutdown never grants a worker
151
+ permission to mutate live state.
152
+
153
+ Invocation-owned Orchestrator Tools use the existing durable child Knowledge
154
+ snapshot. Tool construction does not read that Knowledge again on EventLoop.
155
+ Standalone Tool construction retains its existing caller-side snapshot behavior.
156
+
157
+ The shared F1/F4 fixture now rejects EventLoop content reads, execution loads,
158
+ and transactions. `recovery_io_boundary_spec.rb` additionally covers resolved
159
+ output, restart, approval allow/reject, content/readback failures, stale apply,
160
+ shutdown, and unrelated Agent progress while content or readback I/O is blocked.
161
+ These tests use synchronous InMemory operations and explicit queue barriers;
162
+ they do not establish disk durability or production-adapter latency bounds.
163
+
164
+ `spec/phronomy/multi_agent/durable_continuation_spec.rb` adds 27 examples:
165
+
166
+ | Coverage | Examples |
167
+ | --- | ---: |
168
+ | Six executable regressions from the two reviews | 6 |
169
+ | Six Provider call compositions, with/without F1 resolution response loss | 12 |
170
+ | Coordinator/worker cancellation, before/after Team outcome recording | 4 |
171
+ | Skipped worker failure, resumed with/without subsequent cancellation | 1 |
172
+ | Provider/Tool failed or not-performed resolutions, F1 and F4 | 4 |
173
+
174
+ The composition matrix includes no calls, external only, framework only, both
175
+ orders of a mixed response, and multiple external calls around a framework call.
176
+ Each example resumes snapshots of the Provider response resolution and each
177
+ external fact resolution. Framework compositions also resume child reservation
178
+ and child terminal snapshots. Assertions cover final results, Provider call
179
+ counts, absence of external effect replay, and retention of already reserved
180
+ child execution IDs.
181
+
182
+ The two saved-failure regression examples also test a subsequent cancellation.
183
+ The existing durable coordination suite additionally verifies cancellation while
184
+ a coordinator still requires external factual resolution, including another
185
+ restart. Shared fixtures live under `spec/phronomy/multi_agent/support/` and are
186
+ loaded explicitly by the two durable suites.
187
+
188
+ F4 tests restore committed InMemory DurableRecords in a fresh backend and Runtime;
189
+ they do not kill a real OS process or validate every production Persistence
190
+ adapter. F1 tests raise after an actual in-memory transaction commit. Provider
191
+ responses are supplied by WebMock, with no live LLM calls.