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.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +3 -4
  3. data/CHANGELOG.md +215 -10
  4. data/CONTRIBUTING.md +81 -9
  5. data/README.md +15 -6
  6. data/VERIFY.sh +587 -0
  7. data/benchmark/bench_agent_invoke.rb +2 -2
  8. data/benchmark/bench_context_assembler.rb +39 -68
  9. data/benchmark/bench_regression.rb +2 -2
  10. data/docs/architecture/agent-context.md +174 -0
  11. data/docs/architecture/before-llm-input.md +78 -0
  12. data/docs/architecture/context-management.md +232 -0
  13. data/docs/architecture/knowledge-and-rag.md +130 -0
  14. data/docs/architecture/multi-agent-handoff.md +147 -0
  15. data/docs/architecture/persistence.md +186 -0
  16. data/docs/architecture/removed/agent-context.md +72 -0
  17. data/docs/architecture/security-boundaries.md +173 -0
  18. data/docs/architecture/tracing.md +194 -0
  19. data/docs/architecture.md +89 -0
  20. data/docs/archive/design/archived/04_api_design.md +507 -0
  21. data/docs/archive/design/archived/09_guardrails.md +186 -0
  22. data/docs/archive/design/archived/17_rails_integration.md +175 -0
  23. data/docs/archive/design/historical/00_design_philosophy.md +122 -0
  24. data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
  25. data/docs/archive/design/historical/06_design_decisions.md +143 -0
  26. data/docs/changelog/0.14-and-earlier.md +1 -1
  27. data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
  28. data/docs/decisions/002-workflow-context-immutability.md +26 -1
  29. data/docs/decisions/006-no-built-in-guardrails.md +2 -1
  30. data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
  31. data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
  32. data/docs/decisions/016-semantic-multi-agent-handoff.md +114 -0
  33. data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
  34. data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
  35. data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
  36. data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
  37. data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
  38. data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
  39. data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
  40. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
  41. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
  42. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
  43. data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
  44. data/docs/decisions/028-preparing-recovery-replay-contract.md +106 -0
  45. data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +220 -0
  46. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +235 -0
  47. data/docs/decisions/031-durable-multi-agent-coordination.md +301 -0
  48. data/docs/decisions/README.md +176 -0
  49. data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +33 -0
  50. data/docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md +191 -0
  51. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +862 -0
  52. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +106 -0
  53. data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +179 -0
  54. data/docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md +302 -0
  55. data/docs/features.md +65 -11
  56. data/docs/getting-started.md +77 -45
  57. data/docs/migrations/0.19.md +14 -7
  58. data/docs/migrations/0.22.md +390 -0
  59. data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
  60. data/docs/persistence-backends.md +129 -40
  61. data/docs/runtime-and-concurrency.md +227 -33
  62. data/examples/README.md +13 -0
  63. data/lib/phronomy/agent/agent_execution.rb +21 -17
  64. data/lib/phronomy/agent/agent_invocation.rb +288 -93
  65. data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
  66. data/lib/phronomy/agent/agent_root.rb +3 -3
  67. data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
  68. data/lib/phronomy/agent/async_event_api.rb +162 -72
  69. data/lib/phronomy/agent/base.rb +416 -181
  70. data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
  71. data/lib/phronomy/agent/context_assembler.rb +437 -178
  72. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  73. data/lib/phronomy/agent/context_plan.rb +18 -13
  74. data/lib/phronomy/agent/context_plan_validator.rb +246 -88
  75. data/lib/phronomy/agent/context_policies/default.rb +123 -34
  76. data/lib/phronomy/agent/context_policy.rb +109 -3
  77. data/lib/phronomy/agent/context_policy_input.rb +244 -0
  78. data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
  79. data/lib/phronomy/agent/exact_execution.rb +153 -0
  80. data/lib/phronomy/agent/execution_cancellation.rb +25 -0
  81. data/lib/phronomy/agent/execution_coordinator.rb +2417 -572
  82. data/lib/phronomy/agent/handoff.rb +43 -0
  83. data/lib/phronomy/agent/handoff_capability_factory.rb +45 -0
  84. data/lib/phronomy/agent/handoff_context.rb +120 -0
  85. data/lib/phronomy/agent/handoff_execution_coordinator.rb +143 -0
  86. data/lib/phronomy/agent/handoff_policy.rb +143 -0
  87. data/lib/phronomy/agent/handoff_projection.rb +207 -0
  88. data/lib/phronomy/agent/handoff_request.rb +45 -0
  89. data/lib/phronomy/agent/handoff_runner.rb +178 -0
  90. data/lib/phronomy/agent/handoff_state.rb +43 -0
  91. data/lib/phronomy/agent/journal_record.rb +17 -4
  92. data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
  93. data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
  94. data/lib/phronomy/agent/llm_operation_result.rb +12 -7
  95. data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
  96. data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
  97. data/lib/phronomy/agent/recovery_coordinator/continuation.rb +174 -0
  98. data/lib/phronomy/agent/recovery_coordinator/installation.rb +381 -0
  99. data/lib/phronomy/agent/recovery_coordinator/resolution.rb +511 -0
  100. data/lib/phronomy/agent/recovery_coordinator.rb +217 -0
  101. data/lib/phronomy/agent/recovery_support.rb +504 -0
  102. data/lib/phronomy/agent/ruby_llm_materializer.rb +16 -13
  103. data/lib/phronomy/agent/selection/candidate.rb +53 -0
  104. data/lib/phronomy/agent/selection/constraint.rb +49 -0
  105. data/lib/phronomy/agent/shared_state.rb +38 -1
  106. data/lib/phronomy/agent/tool_approval_request.rb +33 -5
  107. data/lib/phronomy/agent/tool_definition_set.rb +49 -3
  108. data/lib/phronomy/agent/tool_invocation.rb +339 -103
  109. data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
  110. data/lib/phronomy/agent.rb +20 -2
  111. data/lib/phronomy/agent_already_exists_error.rb +5 -0
  112. data/lib/phronomy/agent_purged_error.rb +5 -0
  113. data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
  114. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
  115. data/lib/phronomy/engine/event_loop.rb +622 -63
  116. data/lib/phronomy/engine/fsm_session.rb +194 -21
  117. data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
  118. data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
  119. data/lib/phronomy/engine/runtime.rb +92 -20
  120. data/lib/phronomy/generator_verifier.rb +12 -14
  121. data/lib/phronomy/invocation_context.rb +9 -29
  122. data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
  123. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
  124. data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
  125. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
  126. data/lib/phronomy/multi_agent/orchestrator.rb +71 -26
  127. data/lib/phronomy/multi_agent/team_coordinator.rb +473 -125
  128. data/lib/phronomy/multi_agent/team_execution.rb +44 -0
  129. data/lib/phronomy/multi_agent/team_root.rb +41 -0
  130. data/lib/phronomy/persistence/durable_codec.rb +706 -0
  131. data/lib/phronomy/persistence/durable_record.rb +117 -0
  132. data/lib/phronomy/persistence/in_memory.rb +471 -133
  133. data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
  134. data/lib/phronomy/persistence/repository_facades.rb +535 -0
  135. data/lib/phronomy/persistence.rb +174 -40
  136. data/lib/phronomy/recovery.rb +186 -0
  137. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  138. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +2 -1
  139. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
  140. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
  141. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  142. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +137 -0
  143. data/lib/phronomy/testing/persistence_contract.rb +5 -0
  144. data/lib/phronomy/tools/agent.rb +1 -1
  145. data/lib/phronomy/tracing/automatic.rb +176 -0
  146. data/lib/phronomy/tracing/base.rb +11 -2
  147. data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
  148. data/lib/phronomy/version.rb +1 -1
  149. data/lib/phronomy/workflow.rb +3 -6
  150. data/lib/phronomy/workflow_context.rb +14 -5
  151. data/lib/phronomy/workflow_recovery.rb +123 -0
  152. data/lib/phronomy/workflow_runner.rb +468 -256
  153. data/lib/phronomy.rb +6 -0
  154. data/scripts/api_snapshot.rb +12 -0
  155. data/sig/phronomy/agent.rbs +209 -7
  156. data/sig/phronomy/handoff.rbs +41 -0
  157. data/sig/phronomy/multi_agent.rbs +35 -0
  158. data/sig/phronomy/persistence.rbs +123 -4
  159. data/sig/phronomy/runtime.rbs +1 -4
  160. data/sig/phronomy/workflow.rbs +2 -2
  161. data/sig/phronomy.rbs +10 -0
  162. metadata +84 -18
  163. data/examples/workflows/agent_event_mapping.rb +0 -101
  164. data/examples/workflows/generic_task_event_mapping.rb +0 -66
  165. data/lib/phronomy/agent/activation_registry.rb +0 -28
  166. data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
  167. data/lib/phronomy/agent/context_candidate.rb +0 -47
  168. data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
  169. data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
  170. data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
  171. data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
  172. data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
  173. data/lib/phronomy/agent/context_policy_registry.rb +0 -46
  174. data/lib/phronomy/agent/context_request.rb +0 -35
  175. data/lib/phronomy/agent/context_selection_unit.rb +0 -38
  176. data/lib/phronomy/agent/derived_content_spec.rb +0 -34
  177. data/lib/phronomy/agent/runner.rb +0 -97
  178. 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.