phronomy 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +3 -4
- data/CHANGELOG.md +215 -10
- data/CONTRIBUTING.md +81 -9
- data/README.md +15 -6
- data/VERIFY.sh +587 -0
- data/benchmark/bench_agent_invoke.rb +2 -2
- data/benchmark/bench_context_assembler.rb +39 -68
- data/benchmark/bench_regression.rb +2 -2
- data/docs/architecture/agent-context.md +174 -0
- data/docs/architecture/before-llm-input.md +78 -0
- data/docs/architecture/context-management.md +232 -0
- data/docs/architecture/knowledge-and-rag.md +130 -0
- data/docs/architecture/multi-agent-handoff.md +147 -0
- data/docs/architecture/persistence.md +186 -0
- data/docs/architecture/removed/agent-context.md +72 -0
- data/docs/architecture/security-boundaries.md +173 -0
- data/docs/architecture/tracing.md +194 -0
- data/docs/architecture.md +89 -0
- data/docs/archive/design/archived/04_api_design.md +507 -0
- data/docs/archive/design/archived/09_guardrails.md +186 -0
- data/docs/archive/design/archived/17_rails_integration.md +175 -0
- data/docs/archive/design/historical/00_design_philosophy.md +122 -0
- data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
- data/docs/archive/design/historical/06_design_decisions.md +143 -0
- data/docs/changelog/0.14-and-earlier.md +1 -1
- data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
- data/docs/decisions/002-workflow-context-immutability.md +26 -1
- data/docs/decisions/006-no-built-in-guardrails.md +2 -1
- data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
- data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
- data/docs/decisions/016-semantic-multi-agent-handoff.md +114 -0
- data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
- data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
- data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
- data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
- data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
- data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
- data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
- data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
- 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 +176 -0
- 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 +65 -11
- data/docs/getting-started.md +77 -45
- data/docs/migrations/0.19.md +14 -7
- data/docs/migrations/0.22.md +390 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
- data/docs/persistence-backends.md +129 -40
- data/docs/runtime-and-concurrency.md +227 -33
- data/examples/README.md +13 -0
- data/lib/phronomy/agent/agent_execution.rb +21 -17
- data/lib/phronomy/agent/agent_invocation.rb +288 -93
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
- data/lib/phronomy/agent/agent_root.rb +3 -3
- data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
- data/lib/phronomy/agent/async_event_api.rb +162 -72
- data/lib/phronomy/agent/base.rb +416 -181
- data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
- data/lib/phronomy/agent/context_assembler.rb +437 -178
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_plan.rb +18 -13
- data/lib/phronomy/agent/context_plan_validator.rb +246 -88
- data/lib/phronomy/agent/context_policies/default.rb +123 -34
- data/lib/phronomy/agent/context_policy.rb +109 -3
- data/lib/phronomy/agent/context_policy_input.rb +244 -0
- data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
- 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 +2417 -572
- data/lib/phronomy/agent/handoff.rb +43 -0
- data/lib/phronomy/agent/handoff_capability_factory.rb +45 -0
- data/lib/phronomy/agent/handoff_context.rb +120 -0
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +143 -0
- data/lib/phronomy/agent/handoff_policy.rb +143 -0
- data/lib/phronomy/agent/handoff_projection.rb +207 -0
- data/lib/phronomy/agent/handoff_request.rb +45 -0
- data/lib/phronomy/agent/handoff_runner.rb +178 -0
- data/lib/phronomy/agent/handoff_state.rb +43 -0
- data/lib/phronomy/agent/journal_record.rb +17 -4
- data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
- data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
- data/lib/phronomy/agent/llm_operation_result.rb +12 -7
- data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
- data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
- data/lib/phronomy/agent/recovery_coordinator/continuation.rb +174 -0
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +381 -0
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +511 -0
- data/lib/phronomy/agent/recovery_coordinator.rb +217 -0
- data/lib/phronomy/agent/recovery_support.rb +504 -0
- data/lib/phronomy/agent/ruby_llm_materializer.rb +16 -13
- data/lib/phronomy/agent/selection/candidate.rb +53 -0
- data/lib/phronomy/agent/selection/constraint.rb +49 -0
- data/lib/phronomy/agent/shared_state.rb +38 -1
- data/lib/phronomy/agent/tool_approval_request.rb +33 -5
- data/lib/phronomy/agent/tool_definition_set.rb +49 -3
- data/lib/phronomy/agent/tool_invocation.rb +339 -103
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
- data/lib/phronomy/agent.rb +20 -2
- data/lib/phronomy/agent_already_exists_error.rb +5 -0
- data/lib/phronomy/agent_purged_error.rb +5 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
- data/lib/phronomy/engine/event_loop.rb +622 -63
- data/lib/phronomy/engine/fsm_session.rb +194 -21
- data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
- data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
- data/lib/phronomy/engine/runtime.rb +92 -20
- data/lib/phronomy/generator_verifier.rb +12 -14
- data/lib/phronomy/invocation_context.rb +9 -29
- data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
- data/lib/phronomy/multi_agent/orchestrator.rb +71 -26
- 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 +706 -0
- data/lib/phronomy/persistence/durable_record.rb +117 -0
- data/lib/phronomy/persistence/in_memory.rb +471 -133
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
- data/lib/phronomy/persistence/repository_facades.rb +535 -0
- data/lib/phronomy/persistence.rb +174 -40
- data/lib/phronomy/recovery.rb +186 -0
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +2 -1
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- 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/tracing/automatic.rb +176 -0
- data/lib/phronomy/tracing/base.rb +11 -2
- data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +3 -6
- data/lib/phronomy/workflow_context.rb +14 -5
- data/lib/phronomy/workflow_recovery.rb +123 -0
- data/lib/phronomy/workflow_runner.rb +468 -256
- data/lib/phronomy.rb +6 -0
- data/scripts/api_snapshot.rb +12 -0
- data/sig/phronomy/agent.rbs +209 -7
- data/sig/phronomy/handoff.rbs +41 -0
- data/sig/phronomy/multi_agent.rbs +35 -0
- data/sig/phronomy/persistence.rbs +123 -4
- data/sig/phronomy/runtime.rbs +1 -4
- data/sig/phronomy/workflow.rbs +2 -2
- data/sig/phronomy.rbs +10 -0
- metadata +84 -18
- data/examples/workflows/agent_event_mapping.rb +0 -101
- data/examples/workflows/generic_task_event_mapping.rb +0 -66
- data/lib/phronomy/agent/activation_registry.rb +0 -28
- data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
- data/lib/phronomy/agent/context_candidate.rb +0 -47
- data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
- data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
- data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
- data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
- data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
- data/lib/phronomy/agent/context_policy_registry.rb +0 -46
- data/lib/phronomy/agent/context_request.rb +0 -35
- data/lib/phronomy/agent/context_selection_unit.rb +0 -38
- data/lib/phronomy/agent/derived_content_spec.rb +0 -34
- data/lib/phronomy/agent/runner.rb +0 -97
- data/lib/phronomy/multi_agent/handoff.rb +0 -64
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# ADR-019: Filter Contract and Security Boundaries
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted.
|
|
6
|
+
|
|
7
|
+
## Date
|
|
8
|
+
|
|
9
|
+
2026-08-23
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
Phronomy's live implementation and public API have moved from the former
|
|
14
|
+
`Guardrail::*` hierarchy to a unified `Phronomy::Filter::Base` model.
|
|
15
|
+
|
|
16
|
+
The old architecture described separate `InputGuardrail` and
|
|
17
|
+
`OutputGuardrail` base classes, `GuardrailError`, `*_guardrail` registration
|
|
18
|
+
APIs, and a built-in PII detector. Those APIs no longer exist. Current source
|
|
19
|
+
instead exposes explicit Filter call sites for input, output, and Tool
|
|
20
|
+
results, with one Filter abstraction that can transform a value or reject it.
|
|
21
|
+
|
|
22
|
+
ADR-006 preserved useful policy intent — especially avoiding a false sense of
|
|
23
|
+
complete generic security and keeping domain-specific policy
|
|
24
|
+
application-owned — but its normative API vocabulary still described the
|
|
25
|
+
removed Guardrail hierarchy. Leaving that ADR normative makes architecture
|
|
26
|
+
authority disagree with the current Filter contract.
|
|
27
|
+
|
|
28
|
+
This ADR supersedes ADR-006 for the current Filter/security-policy
|
|
29
|
+
architecture while preserving ADR-006 as decision history.
|
|
30
|
+
|
|
31
|
+
## Decision
|
|
32
|
+
|
|
33
|
+
### 1. `Filter::Base` is the current policy-interception abstraction
|
|
34
|
+
|
|
35
|
+
The current public abstraction is:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
Phronomy::Filter::Base
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A Filter receives a value at an explicitly defined call site and may:
|
|
42
|
+
|
|
43
|
+
1. return the original value;
|
|
44
|
+
2. return a transformed value; or
|
|
45
|
+
3. reject the value by raising `Phronomy::FilterBlockError`, normally through
|
|
46
|
+
`Filter::Base#block!`.
|
|
47
|
+
|
|
48
|
+
A Filter is not divided into InputFilter/OutputFilter subclasses. The same
|
|
49
|
+
Filter instance may be registered at more than one call site when that is
|
|
50
|
+
appropriate for the application.
|
|
51
|
+
|
|
52
|
+
### 2. Filter call sites remain explicit
|
|
53
|
+
|
|
54
|
+
Current Agent Filter registration supports distinct call sites:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
input
|
|
58
|
+
output
|
|
59
|
+
Tool result
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Multiple Filters at one call site run in registration order. A blocking
|
|
63
|
+
Filter short-circuits later processing through `FilterBlockError`.
|
|
64
|
+
|
|
65
|
+
The call-site distinction is semantically important. A Filter registered for
|
|
66
|
+
one site does not silently become a universal content-inspection hook.
|
|
67
|
+
|
|
68
|
+
### 3. Input Filter means raw invocation-input filtering today
|
|
69
|
+
|
|
70
|
+
The current `input_filter` / `add_input_filter` path applies to raw invocation
|
|
71
|
+
input before later Context assembly.
|
|
72
|
+
|
|
73
|
+
It does **not** automatically inspect every value that may later contribute to
|
|
74
|
+
an LLM input, including all Knowledge, retrieval results, Tool-derived
|
|
75
|
+
Context, or `before_llm_input` `segment_candidates`.
|
|
76
|
+
|
|
77
|
+
This ADR does not decide whether Phronomy should add a dedicated
|
|
78
|
+
Context-candidate inspection point. That broader trust-boundary question
|
|
79
|
+
remains the separate Filter/Security Boundary review.
|
|
80
|
+
|
|
81
|
+
### 4. `PromptInjectionFilter` is a bounded heuristic baseline
|
|
82
|
+
|
|
83
|
+
Phronomy may provide the lightweight built-in:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
Phronomy::Filter::PromptInjectionFilter
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
as a convenience baseline for common prompt-injection patterns.
|
|
90
|
+
|
|
91
|
+
This Filter is a heuristic pattern detector. Its presence does not mean:
|
|
92
|
+
|
|
93
|
+
- all prompt injection is detected;
|
|
94
|
+
- every untrusted Context source is automatically inspected;
|
|
95
|
+
- Filter registration establishes an LLM security sandbox; or
|
|
96
|
+
- Phronomy provides a complete content-security guarantee.
|
|
97
|
+
|
|
98
|
+
Applications remain responsible for selecting appropriate Filter call sites
|
|
99
|
+
and for additional domain-specific controls.
|
|
100
|
+
|
|
101
|
+
### 5. Generic built-in PII policy is not a Phronomy core guarantee
|
|
102
|
+
|
|
103
|
+
The removed `PIIPatternDetector` architecture is not carried forward.
|
|
104
|
+
|
|
105
|
+
PII definitions, locale-specific identifiers, compliance requirements and
|
|
106
|
+
transformation/rejection policy are application/domain responsibilities
|
|
107
|
+
unless a future explicit architecture decision introduces a new contract.
|
|
108
|
+
|
|
109
|
+
Applications can implement these policies as `Filter::Base` subclasses or
|
|
110
|
+
application-owned components.
|
|
111
|
+
|
|
112
|
+
### 6. Filter/approval/offload are not process-security isolation
|
|
113
|
+
|
|
114
|
+
Filter, Tool approval, EventLoop/OffloadPool execution boundaries and
|
|
115
|
+
application authorization are distinct mechanisms.
|
|
116
|
+
|
|
117
|
+
None of them, by itself, promises:
|
|
118
|
+
|
|
119
|
+
- OS process isolation;
|
|
120
|
+
- container isolation;
|
|
121
|
+
- filesystem/network sandboxing;
|
|
122
|
+
- privilege separation; or
|
|
123
|
+
- containment of arbitrary malicious Tool/application code.
|
|
124
|
+
|
|
125
|
+
If an application needs those properties, the isolation mechanism is
|
|
126
|
+
application/deployment-owned unless Phronomy later defines a specific
|
|
127
|
+
sandbox SPI.
|
|
128
|
+
|
|
129
|
+
### 7. Removed Guardrail contracts stay removed
|
|
130
|
+
|
|
131
|
+
The following legacy architecture is not a current compatibility contract and
|
|
132
|
+
must not be reintroduced merely for naming compatibility:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
Phronomy::Guardrail::Base
|
|
136
|
+
Phronomy::Guardrail::InputGuardrail
|
|
137
|
+
Phronomy::Guardrail::OutputGuardrail
|
|
138
|
+
Phronomy::GuardrailError
|
|
139
|
+
add_input_guardrail
|
|
140
|
+
add_output_guardrail
|
|
141
|
+
input_guardrail
|
|
142
|
+
output_guardrail
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Current code uses `Filter::Base`, `FilterBlockError`, and explicit
|
|
146
|
+
`*_filter` registration APIs.
|
|
147
|
+
|
|
148
|
+
### 8. Historical Guardrail documentation
|
|
149
|
+
|
|
150
|
+
`docs/archive/design/archived/09_guardrails.md` records the removed Guardrail design. It is
|
|
151
|
+
non-normative and is not rewritten into current Filter architecture.
|
|
152
|
+
|
|
153
|
+
The physical move to the repository archive was completed by ACS-02
|
|
154
|
+
(canonical architecture documentation lifecycle migration).
|
|
155
|
+
|
|
156
|
+
### 9. Security-boundary questions deferred at ADR acceptance
|
|
157
|
+
|
|
158
|
+
At the time this ADR was accepted, this decision deliberately did not
|
|
159
|
+
settle the broader Filter/Security Boundary review, including:
|
|
160
|
+
|
|
161
|
+
- whether and where all untrusted Context candidates require inspection;
|
|
162
|
+
- whether a dedicated pre-Manifest Context inspection stage is needed;
|
|
163
|
+
- stronger PromptInjectionFilter guarantees;
|
|
164
|
+
- Tool sandbox architecture;
|
|
165
|
+
- or a new public security-policy SPI.
|
|
166
|
+
|
|
167
|
+
Those questions must not be smuggled into the current Filter contract by
|
|
168
|
+
changing call-site semantics implicitly.
|
|
169
|
+
|
|
170
|
+
## Follow-up security-boundary review
|
|
171
|
+
|
|
172
|
+
The subsequent D02-F02 reconciliation review closed the Context-inspection
|
|
173
|
+
question without broadening the Filter SPI:
|
|
174
|
+
|
|
175
|
+
- no fourth `context_filter` / `add_context_filter` call site is added;
|
|
176
|
+
- Application `ContextPolicy` is the semantic trust/selection authority for
|
|
177
|
+
one LLM Call's typed Context;
|
|
178
|
+
- Framework validation remains structural/integrity validation rather than
|
|
179
|
+
arbitrary semantic filtering;
|
|
180
|
+
- Tool approval remains authorization rather than sanitization or sandboxing;
|
|
181
|
+
- OS/process/container/filesystem/network isolation remains an
|
|
182
|
+
Application/deployment concern.
|
|
183
|
+
|
|
184
|
+
The current explanatory boundary is
|
|
185
|
+
[`docs/architecture/security-boundaries.md`](../architecture/security-boundaries.md).
|
|
186
|
+
|
|
187
|
+
## Supersession
|
|
188
|
+
|
|
189
|
+
This ADR supersedes
|
|
190
|
+
[`006-no-built-in-guardrails`](006-no-built-in-guardrails.md) as the normative
|
|
191
|
+
Filter/security-policy architecture.
|
|
192
|
+
|
|
193
|
+
ADR-006 remains preserved as history explaining the earlier minimal-built-in
|
|
194
|
+
policy and its later prompt-injection exception.
|
|
195
|
+
|
|
196
|
+
## Consequences
|
|
197
|
+
|
|
198
|
+
### Positive
|
|
199
|
+
|
|
200
|
+
- normative architecture now matches the live Filter model;
|
|
201
|
+
- old Guardrail class names cannot be mistaken for current public contracts;
|
|
202
|
+
- useful "avoid false confidence" policy intent from ADR-006 is retained;
|
|
203
|
+
- Filter behavior and security-isolation guarantees remain distinct;
|
|
204
|
+
- current raw-input filtering is not falsely described as universal Context
|
|
205
|
+
inspection; and
|
|
206
|
+
- broader security-boundary design can proceed without being pre-decided by
|
|
207
|
+
terminology cleanup.
|
|
208
|
+
|
|
209
|
+
### Trade-offs
|
|
210
|
+
|
|
211
|
+
- applications needing PII/domain policy continue to supply that policy
|
|
212
|
+
themselves; and
|
|
213
|
+
- the built-in PromptInjectionFilter remains intentionally modest rather than
|
|
214
|
+
a complete security solution.
|
|
215
|
+
|
|
216
|
+
## Non-goals
|
|
217
|
+
|
|
218
|
+
This ADR does not:
|
|
219
|
+
|
|
220
|
+
- change Filter runtime behavior or ordering;
|
|
221
|
+
- change the current raw-input `input_filter` placement;
|
|
222
|
+
- automatically apply input Filters to Knowledge/RAG/hook Context;
|
|
223
|
+
- add a Context-candidate inspection API;
|
|
224
|
+
- add OS/process/container Tool sandboxing;
|
|
225
|
+
- change Tool approval semantics;
|
|
226
|
+
- change Persistence or durable representation;
|
|
227
|
+
- make Filter stability stronger than the public feature catalog currently
|
|
228
|
+
states; or
|
|
229
|
+
- perform the final `spec/design/*` path migration.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# ADR-020: Canonical Workflow Instance Identity
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted.
|
|
6
|
+
|
|
7
|
+
## Date
|
|
8
|
+
|
|
9
|
+
2026-08-23
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
Phronomy currently uses `thread_id` for the logical/durable identity of a
|
|
14
|
+
Workflow. The value does not identify a Ruby or operating-system Thread. It
|
|
15
|
+
identifies one logical Workflow instance across invoke, halt, resume, live
|
|
16
|
+
signal routing, and `Persistence#workflow_states`.
|
|
17
|
+
|
|
18
|
+
At the same time, `FSMSession#id` identifies one concrete Runtime FSM
|
|
19
|
+
incarnation. One logical Workflow may use more than one FSMSession over its
|
|
20
|
+
lifetime. Reusing the generic name `thread_id` for the Workflow domain identity
|
|
21
|
+
therefore obscures the boundary between durable identity and Runtime routing.
|
|
22
|
+
|
|
23
|
+
ADR-014 records the earlier `thread_id` terminology. The architecture
|
|
24
|
+
reconciliation program resolved this terminology under CG-01 / ACS-13. This ADR
|
|
25
|
+
makes that narrow identity decision repository authority without pulling the
|
|
26
|
+
remaining Workflow admission-owner, terminal durability, fencing, or recovery
|
|
27
|
+
changes into the same implementation slice.
|
|
28
|
+
|
|
29
|
+
## Decision
|
|
30
|
+
|
|
31
|
+
### 1. `workflow_instance_id` is the canonical Workflow identity
|
|
32
|
+
|
|
33
|
+
The canonical logical Workflow identity is:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
workflow_instance_id
|
|
37
|
+
= one logical Workflow instance
|
|
38
|
+
= the logical Persistence#workflow_states key when the Workflow is durable
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`thread_id` is not retained as a Workflow architecture term.
|
|
42
|
+
|
|
43
|
+
`workflow_instance_id` is framework-owned WorkflowContext metadata, not an
|
|
44
|
+
application state field. Applications must not declare
|
|
45
|
+
`WorkflowContext.field(:workflow_instance_id)`; Phronomy rejects that declaration
|
|
46
|
+
to prevent application state from shadowing or persisting the canonical Workflow
|
|
47
|
+
identity. Applications already using that field name must rename their
|
|
48
|
+
application field when adopting this identity contract.
|
|
49
|
+
|
|
50
|
+
### 2. The migration is a clean break
|
|
51
|
+
|
|
52
|
+
Public and extension surfaces change directly:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
WorkflowContext#thread_id
|
|
56
|
+
-> WorkflowContext#workflow_instance_id
|
|
57
|
+
|
|
58
|
+
Workflow#signal(thread_id:)
|
|
59
|
+
-> Workflow#signal(workflow_instance_id:)
|
|
60
|
+
|
|
61
|
+
Workflow invocation config[:thread_id]
|
|
62
|
+
-> config[:workflow_instance_id]
|
|
63
|
+
|
|
64
|
+
WorkflowStateRepository load/save/delete parameter meaning
|
|
65
|
+
thread_id
|
|
66
|
+
-> workflow_instance_id
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
No deprecated Workflow `thread_id` alias is provided.
|
|
70
|
+
|
|
71
|
+
The removed Workflow config key is rejected explicitly rather than ignored.
|
|
72
|
+
Silently ignoring it could allocate a fresh identity and branch durable Workflow
|
|
73
|
+
history, which is more dangerous than a visible migration error.
|
|
74
|
+
|
|
75
|
+
### 3. Durable identifier values do not change
|
|
76
|
+
|
|
77
|
+
This decision renames the semantic parameter, not the identifier value.
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
old durable key value: "order-123"
|
|
81
|
+
new durable key value: "order-123"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Existing durable values remain the identity of the same logical Workflow
|
|
85
|
+
instance. A backend does not need to generate replacement identifiers.
|
|
86
|
+
|
|
87
|
+
The Persistence Backend SPI uses `workflow_instance_id` as the logical parameter
|
|
88
|
+
name and meaning. A backend may keep an existing physical database column or key
|
|
89
|
+
name internally; this ADR does not require a physical schema rename solely for
|
|
90
|
+
terminology.
|
|
91
|
+
|
|
92
|
+
### 4. Workflow identity is not inherited from generic InvocationContext identity
|
|
93
|
+
|
|
94
|
+
Workflow invocation does not implicitly copy `InvocationContext#thread_id` into
|
|
95
|
+
its domain identity.
|
|
96
|
+
|
|
97
|
+
If an application needs durable Workflow identity, it supplies
|
|
98
|
+
`workflow_instance_id` explicitly through the Workflow API/configuration.
|
|
99
|
+
|
|
100
|
+
This decision does not remove Agent/correlation `thread_id` surfaces from
|
|
101
|
+
`InvocationContext` or Agent APIs. Their generic-identity reconciliation is a
|
|
102
|
+
separate Compatibility Gate.
|
|
103
|
+
|
|
104
|
+
### 5. FSMSession identity remains separate
|
|
105
|
+
|
|
106
|
+
`FSMSession#id` / `fsm_session_id` identifies one concrete Runtime FSM
|
|
107
|
+
incarnation and is not the Workflow domain identity.
|
|
108
|
+
|
|
109
|
+
CG-01 does not rename the shared Runtime-internal `graph_thread_id` bridge used
|
|
110
|
+
by `FSMSession`. WorkflowRunner may continue to pass the canonical
|
|
111
|
+
`workflow_instance_id` value through that existing generic bridge so that a
|
|
112
|
+
replacement WorkflowContext receives the same durable identity before halt or
|
|
113
|
+
finish. WorkflowContext maps that Runtime-internal metadata onto
|
|
114
|
+
`workflow_instance_id`; this does not create a public `thread_id` accessor,
|
|
115
|
+
Workflow config key, signal keyword, or deprecated Workflow API alias. Renaming
|
|
116
|
+
the shared FSMSession graph-metadata protocol belongs to the separate Runtime
|
|
117
|
+
identity reconciliation.
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
Workflow W1
|
|
121
|
+
workflow_instance_id = W1
|
|
122
|
+
|
|
123
|
+
FSMSession S1
|
|
124
|
+
id = S1
|
|
125
|
+
-> halt
|
|
126
|
+
|
|
127
|
+
FSMSession S2
|
|
128
|
+
id = S2
|
|
129
|
+
-> resume / finish
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
No `workflow_execution_id` is introduced by this decision.
|
|
133
|
+
|
|
134
|
+
### 6. This ADR supersedes only ADR-014 Workflow identity terminology
|
|
135
|
+
|
|
136
|
+
ADR-014 remains historical and current authority for the portions not superseded
|
|
137
|
+
here. Wherever ADR-014 calls the logical/durable Workflow identity `thread_id`,
|
|
138
|
+
this ADR replaces that terminology with `workflow_instance_id`.
|
|
139
|
+
|
|
140
|
+
The current admission-owner representation and terminal-save ordering are not
|
|
141
|
+
made normative by this ADR merely because the current implementation still
|
|
142
|
+
contains them.
|
|
143
|
+
|
|
144
|
+
## Consequences
|
|
145
|
+
|
|
146
|
+
### Positive
|
|
147
|
+
|
|
148
|
+
- The Workflow domain identity says what it identifies.
|
|
149
|
+
- Durable Workflow identity and Runtime FSMSession identity are no longer named
|
|
150
|
+
as if they were the same kind of session/thread concept.
|
|
151
|
+
- Persistence backend implementations receive an unambiguous logical SPI
|
|
152
|
+
parameter name.
|
|
153
|
+
- Existing durable identifier values can continue unchanged.
|
|
154
|
+
- A removed config key cannot silently create a new durable identity.
|
|
155
|
+
|
|
156
|
+
### Trade-offs
|
|
157
|
+
|
|
158
|
+
- This is a pre-1.0 breaking API/SPI change.
|
|
159
|
+
- Applications using `config[:thread_id]`, `WorkflowContext#thread_id`, or
|
|
160
|
+
`Workflow#signal(thread_id:)` must migrate.
|
|
161
|
+
- Custom Persistence backends should update method parameter names and
|
|
162
|
+
documentation even when their physical database schema remains unchanged.
|
|
163
|
+
|
|
164
|
+
## Explicit non-goals
|
|
165
|
+
|
|
166
|
+
CG-01 does not decide or implement:
|
|
167
|
+
|
|
168
|
+
- replacement of Workflow admission ownership by an opaque owner token/handle;
|
|
169
|
+
- durable terminal/halt save as an FSM barrier before logical terminalization;
|
|
170
|
+
- EventLoop/FSMSession ownership redesign beyond identity nomenclature;
|
|
171
|
+
- cross-process ownership, leases, or fencing;
|
|
172
|
+
- process-loss recovery or rehydration;
|
|
173
|
+
- Agent/InvocationContext generic identity removal;
|
|
174
|
+
- a new `workflow_execution_id`.
|
|
175
|
+
|
|
176
|
+
Those concerns remain separate architecture-reconciliation work and must not be
|
|
177
|
+
inferred from this identity-only decision.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# ADR 021: Generic Agent Invocation Identity Removal
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted
|
|
4
|
+
**Date**: 2026-08-23
|
|
5
|
+
**Partially supersedes**: [ADR-014](014-unified-persistence-durable-state.md) for `InvocationContext` generic session/correlation semantics and Agent-side generic invocation identity
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Phronomy historically exposed `thread_id` and `session_id` as generic
|
|
12
|
+
invocation metadata. Agent `thread_id` was copied through execution metadata,
|
|
13
|
+
Journal `correlation_id`, Manifest model configuration, and Multi-Agent child
|
|
14
|
+
dispatch, but it did not identify Agent ownership, durable Agent state, resume,
|
|
15
|
+
or Runtime event routing.
|
|
16
|
+
|
|
17
|
+
`InvocationContext#session_id` likewise did not correspond to a Phronomy-owned
|
|
18
|
+
session domain object or lifecycle. Renaming either value to another generic
|
|
19
|
+
identity such as `correlation_id`, `conversation_id`, or
|
|
20
|
+
`application_session_id` would preserve the ambiguity instead of resolving it.
|
|
21
|
+
|
|
22
|
+
Phronomy already has purpose-specific semantic identifiers such as
|
|
23
|
+
`execution_id`, `llm_call_id`, `tool_invocation_id`, `tool_call_id`,
|
|
24
|
+
`approval_request_id`, `workflow_instance_id`, and Runtime-local
|
|
25
|
+
`FSMSession#id`. Tracing task identifiers are a separate observability concern.
|
|
26
|
+
|
|
27
|
+
## Decision
|
|
28
|
+
|
|
29
|
+
Generic Agent invocation identity is removed as a clean break.
|
|
30
|
+
|
|
31
|
+
The public contract does not include:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
InvocationContext#thread_id
|
|
35
|
+
InvocationContext#session_id
|
|
36
|
+
|
|
37
|
+
Agent#invoke(thread_id:)
|
|
38
|
+
Agent#invoke_async(thread_id:)
|
|
39
|
+
Agent#stream(thread_id:)
|
|
40
|
+
Agent#stream_async(thread_id:)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The same generic value is not propagated through Multi-Agent child dispatch,
|
|
44
|
+
AgentExecution metadata, approval context, or finalized LLM model
|
|
45
|
+
configuration.
|
|
46
|
+
|
|
47
|
+
No replacement generic identity field is introduced.
|
|
48
|
+
|
|
49
|
+
`InvocationContext#task_id` and `#parent_task_id` remain tracing /
|
|
50
|
+
observability identifiers. They are not promoted to Agent, Workflow, Tool, or
|
|
51
|
+
Runtime domain identity.
|
|
52
|
+
|
|
53
|
+
Agent configuration rejects legacy `thread_id` and `session_id` keys at
|
|
54
|
+
execution/resume boundaries rather than retaining an untyped compatibility
|
|
55
|
+
backdoor.
|
|
56
|
+
|
|
57
|
+
## Durable Journal migration
|
|
58
|
+
|
|
59
|
+
The architecture decision also removes generic `JournalRecord#correlation_id`
|
|
60
|
+
from the new canonical representation. Existing durable Journal data is not
|
|
61
|
+
rewritten merely to remove that field.
|
|
62
|
+
|
|
63
|
+
Implementation is staged:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
CG-02a
|
|
67
|
+
public / non-durable generic identity removal
|
|
68
|
+
status: reconciled by the CG-02a change
|
|
69
|
+
|
|
70
|
+
CG-02b
|
|
71
|
+
remove correlation_id from the new canonical Journal representation
|
|
72
|
+
accept/ignore the legacy key during backward read
|
|
73
|
+
no eager durable rewrite
|
|
74
|
+
status: reconciled by the CG-02b change
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`JournalRecord.from_h` intentionally reconstructs only current canonical
|
|
78
|
+
attributes. A legacy durable Hash may therefore contain `correlation_id`; the
|
|
79
|
+
removed key is accepted and ignored rather than restored as domain identity.
|
|
80
|
+
This compatibility rule avoids an eager rewrite of existing Journal data and
|
|
81
|
+
does not define a general unknown-field or schema-versioning policy.
|
|
82
|
+
|
|
83
|
+
## Explicit non-goals
|
|
84
|
+
|
|
85
|
+
This decision does not:
|
|
86
|
+
|
|
87
|
+
- change FSMSession identity ownership;
|
|
88
|
+
- rename the shared Runtime `set_graph_metadata(thread_id:)` bridge;
|
|
89
|
+
- remove Runtime-only `AgentInvocation#session_id`;
|
|
90
|
+
- migrate `agent_invocation_id` parent references to `execution_id`
|
|
91
|
+
(subsequently decided by [ADR-022](022-agent-execution-parent-identity-and-runtime-routing-boundary.md));
|
|
92
|
+
- redesign Tool / Approval Runtime routing;
|
|
93
|
+
- remove `AgentExecutionActivation`;
|
|
94
|
+
- implement EventLoop single-writer ownership;
|
|
95
|
+
- implement recovery, fencing, or cross-process ownership.
|
|
96
|
+
|
|
97
|
+
Those concerns belong to the subsequent Runtime identity / ownership change
|
|
98
|
+
sets and compatibility gates.
|
|
99
|
+
|
|
100
|
+
## Consequences
|
|
101
|
+
|
|
102
|
+
### Positive
|
|
103
|
+
|
|
104
|
+
- Agent and Workflow no longer overload the same generic `thread_id` term.
|
|
105
|
+
- Arbitrary application session/correlation concepts are not elevated into the
|
|
106
|
+
Phronomy core identity catalog.
|
|
107
|
+
- Tracing task correlation remains available without being confused with
|
|
108
|
+
lifecycle ownership.
|
|
109
|
+
- Multi-Agent child execution no longer inherits an undefined generic identity.
|
|
110
|
+
- The public contract and architecture vocabulary become purpose-specific.
|
|
111
|
+
|
|
112
|
+
### Trade-offs
|
|
113
|
+
|
|
114
|
+
- This is a pre-1.0 breaking API change.
|
|
115
|
+
- Applications that used Agent `thread_id` / `InvocationContext#session_id`
|
|
116
|
+
must move correlation into application tracing/observability metadata or use
|
|
117
|
+
the actual purpose-specific domain identifier.
|
|
118
|
+
- Legacy durable Journal data retains targeted backward-read compatibility;
|
|
119
|
+
broader codec/schema versioning policy remains a separate persistence decision.
|