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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb31ae2fdc7b102461fd91480ea2e2aaeda618fb65cc3eb885a9525ca1e985e2
|
|
4
|
+
data.tar.gz: 2ede56b656b789ff195561ac72cac5df2f51dba410dbee8e3025437985509e61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe717ea7a31272ad7d7247f214c59ca8c5370a80ce80e67d69d8bb14f5e285d779004545e56f3298e85d63ca7352ba430f2912e967453b899c1181e8d00fb77e
|
|
7
|
+
data.tar.gz: e3a45c05b4b8f129e61fa09ae7e23be8916aeae762642f5333fb3876c6d142091d836c8c33d3dcb0a3908d1d1d6d99261bffde3e07d8ad505aa136a0df9f9097
|
data/.mutant.yml
CHANGED
|
@@ -17,6 +17,6 @@ matcher:
|
|
|
17
17
|
- Phronomy::Agent::ContextPolicies::Default
|
|
18
18
|
- Phronomy::Agent::ContextPolicyInputBuilder
|
|
19
19
|
- Phronomy::Agent::ContextPlanValidator
|
|
20
|
-
- Phronomy::
|
|
21
|
-
- Phronomy::
|
|
20
|
+
- Phronomy::Agent::HandoffPolicy
|
|
21
|
+
- Phronomy::Agent::HandoffProjection
|
|
22
22
|
- Phronomy::VectorStore::InMemory
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,24 @@ Release history for 0.14.0 and earlier is archived in
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Add runtime dependency constraint `json < 3` to prevent Faraday/RubyLLM JSON
|
|
18
|
+
parser incompatibilities from resolving in consumer environments.
|
|
19
|
+
|
|
20
|
+
- Run recovered output filtering inside the ordinary Agent FSM so filter
|
|
21
|
+
exceptions commit a failed execution and release admission, including after
|
|
22
|
+
another restart. Preserve explicit filter blocking as the `blocked` status.
|
|
23
|
+
- Propagate a coordinator's blocked outcome, and a worker's blocked outcome
|
|
24
|
+
under `on_error: :raise`, to Team failure. Preserve skipped worker errors and
|
|
25
|
+
committed failure precedence over later cancellation across restart.
|
|
26
|
+
- Keep Recovery content materialization, approval restoration reads, and F1
|
|
27
|
+
resolution readback off EventLoop. Apply prepared invocation state only on
|
|
28
|
+
EventLoop, reject stale results, and retain confirmed resolution facts when
|
|
29
|
+
later content reads fail.
|
|
30
|
+
- Avoid reading Orchestrator Knowledge again while constructing invocation-owned
|
|
31
|
+
Tools; those children inherit the Knowledge already saved with their slots.
|
|
32
|
+
|
|
15
33
|
---
|
|
16
34
|
|
|
17
35
|
## [0.23.0] - 2026-08-29
|
data/CONTRIBUTING.md
CHANGED
|
@@ -225,8 +225,8 @@ The authoritative subject list is `.mutant.yml`. It currently includes:
|
|
|
225
225
|
- `Phronomy::Agent::ContextPolicies::Default`
|
|
226
226
|
- `Phronomy::Agent::ContextPolicyInputBuilder`
|
|
227
227
|
- `Phronomy::Agent::ContextPlanValidator`
|
|
228
|
-
- `Phronomy::
|
|
229
|
-
- `Phronomy::
|
|
228
|
+
- `Phronomy::Agent::HandoffPolicy`
|
|
229
|
+
- `Phronomy::Agent::HandoffProjection`
|
|
230
230
|
- `Phronomy::VectorStore::InMemory`
|
|
231
231
|
|
|
232
232
|
The nightly mutation matrix mirrors this authoritative list so each subject can
|
data/README.md
CHANGED
|
@@ -19,7 +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
|
|
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.
|
|
23
23
|
- **EventLoop + FSMSession** — the framework control plane for logical lifecycle coordination.
|
|
24
24
|
- **OffloadPool** — bounded operating-system-thread execution boundary for synchronous work that must not run on EventLoop.
|
|
25
25
|
- **Task** — the common thread-free completion handle returned by Phronomy asynchronous APIs, including OffloadPool-backed work.
|
|
@@ -20,19 +20,19 @@ returns control to the caller. Handoff changes the active Agent for the current
|
|
|
20
20
|
coordination lifetime.
|
|
21
21
|
|
|
22
22
|
Normative Handoff intent is
|
|
23
|
-
[ADR-
|
|
23
|
+
[ADR-030](../decisions/030-agent-handoff-domain-and-durable-responsibility.md).
|
|
24
24
|
|
|
25
25
|
## 2. Public API
|
|
26
26
|
|
|
27
27
|
```ruby
|
|
28
|
-
handoff = Phronomy::
|
|
28
|
+
handoff = Phronomy::Agent::Handoff.new(
|
|
29
29
|
source_agent: triage,
|
|
30
30
|
target_agent: billing,
|
|
31
31
|
description: "Transfer billing responsibility",
|
|
32
32
|
policy: policy
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
runner = Phronomy::
|
|
35
|
+
runner = Phronomy::Agent::HandoffRunner.new(
|
|
36
36
|
main_agent: triage,
|
|
37
37
|
handoffs: [handoff]
|
|
38
38
|
)
|
|
@@ -113,40 +113,35 @@ state changes only through normal Target-owned execution/mutation paths.
|
|
|
113
113
|
A Target Agent execution has its own `execution_id`; Source execution identity is
|
|
114
114
|
provenance/audit context, not Target execution identity.
|
|
115
115
|
|
|
116
|
-
## 9.
|
|
117
|
-
|
|
118
|
-
The
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
- `Phronomy::Agent::Runner`;
|
|
149
|
-
- `agents:` / `routes:` Runner configuration;
|
|
150
|
-
- Agent-owned Handoff Tool registration;
|
|
151
|
-
- generated Tool-name identity;
|
|
152
|
-
- blanket Source history/Knowledge copying.
|
|
116
|
+
## 9. Persisted responsibility and recovery
|
|
117
|
+
|
|
118
|
+
The original main Agent ID anchors `HandoffState`; the active Agent is retained
|
|
119
|
+
across compatible Runtime restarts. The graph must use one Persistence instance.
|
|
120
|
+
Source `handed_off`, its journal/root transition, immutable HandoffContext and the
|
|
121
|
+
reserved Target execution ID commit atomically. The Target is admitted under
|
|
122
|
+
that ID only after an authoritative absence read and the usual Agent admission.
|
|
123
|
+
An active exact Target uses Agent Recovery; a terminal Target result is reused.
|
|
124
|
+
Target terminal settlement and routing stabilization share a transaction.
|
|
125
|
+
|
|
126
|
+
A new Runtime supplies the graph/current definitions again. Tool transport names
|
|
127
|
+
are deterministic from stable Source/Target Agent IDs and are resolved from that
|
|
128
|
+
graph, without a global class registry. Persisted Context is not reprojected under
|
|
129
|
+
current policy. Missing graph edges/definitions or a different Persistence domain
|
|
130
|
+
fail before semantic continuation.
|
|
131
|
+
|
|
132
|
+
`HandoffRunner#result(source_execution_id)` follows retained transfer links.
|
|
133
|
+
`cancel(execution_id)` records a request against that exact turn; pending absent
|
|
134
|
+
Targets are stopped without admission, active Targets use their existing Agent
|
|
135
|
+
cancellation token, terminal outcomes remain immutable. No cancellation or
|
|
136
|
+
observer loss rewinds active responsibility to the main Agent. Cancellation is
|
|
137
|
+
not compensation for external effects; unresolved X0 still needs Agent Recovery.
|
|
138
|
+
|
|
139
|
+
F1 commit response loss is resolved by exact readback. Read/decode failure is not
|
|
140
|
+
absence. F4 recovery requires retained storage and compatible current wiring.
|
|
141
|
+
`on_event` is Runtime-only; terminal results can be read without redelivery.
|
|
142
|
+
|
|
143
|
+
Current graph objects and observers are not durably rehydrated; Application
|
|
144
|
+
supplies compatible wiring. Source execution identity links each completed
|
|
145
|
+
transfer to its exact reserved Target execution, independently of later turns.
|
|
146
|
+
`Persistence#handoff_result(source_execution_id)` reads that turn without loading
|
|
147
|
+
Agent owners, graph definitions, or listeners.
|
|
@@ -12,14 +12,16 @@
|
|
|
12
12
|
`Phronomy::Persistence` is the single durable-state backend abstraction for
|
|
13
13
|
stateful Agents and durable Workflows.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
| Repository | Durable authority |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `contents` | Immutable content and manifests |
|
|
18
|
+
| `agents` | AgentRoot |
|
|
19
|
+
| `journals` | Append-only Agent journal |
|
|
20
|
+
| `executions` | AgentExecution and owned child coordination |
|
|
21
|
+
| `workflow_states` | Workflow snapshots |
|
|
22
|
+
| `handoff_states` | Active responsibility and exact Target reservation |
|
|
23
|
+
| `teams` | TeamRoot |
|
|
24
|
+
| `team_executions` | Team tasks, assignments and outcomes |
|
|
23
25
|
|
|
24
26
|
Persistence stores defined durable logical state. It is not serialization of the
|
|
25
27
|
currently running Runtime object graph.
|
|
@@ -173,3 +175,12 @@ Therefore:
|
|
|
173
175
|
|
|
174
176
|
InMemory implementation mechanisms do not become requirements for external
|
|
175
177
|
backends unless the Backend SPI states the corresponding semantic property.
|
|
178
|
+
|
|
179
|
+
## Durable semantic coordination extension
|
|
180
|
+
|
|
181
|
+
ADR-031 extends the single transaction domain to `handoff_states`, `teams` and
|
|
182
|
+
`team_executions`, alongside the existing five repositories. Current root and
|
|
183
|
+
transaction views require all eight. Explicit identity/revision/admission metadata
|
|
184
|
+
remains separate from opaque DurableRecord payloads. See the normative
|
|
185
|
+
[backend contract](../persistence-backends.md) and
|
|
186
|
+
[recovery clarifications](../design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md).
|
data/docs/architecture.md
CHANGED
|
@@ -50,7 +50,7 @@ explicit compatibility decision.
|
|
|
50
50
|
| Journal-backed Knowledge and retrieval integration | [Knowledge and RAG](architecture/knowledge-and-rag.md) |
|
|
51
51
|
| Filter, Context trust policy, approval, and isolation boundaries | [Security Boundaries](architecture/security-boundaries.md) |
|
|
52
52
|
| Automatic logical-operation tracing and custom tracer SPI | [Tracing](architecture/tracing.md) |
|
|
53
|
-
|
|
|
53
|
+
| Durable Agent-domain responsibility transfer | [Multi-Agent Handoff](architecture/multi-agent-handoff.md) |
|
|
54
54
|
| Durable state, Runtime ownership, recovery, and codec boundaries | [Persistence](architecture/persistence.md) |
|
|
55
55
|
| Request-scoped pre-Manifest customization | [before_llm_input](architecture/before-llm-input.md) |
|
|
56
56
|
| Removed Agent Context / Memory architectures that must not return | [Removed Agent Context Architecture](architecture/removed/agent-context.md) |
|
|
@@ -80,3 +80,10 @@ It must not be used as a current implementation or compatibility contract.
|
|
|
80
80
|
|
|
81
81
|
The old `spec/design/` documentation location is not part of the current
|
|
82
82
|
documentation architecture.
|
|
83
|
+
|
|
84
|
+
Durable multi-agent coordination is described by [ADR-029](decisions/029-semantic-completion-and-application-effect-boundary.md),
|
|
85
|
+
[ADR-030](decisions/030-agent-handoff-domain-and-durable-responsibility.md) and
|
|
86
|
+
[ADR-031](decisions/031-durable-multi-agent-coordination.md).
|
|
87
|
+
TeamExecution is a purpose-specific CAS authority delegating coordinator/workers
|
|
88
|
+
to ordinary Agents, with no Team FSMSession or second Workflow engine.
|
|
89
|
+
Static subagent reservation lives in the existing parent AgentExecution metadata.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Superseded by [ADR-030](030-agent-handoff-domain-and-durable-responsibility.md).
|
|
6
|
+
The semantic Context-transfer rationale is retained in ADR-030; the namespace and
|
|
7
|
+
Runtime-only routing contract below are historical.
|
|
6
8
|
|
|
7
9
|
## Context
|
|
8
10
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR-028: Replay-safe recovery for durably admitted `:preparing` Agent executions
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
ADR-018 defines Agent execution resumption as continuation of the same logical
|
|
8
|
+
`execution_id` from durable recovery state when there is no unresolved semantic
|
|
9
|
+
operation that prevents safe continuation. The implementation already persists a
|
|
10
|
+
new `AgentExecution` in `status: :preparing, phase: :preparing` before input
|
|
11
|
+
filters, `before_llm_input`, Context Policy, and Manifest finalization run.
|
|
12
|
+
|
|
13
|
+
Before this decision, process loss in that window left a durable active execution
|
|
14
|
+
with no automatic continuation path.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
A `:preparing` execution may be resumed automatically only when the framework
|
|
19
|
+
durably recorded `preparation_replayable == true`. Missing, false, or unsupported
|
|
20
|
+
values fail closed. The recovered continuation keeps the same `execution_id`.
|
|
21
|
+
|
|
22
|
+
The preparation region is **replay-safe, not deterministic**. Input filtering,
|
|
23
|
+
`before_llm_input`, Context Policy, retrieval, and other preparation work may be
|
|
24
|
+
executed again after process loss. Applications must ensure those callbacks are
|
|
25
|
+
safe under at-least-once execution. The framework does not require repeated
|
|
26
|
+
preparation to produce byte-identical results.
|
|
27
|
+
|
|
28
|
+
Before Manifest finalization, preparation results may be recomputed. After the
|
|
29
|
+
Manifest is durably committed, the Manifest is the authority and existing
|
|
30
|
+
post-Manifest recovery rules apply.
|
|
31
|
+
|
|
32
|
+
### Durable application context
|
|
33
|
+
|
|
34
|
+
`config` remains a runtime/application Hash and is not generally durable. An
|
|
35
|
+
Application value that affects preparation semantics and must survive restart is
|
|
36
|
+
placed under one reserved key:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
config: {
|
|
40
|
+
durable_context: {
|
|
41
|
+
"tenant" => "A",
|
|
42
|
+
"search_profile" => "legal"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`config[:durable_context]`, when present, must be a Hash accepted by
|
|
48
|
+
`Phronomy::CanonicalJSON`. The framework performs a Canonical JSON round trip
|
|
49
|
+
before execution admission and uses the detached immutable snapshot for both the
|
|
50
|
+
initial run and any recovery replay. `durable_context: nil` and non-Hash values
|
|
51
|
+
are rejected before an `AgentExecution` is created. Missing and explicit `{}` are
|
|
52
|
+
distinct.
|
|
53
|
+
|
|
54
|
+
The snapshot is stored in the Content Store and the execution retains only
|
|
55
|
+
`durable_context_ref`.
|
|
56
|
+
|
|
57
|
+
### Conservative replay eligibility
|
|
58
|
+
|
|
59
|
+
This change intentionally does not add a new arbitrary raw-input serialization
|
|
60
|
+
format. Current initial admission stores `extract_message(input)` as text, while
|
|
61
|
+
filters and instruction construction may inspect the original Ruby input.
|
|
62
|
+
Therefore automatic `:preparing` replay is enabled only for String invocation
|
|
63
|
+
inputs. Non-String inputs fail closed after process loss at this phase.
|
|
64
|
+
|
|
65
|
+
Automatic preparation replay is also disabled when the framework can see a
|
|
66
|
+
Runtime-only semantic dependency that it cannot reconstruct, including:
|
|
67
|
+
|
|
68
|
+
- Multi-Agent handoff/routing wiring;
|
|
69
|
+
- a custom Agent invocation approval policy;
|
|
70
|
+
- invocation-context approval/redaction/token-budget policy values.
|
|
71
|
+
|
|
72
|
+
These conditions are represented by the single durable boolean
|
|
73
|
+
`preparation_replayable`; the framework does not persist those Runtime objects.
|
|
74
|
+
A true value records that no framework-known blocker was present; it does not
|
|
75
|
+
replace the Application replay-safety contract for callbacks or custom config.
|
|
76
|
+
|
|
77
|
+
Applications that use other ordinary `config` entries to influence preparation,
|
|
78
|
+
Provider-adapter, Tool, or other continuation semantics are responsible for moving
|
|
79
|
+
restart-required values into `config[:durable_context]` and reading them from that
|
|
80
|
+
sub-Hash after recovery.
|
|
81
|
+
|
|
82
|
+
### Agent definition compatibility
|
|
83
|
+
|
|
84
|
+
Existing `agent_definition_id` / `agent_definition_version` load validation
|
|
85
|
+
continues to guard runtime definition compatibility. Applications must increment
|
|
86
|
+
the Agent definition version when a change to filters, hooks, Context Policy, or
|
|
87
|
+
other preparation behavior is not recovery-compatible.
|
|
88
|
+
|
|
89
|
+
## Non-goals
|
|
90
|
+
|
|
91
|
+
This decision does not add:
|
|
92
|
+
|
|
93
|
+
- recovery of caller `Task` objects;
|
|
94
|
+
- durable Multi-Agent active routing or Handoff Context;
|
|
95
|
+
- a durable execution-query API;
|
|
96
|
+
- a new Recovery event;
|
|
97
|
+
- Content Store garbage collection;
|
|
98
|
+
- Workflow checkpoint changes;
|
|
99
|
+
- deterministic replay of Application callbacks.
|
|
100
|
+
|
|
101
|
+
## Consequences
|
|
102
|
+
|
|
103
|
+
Direct, replay-safe String invocations can continue the same durable logical
|
|
104
|
+
execution after process loss in the initial preparation window. Unsupported or
|
|
105
|
+
ambiguous cases remain fail-closed rather than being guessed or silently
|
|
106
|
+
abandoned.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# ADR-029: Semantic Completion and Application Effect Boundary
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted. V2 revision 2, 2026-09-06.
|
|
6
|
+
|
|
7
|
+
User approval covers the V2 boundary and the five recovery-contract clarifications.
|
|
8
|
+
Acceptance is design authority; it is not a claim of repository integration or test success.
|
|
9
|
+
|
|
10
|
+
## Date
|
|
11
|
+
|
|
12
|
+
2026-09-06
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
Phronomy durably records Agent semantic execution progress and terminal outcomes.
|
|
17
|
+
After a terminal durable commit, Runtime may notify Application code through
|
|
18
|
+
`on_event` and settle process-local caller Tasks.
|
|
19
|
+
|
|
20
|
+
A previous proposal attempted to make terminal callback delivery itself durable
|
|
21
|
+
across process loss by storing a pending-delivery descriptor, rediscovering
|
|
22
|
+
pending callbacks on `Agent.load`, and durably acknowledging callback attempts.
|
|
23
|
+
|
|
24
|
+
That approach crossed an architectural boundary.
|
|
25
|
+
|
|
26
|
+
An arbitrary Application callback may perform an external effect outside
|
|
27
|
+
Phronomy's Persistence transaction domain. Phronomy cannot make that effect
|
|
28
|
+
exactly once. A process can die after the callback has produced an effect but
|
|
29
|
+
before Phronomy records an acknowledgement, so retrying the callback still
|
|
30
|
+
requires Application-level idempotency.
|
|
31
|
+
|
|
32
|
+
Maintaining a framework outbox/ACK protocol therefore does not remove the
|
|
33
|
+
Application responsibility that matters most, while it adds persistence indexes,
|
|
34
|
+
recovery ordering, acknowledgement reconciliation and callback-specific state to
|
|
35
|
+
the Agent execution engine.
|
|
36
|
+
|
|
37
|
+
## Decision
|
|
38
|
+
|
|
39
|
+
### 1. Semantic terminal state remains the durable boundary
|
|
40
|
+
|
|
41
|
+
The existing AgentExecution terminal statuses remain semantic terminal states:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
completed
|
|
45
|
+
handed_off
|
|
46
|
+
failed
|
|
47
|
+
cancelled
|
|
48
|
+
rejected
|
|
49
|
+
blocked
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Phronomy does not add a callback-only `:completing` status.
|
|
53
|
+
|
|
54
|
+
A known-successful terminal durable transition ends the logical AgentExecution and
|
|
55
|
+
releases normal Agent admission according to ADR-025.
|
|
56
|
+
|
|
57
|
+
### 2. Terminal semantic result/error evidence is durable
|
|
58
|
+
|
|
59
|
+
The terminal transaction continues to persist the canonical semantic evidence
|
|
60
|
+
already required by Agent durability, including as applicable:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
terminal AgentExecution
|
|
64
|
+
AgentRoot revision/lifecycle state
|
|
65
|
+
Journal terminal facts
|
|
66
|
+
result_ref / error_ref
|
|
67
|
+
Provider / Tool durable evidence already owned by Agent execution
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Process loss after this commit must not cause the semantic execution to be rerun
|
|
71
|
+
merely because the Application did not observe its completion callback.
|
|
72
|
+
|
|
73
|
+
### 3. `on_event` is a process-local observation contract
|
|
74
|
+
|
|
75
|
+
Application `on_event` callbacks are Runtime observations.
|
|
76
|
+
|
|
77
|
+
Phronomy invokes them in the current process according to the existing callback
|
|
78
|
+
error policy, but does not create a restart-spanning delivery obligation.
|
|
79
|
+
|
|
80
|
+
Phronomy does not persist:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
terminal_delivery
|
|
84
|
+
delivery_pending
|
|
85
|
+
callback attempt_count
|
|
86
|
+
callback acknowledgement state
|
|
87
|
+
callback/Proc/Task references
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`Agent.load` does not scan for or redeliver missed terminal callbacks.
|
|
91
|
+
|
|
92
|
+
### 4. Callback loss after process loss is allowed
|
|
93
|
+
|
|
94
|
+
The following failure is explicitly permitted:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
terminal semantic commit succeeds
|
|
98
|
+
-> process dies before Application callback
|
|
99
|
+
-> callback is not reconstructed/redelivered
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The semantic outcome remains authoritative and must not be replayed.
|
|
103
|
+
|
|
104
|
+
An Application that requires restart-spanning notification must implement that
|
|
105
|
+
requirement in an Application-owned durable mechanism such as:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
outbox
|
|
109
|
+
job queue
|
|
110
|
+
database transaction/status row
|
|
111
|
+
idempotency key
|
|
112
|
+
Application Workflow
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 5. Callback effects are outside Phronomy's exactly-once guarantee
|
|
116
|
+
|
|
117
|
+
Phronomy does not claim exactly-once semantics for:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
email
|
|
121
|
+
webhook
|
|
122
|
+
external database mutation
|
|
123
|
+
message-broker publication
|
|
124
|
+
arbitrary Application callback side effect
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Applications own idempotency/deduplication appropriate to those systems.
|
|
128
|
+
|
|
129
|
+
### 6. Caller Tasks remain Runtime-only
|
|
130
|
+
|
|
131
|
+
A caller-facing Task can observe same-process success/failure and callback policy.
|
|
132
|
+
It is not rehydrated after process loss.
|
|
133
|
+
|
|
134
|
+
No durable state exists solely to recreate or settle a lost caller Task.
|
|
135
|
+
|
|
136
|
+
### 7. Handoff routing is independent from callback delivery
|
|
137
|
+
|
|
138
|
+
A Source Agent may terminalize as `:handed_off`.
|
|
139
|
+
|
|
140
|
+
Durable Handoff responsibility transfer is governed by ADR-030 and does not depend
|
|
141
|
+
on whether a local `:handoff` Application event was observed.
|
|
142
|
+
|
|
143
|
+
Losing the local callback must not lose the Target routing state.
|
|
144
|
+
|
|
145
|
+
### 8. Read-only outcome access and execution discovery
|
|
146
|
+
|
|
147
|
+
Applications must be able to query an execution's owner, status and durable
|
|
148
|
+
result/error by exact semantic execution ID without invoking/recovering it or
|
|
149
|
+
redelivering callbacks. If admission committed before the caller received its ID,
|
|
150
|
+
a public discovery path from the known Agent/Team identity must cover retained
|
|
151
|
+
terminal as well as active executions. Candidate discovery does not guarantee
|
|
152
|
+
request deduplication or unambiguous correlation. Existing retention applies.
|
|
153
|
+
|
|
154
|
+
Reuse existing APIs where they meet this contract; map missing capabilities only
|
|
155
|
+
after baseline inspection. Do not invent a framework outbox or request registry.
|
|
156
|
+
See [RC-01](RECOVERY_CONTRACT_CLARIFICATIONS.md#1-rc-01--確定結果の参照と実行の発見).
|
|
157
|
+
|
|
158
|
+
### 9. Unknown commit outcome and cancellation
|
|
159
|
+
|
|
160
|
+
A failed read is not proof of absence. A lost commit acknowledgement requires
|
|
161
|
+
readback with the same operation/reserved identities before new semantic work.
|
|
162
|
+
Unresolved storage uncertainty follows existing Persistence error/retry rules,
|
|
163
|
+
not Application factual invention (RC-02).
|
|
164
|
+
|
|
165
|
+
Stopping observation, losing a caller, or shutting down Runtime does not by
|
|
166
|
+
itself request semantic cancellation. Explicit semantic cancellation uses the
|
|
167
|
+
existing Agent contract and preserves exact child identities and confirmed
|
|
168
|
+
outcomes through its existing terminal/settlement boundaries (RC-04).
|
|
169
|
+
|
|
170
|
+
The guarantee is reuse of confirmed durable outcomes and recovery of unfinished
|
|
171
|
+
executions under the same semantic identity. Unknown external Provider/Tool
|
|
172
|
+
effects follow existing Agent Recovery; external effects are not exactly once
|
|
173
|
+
(RC-05).
|
|
174
|
+
|
|
175
|
+
## Persistence / Runtime boundary
|
|
176
|
+
|
|
177
|
+
The ordering remains:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
EventLoop-owned live authority
|
|
181
|
+
-> OffloadPool durable semantic transaction
|
|
182
|
+
-> Persistence commit
|
|
183
|
+
-> EventLoop apply / release admission
|
|
184
|
+
-> optional current-process Application callback
|
|
185
|
+
-> current-process Task settlement
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
No callback acknowledgement transaction follows.
|
|
189
|
+
|
|
190
|
+
## Relationship to existing ADRs
|
|
191
|
+
|
|
192
|
+
- Clarifies ADR-018 X0 boundaries: Application effects remain external.
|
|
193
|
+
- Preserves ADR-023: Runtime routing identities remain non-durable.
|
|
194
|
+
- Preserves ADR-024: EventLoop remains live-state writer.
|
|
195
|
+
- Preserves ADR-025: AgentExecution lifetime ends at semantic terminal commit.
|
|
196
|
+
- Does not alter ADR-028 preparation replay rules.
|
|
197
|
+
- ADR-030/031 may persist additional **semantic coordination facts**, but not
|
|
198
|
+
restart-spanning Application callback delivery.
|
|
199
|
+
|
|
200
|
+
## Required invariants
|
|
201
|
+
|
|
202
|
+
1. Known terminal semantic work is never replayed merely because a callback may
|
|
203
|
+
have been lost.
|
|
204
|
+
2. No terminal callback-delivery metadata/index/ACK protocol is required.
|
|
205
|
+
3. Callbacks, Tasks and external side effects remain Runtime/Application concerns.
|
|
206
|
+
4. Agent terminal statuses remain semantic states, not notification states.
|
|
207
|
+
5. Phronomy does not claim exactly-once external effects.
|
|
208
|
+
6. Read-only result access and retained execution discovery do not trigger work.
|
|
209
|
+
7. Read/commit uncertainty and observation loss do not authorize replacement work.
|
|
210
|
+
|
|
211
|
+
## Non-goals
|
|
212
|
+
|
|
213
|
+
This ADR does not:
|
|
214
|
+
|
|
215
|
+
- provide restart-spanning callback delivery;
|
|
216
|
+
- provide arbitrary callback-side-effect deduplication;
|
|
217
|
+
- recover caller Tasks;
|
|
218
|
+
- introduce an Application outbox inside Persistence;
|
|
219
|
+
- change Handoff routing semantics;
|
|
220
|
+
- change cross-process ownership guarantees.
|