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.
- checksums.yaml +4 -4
- data/.mutant.yml +2 -2
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +1 -1
- data/docs/architecture/multi-agent-handoff.md +35 -40
- data/docs/architecture/persistence.md +19 -8
- data/docs/architecture.md +8 -1
- data/docs/decisions/016-semantic-multi-agent-handoff.md +3 -1
- data/docs/decisions/028-preparing-recovery-replay-contract.md +106 -0
- data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +220 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +235 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +301 -0
- data/docs/decisions/README.md +5 -1
- data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +33 -0
- data/docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md +191 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +862 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +106 -0
- data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +179 -0
- data/docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md +302 -0
- data/docs/features.md +35 -1
- data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
- data/docs/persistence-backends.md +42 -3
- data/lib/phronomy/agent/agent_execution.rb +2 -2
- data/lib/phronomy/agent/agent_invocation.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +18 -1
- data/lib/phronomy/agent/base.rb +28 -0
- data/lib/phronomy/agent/context_assembler.rb +1 -1
- data/lib/phronomy/agent/exact_execution.rb +153 -0
- data/lib/phronomy/agent/execution_cancellation.rb +25 -0
- data/lib/phronomy/agent/execution_coordinator.rb +484 -27
- data/lib/phronomy/{multi_agent → agent}/handoff.rb +4 -4
- data/lib/phronomy/{multi_agent → agent}/handoff_capability_factory.rb +3 -45
- data/lib/phronomy/{multi_agent → agent}/handoff_context.rb +26 -1
- data/lib/phronomy/{multi_agent/execution_coordinator.rb → agent/handoff_execution_coordinator.rb} +32 -5
- data/lib/phronomy/{multi_agent → agent}/handoff_policy.rb +7 -1
- data/lib/phronomy/{multi_agent → agent}/handoff_projection.rb +18 -2
- data/lib/phronomy/{multi_agent → agent}/handoff_request.rb +2 -2
- data/lib/phronomy/agent/handoff_runner.rb +178 -0
- data/lib/phronomy/agent/handoff_state.rb +43 -0
- data/lib/phronomy/agent/recovery_coordinator/continuation.rb +114 -211
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +84 -130
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +76 -200
- data/lib/phronomy/agent/recovery_coordinator.rb +11 -5
- data/lib/phronomy/agent/recovery_support.rb +15 -23
- data/lib/phronomy/agent/ruby_llm_materializer.rb +2 -8
- data/lib/phronomy/agent/tool_invocation.rb +4 -2
- data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
- data/lib/phronomy/engine/runtime.rb +16 -1
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +59 -11
- data/lib/phronomy/multi_agent/team_coordinator.rb +473 -125
- data/lib/phronomy/multi_agent/team_execution.rb +44 -0
- data/lib/phronomy/multi_agent/team_root.rb +41 -0
- data/lib/phronomy/persistence/durable_codec.rb +60 -0
- data/lib/phronomy/persistence/in_memory.rb +264 -2
- data/lib/phronomy/persistence/repository_facades.rb +221 -2
- data/lib/phronomy/persistence.rb +95 -1
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -0
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +137 -0
- data/lib/phronomy/testing/persistence_contract.rb +5 -0
- data/lib/phronomy/tools/agent.rb +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/scripts/api_snapshot.rb +3 -3
- data/sig/phronomy/handoff.rbs +41 -0
- data/sig/phronomy/multi_agent.rbs +28 -32
- data/sig/phronomy/persistence.rbs +64 -3
- metadata +44 -12
- data/lib/phronomy/multi_agent/coordination_state.rb +0 -18
- data/lib/phronomy/multi_agent/coordinator.rb +0 -154
- 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.
|