phronomy 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -1
  3. data/README.md +17 -6
  4. data/docs/architecture/agent-configuration-and-tool-binding.md +8 -0
  5. data/docs/architecture/agent-iteration-budget.md +81 -0
  6. data/docs/architecture/dx05-agent-scope.md +9 -2
  7. data/docs/architecture/error-api-review.md +4 -4
  8. data/docs/architecture/generator-verifier-lifecycle-result.md +126 -0
  9. data/docs/architecture/generator-verifier-ownership.md +18 -109
  10. data/docs/architecture/llm-continuation-metadata.md +88 -0
  11. data/docs/architecture/lm-studio-output-failures.md +111 -0
  12. data/docs/architecture/r8-unit1.md +68 -68
  13. data/docs/architecture/r8-unit15.md +3 -0
  14. data/docs/architecture/r8-unit2.md +34 -34
  15. data/docs/architecture/r8-unit7.md +2 -1
  16. data/docs/architecture/structured-output-and-scope.md +183 -0
  17. data/docs/architecture/tracing.md +22 -4
  18. data/docs/architecture/transport-error-boundaries.md +103 -0
  19. data/docs/architecture/trust-acceptance-followup.md +87 -0
  20. data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +1 -1
  21. data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +23 -23
  22. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +99 -99
  23. data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +135 -136
  24. data/docs/features.md +10 -1
  25. data/docs/migrations/r8-unit15.md +3 -0
  26. data/gemfiles/http_minimum.gemfile +6 -1
  27. data/lib/phronomy/agent/admission.rb +1 -1
  28. data/lib/phronomy/agent/async_event_api.rb +16 -7
  29. data/lib/phronomy/agent/base.rb +8 -1
  30. data/lib/phronomy/agent/context_importer.rb +4 -0
  31. data/lib/phronomy/agent/execution/agent_invocation.rb +6 -2
  32. data/lib/phronomy/agent/execution/dispatch_preparation.rb +6 -1
  33. data/lib/phronomy/agent/execution/exact_execution.rb +9 -0
  34. data/lib/phronomy/agent/execution/execution_cancellation.rb +1 -1
  35. data/lib/phronomy/agent/execution/execution_coordinator.rb +63 -5
  36. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +24 -4
  37. data/lib/phronomy/agent/execution/execution_registry.rb +5 -1
  38. data/lib/phronomy/agent/execution/initial_preparation.rb +10 -1
  39. data/lib/phronomy/agent/execution/invocation_actions.rb +1 -1
  40. data/lib/phronomy/agent/execution/invocation_transitions.rb +4 -4
  41. data/lib/phronomy/agent/execution/iteration_budget.rb +35 -0
  42. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +3 -0
  43. data/lib/phronomy/agent/execution_observation.rb +5 -5
  44. data/lib/phronomy/agent/lifecycle/scope.rb +33 -23
  45. data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +4 -2
  46. data/lib/phronomy/agent/output_contract.rb +64 -0
  47. data/lib/phronomy/agent/output_error.rb +53 -0
  48. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +3 -3
  49. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +1 -0
  50. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +1 -1
  51. data/lib/phronomy/agent/recovery/recovery_support.rb +5 -1
  52. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +2 -1
  53. data/lib/phronomy/agent/runtime_input.rb +15 -4
  54. data/lib/phronomy/agent/saved_context_reader.rb +2 -1
  55. data/lib/phronomy/agent/store.rb +10 -2
  56. data/lib/phronomy/common/values/immutable.rb +33 -0
  57. data/lib/phronomy/context/assembly.rb +7 -4
  58. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +3 -1
  59. data/lib/phronomy/engine/fsm_session.rb +16 -1
  60. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +28 -5
  61. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +14 -27
  62. data/lib/phronomy/generation/generator_verifier/approval.rb +30 -0
  63. data/lib/phronomy/generation/generator_verifier/cleanup_error.rb +15 -0
  64. data/lib/phronomy/generation/generator_verifier/invocation.rb +147 -0
  65. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +16 -0
  66. data/lib/phronomy/generation/generator_verifier/protocol.rb +55 -0
  67. data/lib/phronomy/generation/generator_verifier/result.rb +121 -0
  68. data/lib/phronomy/generation/generator_verifier/unaccepted_result_error.rb +14 -0
  69. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +13 -11
  70. data/lib/phronomy/generation/generator_verifier.rb +170 -85
  71. data/lib/phronomy/llm_adapter/backends/lm_studio.rb +139 -0
  72. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +134 -12
  73. data/lib/phronomy/llm_adapter/backends/structured_response_capture.rb +41 -0
  74. data/lib/phronomy/llm_adapter/message.rb +4 -0
  75. data/lib/phronomy/llm_adapter/request.rb +17 -2
  76. data/lib/phronomy/llm_adapter/response.rb +11 -4
  77. data/lib/phronomy/llm_adapter/structured_output_unsupported_error.rb +14 -0
  78. data/lib/phronomy/llm_adapter/transport_error.rb +4 -0
  79. data/lib/phronomy/multi_agent/shared_state.rb +1 -0
  80. data/lib/phronomy/output_parser/json_schema_parser.rb +113 -0
  81. data/lib/phronomy/output_parser/parse_error.rb +10 -1
  82. data/lib/phronomy/runtime_composition/agent_defaults.rb +4 -0
  83. data/lib/phronomy/tracing/automatic.rb +14 -8
  84. data/lib/phronomy/tracing/base.rb +2 -1
  85. data/lib/phronomy/tracing/langfuse_tracer.rb +3 -3
  86. data/lib/phronomy/tracing/observation.rb +2 -1
  87. data/lib/phronomy/tracing/open_telemetry_tracer.rb +3 -3
  88. data/lib/phronomy/version.rb +1 -1
  89. data/lib/phronomy.rb +1 -2
  90. data/scripts/api_snapshot.rb +3 -0
  91. data/scripts/migrate_generator_verifier_result.rb +18 -0
  92. data/sig/phronomy/agent/execution_contract.rbs +4 -3
  93. data/sig/phronomy/agent.rbs +20 -7
  94. data/sig/phronomy/extensions.rbs +8 -3
  95. data/sig/phronomy/generation/generator_verifier.rbs +70 -0
  96. data/sig/phronomy/llm_adapter/backends/lm_studio.rbs +8 -0
  97. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +1 -0
  98. data/sig/phronomy/llm_adapter/values.rbs +8 -2
  99. data/sig/phronomy/tracing/observation.rbs +1 -1
  100. data/sig/phronomy.rbs +2 -0
  101. metadata +59 -8
  102. data/lib/phronomy/generation/generator_verifier/default_parser.rb +0 -27
  103. data/lib/phronomy/generation/low_confidence_error.rb +0 -14
  104. data/lib/phronomy/runtime_composition/generation_defaults.rb +0 -6
  105. data/sig/phronomy/generation/low_confidence_error.rbs +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ee0a6e2cc02a9014a1f6b4b0587474255697960b0e1589070a7846d038452ad
4
- data.tar.gz: eb866c788b4dbaf5c560ec0a6a9fe1ef641d630932c7f4309c5ca0741dce2ad0
3
+ metadata.gz: 077db55116aa3b2b1deeeabef69b46b5481c72f1636aa4c623ad8b843d20ef38
4
+ data.tar.gz: 07bbf1214b2aaaf6df1403c69ff8e9f7a7bc4bc8dac63ec4a7a1b11955c0a014
5
5
  SHA512:
6
- metadata.gz: a4970e936adca3d87ee8a8a18469630a563df8fb8a07f06796f4a4bfcae3b1e97b67a51869fdbb68760892201deb12f5f765c082cdfe8559c5f6d0c5ad10dd1a
7
- data.tar.gz: 10c461ef9e86a476cc73020dc1167d6b80564f02f47c3da0161a7da8a34953be96efa236ebcfdf938c7b8a5bfeb2ea6ad3fdaca137d6f70cd5238a71bd6cb829
6
+ metadata.gz: a28ae543ff3826e964cd235580a01175446187546f5f2c94ed34bf12cfd967337df9673654b3029d9173f99479cc32ef966f5bac71db2918cd0265402860e124
7
+ data.tar.gz: ca6dbea79bc432cb1802dbec83cae8b427011b6782f11cfce02fa30ec5b5003439e6b4e6fedd48b6a589ba6b9374075491ae796549dabe41746f20c43f2ee143
data/CHANGELOG.md CHANGED
@@ -12,7 +12,113 @@ Release history for 0.14.0 and earlier is archived in
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
- ### r8 first implementation unit (breaking, unreleased)
15
+ ## [0.30.1] - 2026-10-24
16
+
17
+ ### Documentation
18
+
19
+ - Translate historical r8 unit 1/2 architecture notes and durable semantic
20
+ coordination records into English, preserving API names, executable commands,
21
+ requirement IDs, dates and the original validation limits.
22
+ - Repair ADR-029's link to the recovery-contract document and its English heading.
23
+
24
+ Runtime behavior, public APIs and dependency requirements match 0.30.0.
25
+
26
+ ## [0.30.0] - 2026-10-24
27
+
28
+ ### Added
29
+
30
+ - Agent `output_schema:` and `OutputParser::JsonSchemaParser` provide strict,
31
+ immutable JSON results. Refused, incomplete and invalid output raises
32
+ `Agent::OutputError`; native schema delivery is preserved through Tool rounds,
33
+ approval and recovery without output repair or replaying confirmed Tool work.
34
+ - Explicit `LLMAdapter::LMStudio` selection identifies the known generated-format
35
+ rejection as `llm.provider_output_parse_failed`, retaining the actual HTTP
36
+ status and original SDK cause. Opt-in retry resends the same eligible,
37
+ non-streaming gpt-oss-20b request at most once, without replaying Agent,
38
+ Workflow or Tool work. Ordinary SDK retries are disabled in this backend's
39
+ private snapshot; the default backend and global SDK settings are unchanged.
40
+
41
+ ### Changed
42
+
43
+ - GeneratorVerifier owns immutable evaluation Results, explicit approval routing
44
+ and Agent lifetime. Use `accepted?`, `termination_reason` and
45
+ `raise_if_unaccepted`; `output_failed` distinguishes invalid output from a
46
+ completed evaluation that reaches `iteration_limit`. Acceptance reports the
47
+ configured conditions, not the truth of generated content.
48
+ - Agent scopes and GeneratorVerifier scopes await a directly returned TaskResult
49
+ before releasing their owned Agents. Cleanup preserves the primary failure,
50
+ successful value and handles needed to retry release.
51
+ - Tracing usage is a plain token-count Hash. Custom tracer/exporter callers must
52
+ project LLM-domain TokenUsage values before crossing the tracing boundary.
53
+ - Require Faraday 2.14.4 and faraday-net_http 3.4.4 as the tested HTTP support
54
+ floors. Concrete LLM/Embeddings adapters translate recognized network failures
55
+ into their public TransportError types while preserving causes and callback
56
+ exceptions; cancellation and unknown internal failures are not broadly mapped.
57
+ Strict JSON decoding requires JSON >= 2.21.2 and < 3.
58
+
59
+ ### Removed
60
+
61
+ - GeneratorVerifier parser fallback/repair hooks, `trusted`/`trusted?`,
62
+ `raise_if_untrusted` and `LowConfidenceError`. Migrate saved Result data with
63
+ `scripts/migrate_generator_verifier_result.rb`; no compatibility aliases are
64
+ provided. See the [Result and lifetime contract](docs/architecture/generator-verifier-lifecycle-result.md).
65
+
66
+ ### Fixed
67
+
68
+ - Preserve each SharedState member's explicitly configured model when creating
69
+ its internal instrumented Agent. Keep ordinary Agent inheritance and the
70
+ global fallback for members without an explicit model unchanged.
71
+ - Preserve canonical assistant-message metadata through Agent Tool rounds,
72
+ imports, persistence and recovery. The RubyLLM backend retains supported
73
+ thinking text/signatures and raw reasoning, including final streaming
74
+ responses, and delegates wire rendering to the existing SDK. Incompatible
75
+ continuation scope/version is rejected before inference; legacy messages
76
+ without this metadata remain usable. No database migration is required. See
77
+ [continuation compatibility](docs/architecture/llm-continuation-metadata.md).
78
+ - Enforce `Agent.max_iterations = N` as at most N logical LLM calls per
79
+ execution, including the first and final response. Remove the premature FSM
80
+ cutoff that stopped the ninth single-Tool round before call ten. Tool count,
81
+ approval/resume, streaming chunks and SDK HTTP retries do not consume extra
82
+ logical calls; this limit does not bound physical HTTP attempts or Tool count.
83
+ Preserve the admitted limit and consumed-call records across approval,
84
+ recovery and a fresh Runtime/database pool. Legacy executions without the
85
+ saved limit use the current class setting. A final response on call N succeeds;
86
+ Tools requested on call N retain their results, and call N+1 raises the existing
87
+ `RecursionLimitError`. Zero permits no call; invalid limits fail before inference.
88
+ Generic Engine transition protection remains in place. This correction can
89
+ allow work previously stopped too early and stop runs previously able to reset
90
+ their budget on approval. Deploy at a quiescent Runtime boundary; see
91
+ [iteration budget migration](docs/architecture/agent-iteration-budget.md).
92
+
93
+ ### Verification and migration
94
+
95
+ - Real LM Studio acceptance identified HTTP 400 PEG failures, performed one
96
+ identical-request retry, and returned the final error at the adapter's own
97
+ limit after the second failure. A separate application invocation succeeded
98
+ initially with HTTP 200: two application invocations, three generation POSTs.
99
+ This is not recovery by retry within one invocation. Recovery by retry,
100
+ recovery rate, the PEG root cause and recurrence resolution remain unverified.
101
+ - Review the [structured-output migration](docs/architecture/structured-output-and-scope.md),
102
+ [continuation compatibility](docs/architecture/llm-continuation-metadata.md),
103
+ [iteration budget migration](docs/architecture/agent-iteration-budget.md) and
104
+ [LM Studio retry policy](docs/architecture/lm-studio-output-failures.md) before
105
+ upgrading persistent or in-flight applications.
106
+
107
+ ---
108
+
109
+ ## [0.29.0] - 2026-10-13
110
+
111
+ See the [0.29.0 release record](https://github.com/Raizo-TCS/phronomy/releases/tag/v0.29.0).
112
+
113
+ ---
114
+
115
+ ## [0.28.0] - 2026-10-09
116
+
117
+ The architecture notes below were already included in this release and carried
118
+ unchanged in the 0.29.0 changelog. They are retained from the former Unreleased
119
+ section and are not new 0.30.0 changes.
120
+
121
+ ### r8 first implementation unit (breaking)
16
122
 
17
123
  - Move Context policy/plan/manifest/budget/prompt operations into `Phronomy::Context`.
18
124
  Agent retains snapshot, journal and lifecycle ownership. Manifest and execution
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  > This project is primarily developed and maintained by **AI coding agents**.
5
5
  > As a result, `main` receives frequent, large, and unannounced changes.
6
6
  > External contributors should expect significant churn and potential conflicts at any time.
7
+ > Correct responsibility boundaries and behavior take priority over backward compatibility.
8
+ > Obsolete APIs and compatibility-only paths are removed; update applications with the documented API changes.
7
9
  > We apologise for the instability this may cause.
8
10
 
9
11
  **Phronomy** is a Ruby AI agent framework for stateful Agents, Workflows, Tools,
@@ -35,10 +37,13 @@ tracking `main` directly.
35
37
 
36
38
  See [Features and Application Programming Interface (API) stability](docs/features.md) for the full feature matrix.
37
39
 
38
- The following application APIs are part of unreleased development:
40
+ The following application APIs are available as Beta in v0.30.0:
39
41
 
40
42
  | API | Stability | Contract |
41
43
  |---|---|---|
44
+ | `Phronomy::LLMAdapter::LMStudio` | Beta | Explicit known-output-error identification and bounded opt-in retry; [policy and evidence limits](docs/architecture/lm-studio-output-failures.md) |
45
+ | Agent `output_schema:`, `Agent::OutputError` and `OutputParser::JsonSchemaParser` | Beta | Agent returns validated immutable JSON or an explicit output failure; [contract and migration](docs/architecture/structured-output-and-scope.md) |
46
+ | `Phronomy::GeneratorVerifier`, its approval/lifetime operations and immutable `Result` with output-failure diagnostics | Beta | [Generation and structured-output contract](docs/architecture/structured-output-and-scope.md) |
42
47
  | `Agent.open_scope`, `Agent::Scope`, `Agent::Base#unload_async / #unload!`, `AgentUnloadedError` and `Agent::ScopeCleanupError` | Beta | Runtime-bound ownership and safe release of idle Agent incarnations without changing saved history or closing the Store; [contract](docs/architecture/dx05-agent-scope.md) |
43
48
  | `Workflow::Builder#task_state` and `WorkflowTaskResumeError` | Beta | State-entry-scoped Task completion, live cancellation and explicit restart rejection; [contract](docs/architecture/dx05-workflow-task-state.md) |
44
49
  | `Phronomy::Integrations::OrderedEventDelivery` | Beta | Optional ordered delivery with bounded admission and explicit drain lifetime; [usage](docs/ordered-event-delivery.md) |
@@ -62,15 +67,21 @@ Then run:
62
67
  bundle install
63
68
  ```
64
69
 
65
- Phronomy 0.28 requires RubyLLM 2.0.x. Faraday 2.1.0 is the tested minimum
66
- supported version; the `~> 2.1` requirement allows versions from 2.1 up to,
67
- but excluding, 3.0. CI checks fresh dependency resolution and the exact minimum
68
- with MCP 1.0.0. See the
70
+ Phronomy requires RubyLLM 2.0.x. The tested minimum HTTP stack is Faraday
71
+ 2.14.4 and faraday-net_http 3.4.4; newer versions within their respective major
72
+ versions are allowed. Both floors are explicit: raising Faraday alone can still
73
+ resolve an old Net::HTTP adapter that loses HTTP status during streaming.
74
+ CI checks fresh resolution and this exact pair with MCP 1.0.0. These are verified
75
+ support floors, not a claim about the first upstream release containing a fix.
76
+ See the
69
77
  [RubyLLM 2 and token-ownership migration](docs/architecture/rubyllm-2-token-ownership.md)
70
78
  for removed input-budget overrides and the in-flight Tool-manifest upgrade boundary.
71
79
 
72
80
  Configure RubyLLM with the provider credentials and transport policy required by
73
- your application. Phronomy does not add another LLM transport retry/timeout layer.
81
+ your application. The default adapter delegates transport retry and timeout policy
82
+ to RubyLLM. The explicitly selected LMStudio backend instead disables SDK retries
83
+ and can retry one narrowly recognized generated-format failure at most once;
84
+ see its [policy](docs/architecture/lm-studio-output-failures.md).
74
85
 
75
86
  ```ruby
76
87
  RubyLLM.configure do |c|
@@ -137,3 +137,11 @@ The published SVG is applied43-01. See the [naming boundary](entry-action-and-te
137
137
  and the separate [open Tool schema finding](tool-schema-recording-gap.md).
138
138
 
139
139
  Refactor 45 changes the token declarations as described in [the migration](rubyllm-2-token-ownership.md). Other Agent inheritance rules above remain unchanged. Tool schema/provider configuration follows the RubyLLM 2 inherited configuration contract.
140
+
141
+ ## Agent iteration budget correction
142
+
143
+ `Agent.max_iterations` now bounds logical LLM calls per Agent execution, including
144
+ its first call and its final response. The nonnegative Integer limit is captured
145
+ at admission; zero permits no call. Tool count, approval/resume and backend HTTP
146
+ retries do not consume additional iterations. Declaration inheritance is unchanged.
147
+ See [the iteration-budget boundary and migration](agent-iteration-budget.md).
@@ -0,0 +1,81 @@
1
+ # Agent iteration budget
2
+
3
+ ## Problem and meaning
4
+
5
+ Agent previously converted `max_iterations` to the generic FSM transition limit
6
+ `12 + 8 * max_iterations`. A single-Tool round currently takes ten transitions;
7
+ parallel Tool completions add further transitions. Default ten therefore stopped
8
+ the ninth Tool round before the tenth LLM call. Approval/recovery also creates new
9
+ FSM sessions, so a session counter cannot enforce an execution-wide Agent limit.
10
+
11
+ `Agent.max_iterations = N` now permits at most N logical LLM calls in one
12
+ AgentExecution, including the first and final call. Tool execution, authorization,
13
+ approval, streaming chunks and backend HTTP retries consume no additional calls.
14
+ This does not bound physical HTTP attempts, tokens, elapsed time or Tool count.
15
+
16
+ ## Owners and dependency direction
17
+
18
+ | Owner | Responsibility |
19
+ |---|---|
20
+ | Application / Agent declaration | Choose the maximum logical LLM calls. |
21
+ | Agent::IterationBudget | Validate the limit and decide whether another call is allowed from AgentExecution.llm_calls. |
22
+ | Agent preparation operations | Check before context hooks and provider dispatch; capture the limit in execution metadata. |
23
+ | Agent execution/recovery record encoders | Existing durable call identity, result recording and exactly-once recovery interpretation. |
24
+ | Agent::EngineSessionBuilder / InvocationTransitions | Select generic Engine safety mode and a bound appropriate to the Agent phase graph. |
25
+ | Engine::FSMSession | Bound automatic transition chains, without knowing LLMs, Tools, Agent records or iteration policy. |
26
+ | LLMAdapter / RubyLLM backend | Existing logical-call contract, wire transport, response handling and SDK retry policy. |
27
+
28
+ There is no new mutable Agent counter and no persistence access from Engine.
29
+ The call records already used for context call sequence and recovery remain the
30
+ single authority. InitialPreparation checks before the first call. DispatchPreparation
31
+ includes pending runtime outcomes through the existing record encoder, checks the
32
+ resulting call count, then prepares and commits the next provider dispatch.
33
+ Existing durable pending-call identity prevents unknown outcomes from being
34
+ automatically redispatched. No counter is incremented by Tool or Engine events.
35
+
36
+ Engine's optional `recursion_limit_scope: :automatic_chain` counts consecutive
37
+ accepted `state_completed` transitions. Only an accepted external transition
38
+ starts a new chain; consumed/stale events or events without a transition do not.
39
+ Agent selects this mode with its declared phase count as the safety bound.
40
+ Engine's default `:session` mode retains the previous total-transition limit for
41
+ Workflow and Tool sessions. Total step count remains available for diagnostics.
42
+ This is a generic mechanism, not an Engine dependency on Agent policy.
43
+
44
+ ADR-066's allocation remains intact: Agent owns execution and recovery, C09 owns
45
+ one model operation, C06 owns Tool execution, and the backend owns SDK details.
46
+ No RubyLLM, provider-specific or example-specific code is added.
47
+
48
+ ## Completion, suspension and compatibility
49
+
50
+ - The limit is a nonnegative Integer captured as `agent_max_iterations` in the
51
+ existing execution metadata at admission. Changing a class declaration during
52
+ approval cannot increase or reset the active run's budget. No DB schema migration.
53
+ - Resume/recovery uses the saved call records and saved limit. An older execution
54
+ without this metadata uses the current class limit and records it when preparing
55
+ another call. Its original limit cannot be reconstructed. Restart from legacy
56
+ initial preparation captures the fallback before input hooks.
57
+ - A new AgentExecution gets its own limit; handing work to another Agent gives
58
+ that Agent its own execution budget. GeneratorVerifier/SharedState orchestration
59
+ limits remain separate policies.
60
+ - A final response on call N succeeds. If call N requests Tools, those Tools
61
+ finish or await approval normally; the attempted next LLM call then fails with
62
+ the existing `Phronomy::RecursionLimitError`. Completed Tool results are retained;
63
+ no final answer or successful application outcome is fabricated.
64
+ - Zero dispatches no LLM call. Invalid limits fail before inference. Default ten
65
+ and declaration inheritance remain unchanged. Error class is retained; the
66
+ message now identifies the logical Agent limit rather than an FSM step limit.
67
+
68
+ This correction may allow more work than the old accidental FSM cutoff, or stop
69
+ an approval-heavy run previously able to reset its session counter. Deploy at a
70
+ quiescent Runtime boundary; do not hot-swap an active process. Application owners
71
+ must assess actual iteration needs after the counting bug is fixed. Increasing
72
+ example limits is not part of this change.
73
+
74
+ ## Verification scope
75
+
76
+ Regression tests cover nine Tool rounds plus final call ten, streaming, a real
77
+ RubyLLM retry through stubbed HTTP, many Tools in one response, exact cutoff,
78
+ fresh execution, approval and fresh Runtime restoration, recovered provider/Tool
79
+ outcomes, legacy metadata, zero and invalid limits. Generic Engine tests cover
80
+ automatic loops, stale events and unchanged default session limits. Live LM Studio
81
+ acceptance and its independent peg-native parsing failure remain separate gates.
@@ -83,8 +83,9 @@ logs the Agent ID; it is never silently made creatable again.
83
83
 
84
84
  Scope does not cancel or wait for unjoined child execution as an implicit
85
85
  cleanup policy. An active Agent is retained and cleanup reports `AgentBusyError`.
86
- If the main operation failed, its same exception is preserved and secondary
87
- cleanup failures are logged with Agent IDs. Logger failure cannot replace it.
86
+ If both the main operation and cleanup fail, ScopeCleanupError retains the original
87
+ exception as primary_error/cause and every cleanup failure, plus the retryable scope.
88
+ If cleanup succeeds, the original exception is re-raised with the same identity.
88
89
  If the main operation succeeded, failed cleanup raises `Agent::ScopeCleanupError`
89
90
  (`code: "agent.scope_cleanup_failed"`). `result` preserves the successful value;
90
91
  `failures` is an array of `{agent_id:, error:}` with the original exceptions.
@@ -117,3 +118,9 @@ calling it does not opt into scope cleanup.
117
118
 
118
119
  This is P5 (core). P6 migrates application examples and performs cross-repository
119
120
  acceptance against the published or pinned candidate core.
121
+
122
+ ## Structured-output lifetime update
123
+
124
+ Scope now exposes `release!` and retains only failed owners for retry. CleanupError
125
+ also carries scope, resolved result and primary_error. The exact Task/error/release
126
+ rules are specified in [structured output and scope](structured-output-and-scope.md).
@@ -52,16 +52,16 @@ that every possible backend failure has been injected.
52
52
  | C01 | invoke/admission, TeamExecutionRepository.create_active/save/assert_idle!: active Team run | TeamBusyError with team_id and team.busy replaces AgentBusyError only for Team ownership. Child-Agent failures retain their own classes. No AgentBusy alias or inheritance. | execution_constraint_translation_spec; shared SQL coordination contract |
53
53
  | C01 | HandoffRunner.invoke/cancel, Orchestrator fan-out, operation rejection | Retain ConfigurationError, HandoffError, StateConflictError, recovery requirements and on_error policy. Missing retained children are not missing Teams. Readback reconciliation may itself fail and does not prove absence. OperationRejected stays private; public code team.enqueue_after_finalize is unchanged. | durable_coordination_spec; integration/team_coordinator_spec; SQL reason diagnostics |
54
54
  | C02 | Workflow definition/action, WorkflowContext writes, state CAS, waiting/resume | Retain ConfigurationError, WorkflowContextOwnershipError, WorkflowAsyncActionError and persistence conflict. Workflow has no equivalent target-root load API to justify another NotFound class. DX05-D adds WorkflowTaskResumeError with workflow.task_resume_unsupported only for unsupported restoration of a task-state phase, before any start side effect. Start/source/apply failures keep their original errors, and task cancellation remains distinct from a failed CancellationError. Agent scope is separate. | workflow and execution unit/integration suites; task_state_spec; shared SQL task-state contract (live PostgreSQL acceptance pending) |
55
- | C03 | GeneratorVerifier.invoke; parse fallback and confidence decision | Preserve ParseError-only fallback and LowConfidenceError.result. Add optional constructor code; do not absorb provider/callback errors into a GenerationError. | generator_verifier specs; error_api_diagnostics_spec |
55
+ | C03 | GeneratorVerifier.invoke; schema-qualified confidence decision | Agent owns OutputError; Generation records output_failed with its diagnostic; GeneratorVerifier::UnacceptedResultError carries Result and an optional code. CleanupError retains cleanup and primary failures. See generator-verifier-lifecycle-result.md. | generator_verifier specs; error_api_diagnostics_spec |
56
56
  | C04 | Base.load, create/get, invoke/resume/purge | Add AgentNotFoundError only at the requested root. Journal/content/execution absence remains persistence failure. Preserve foreign AgentNotFound identity. AlreadyExists/Busy/Purged, uncertain purge and recovery-required retain their meaning. | error_api_boundaries_spec; Agent lifecycle/recovery suites |
57
57
  | C04 | stream listener policy; result/failure persistence | StreamCallbackError keeps event_type/original_error/result, with optional code. Listener :fail_task versus :log_and_continue remains explicit; no unconditional callback swallowing. | stream_callback_error_policy_spec; error_api_diagnostics_spec |
58
58
  | C05 | Assembly.prepare and policy invocation; plan/budget validation | Keep BudgetExceededError and ManifestError. Reuse C15 public trace_with, metadata only; a finish failure cannot mask policy failure or export the returned context. | error_api_boundaries_spec; Context suites |
59
59
  | C06 | Tool.call / native async operation; schema, execution, async handle validity | Keep ToolError class; supply tool.invalid_arguments / tool.execution_failed / tool.invalid_async_result at owned conversion sites. Explicit async cause fixes delayed-failure information loss. Existing ToolError/cancellation pass through unchanged. :suppress survives logger failure. | tool/async_operation_spec; tool/base_spec; schema_error_spec |
60
60
  | C07 | StorageBoundary, transaction, SaveOutcome, SnapshotComparison | Retain known-category mapping and original cause. Unknown I/O/application failures pass through. Keep committed/not_committed/unknown separately from exception class; no code-based reconciliation. | storage_boundary_spec; failure_contract_spec; snapshot_comparison_spec; SQL suites |
61
61
  | C08 | fetch, fetch_text, fetch_json and content identity | Invalid requested JSON is IntegrityError with content.invalid_json and original ParserError; invalid UTF-8 is content.invalid_encoding. Raw fetch failures pass through; no dependency on OutputParser. Requesting JSON for plain text is also invalid representation, not proof of physical corruption. | error_api_boundaries_spec; content conformance; SQLite |
62
- | C09 | LLM complete/stream, request/result checks, RubyLLM conversion | Keep Authentication/RateLimit/ContextLength as TransportError children and InvalidResult separate. Unknown exceptions, cancellation and callbacks retain identity. Existing SDK conversion is not an automatic retry policy. | LLMAdapter unit/integration suites |
62
+ | C09 | LLM complete/stream, request/result checks, RubyLLM conversion | Authentication/RateLimit remain TransportError children; ContextLength and InvalidResult remain separate. The RubyLLM backend also maps identified Faraday TimeoutError/ConnectionFailed to TransportError with the original cause chain. Unknown exceptions, cancellation/deadlines and callbacks (including same-class Faraday failures) retain identity. Classification does not imply retry safety. | LLMAdapter suites; transport_error unit and real-HTTP integration contracts |
63
63
  | C10 | VectorStore add/search/remove/clear/size and AsyncClient | Keep InvalidResultError for invalid response and driver communication errors unchanged. Driver-specific transaction/effect uncertainty is not normalized without a backend-specific contract. No universal VectorStore TransportError or sibling-contract borrowing. | VectorStore and async-client suites; live PG/Redis pending |
64
- | C11 | Embeddings.embed / AsyncClient; RubyLLM errors | Add owner-local AuthenticationError and RateLimitError under existing TransportError. Callers can request credentials versus surface quota refusal. Other SDK errors retain TransportError; arbitrary I/O failures, cancellation and programming errors are not guessed to be SDK failures. | error_api_boundaries_spec; embeddings/async_client_spec; RubyLLMEmbeddings specs |
64
+ | C11 | Embeddings.embed / AsyncClient; RubyLLM errors | Owner-local AuthenticationError and RateLimitError remain under TransportError. Other RubyLLM errors and identified Faraday TimeoutError/ConnectionFailed map to TransportError in the concrete backend, preserving causes. Arbitrary I/O failures, cancellation/deadlines and programming errors are not guessed to be SDK failures. | error_api_boundaries_spec; embeddings/async_client_spec; transport_error unit and real-HTTP integration contracts |
65
65
  | C12 | Records.read/fetch, conditions/unique constraints, streams/blobs, atomic views | Keep nil read versus raising fetch; preserve condition/resource/constraint. Add optional code to specialized constructors; SPI 2 and storage format unchanged. No Agent or Persistence semantics in the backend. | storage SPI snapshot; backend conformance; SQLite |
66
66
  | C13 | TaskResult completion, map/flat_map/all_settled, wait/timeout/cancel | Transport errors unchanged. Observer failure leaves settled result intact and continues later observers even if logging fails. Transformation failure settles the derived task. Outcome attributes and physical completion remain separate. | task_result_spec; error_api_boundaries_spec; async composition suites |
67
67
  | C15 | Base.trace, trace_with, OpenTelemetry.trace, Automatic | Explicit tracing propagates start/finish-only failure. After a primary business failure finish is attempted once and secondary report failure is suppressed. No finish after start failure. OpenTelemetry keeps with_span context. Automatic retains best-effort observation policy; Langfuse keeps its adapter-specific network suppression. | tracing/base_spec; open_telemetry_tracer_spec; automatic and extension specs |
@@ -211,7 +211,7 @@ Ripper inspected 339 Ruby files. The following 63 definitions include base, priv
211
211
  | `Phronomy::SchedulerReentrancyError` | `Phronomy::EventLoopReentrancyError` | `lib/phronomy/execution/scheduler_reentrancy_error.rb` |
212
212
  | `Phronomy::TimeoutError` | `Phronomy::Error` | `lib/phronomy/execution/timeout_error.rb` |
213
213
  | `Phronomy::FilterBlockError` | `Phronomy::Error` | `lib/phronomy/filter/filter_block_error.rb` |
214
- | `Phronomy::LowConfidenceError` | `Phronomy::Error` | `lib/phronomy/generation/low_confidence_error.rb` |
214
+ | `Phronomy::GeneratorVerifier::UnacceptedResultError` | `Phronomy::Error` | `lib/phronomy/generation/generator_verifier/unaccepted_result_error.rb` |
215
215
  | `Phronomy::Integrations::OrderedEventDelivery::ClosedError` | `Phronomy::Error` | `lib/phronomy/integrations/ordered_event_delivery.rb` |
216
216
  | `Phronomy::Integrations::OrderedEventDelivery::OverflowError` | `Phronomy::Error` | `lib/phronomy/integrations/ordered_event_delivery.rb` |
217
217
  | `Phronomy::LLMAdapter::AuthenticationError` | `Phronomy::LLMAdapter::TransportError` | `lib/phronomy/llm_adapter/authentication_error.rb` |
@@ -0,0 +1,126 @@
1
+ # GeneratorVerifier: approval, lifetime and result contract
2
+
3
+ Generation (C03) is a responsibility group, not a Ruby namespace.
4
+ Phronomy::GeneratorVerifier is the public draft/review/retry API. Applications
5
+ choose the Agent classes, Tools, prompts, evaluation thresholds and authorization
6
+ policy. Generation owns composition and evaluation; Agent (C04) owns Tool
7
+ authorization, durable execution, cancellation and safe unload.
8
+
9
+ Correct behavior takes priority over backward compatibility. This change removes
10
+ trusted/trusted?, raise_if_untrusted and LowConfidenceError. There are no legacy
11
+ aliases or compatibility branches.
12
+
13
+ ## Application operations
14
+
15
+ Construct with new or open. Construction acquires both owned Agents, synchronously;
16
+ a failure acquiring the second releases the first. Prepare the pipeline before
17
+ entering EventLoop. Both Agents and their conversations are reused between
18
+ invocations. open requires a block, returns its value, and always attempts release!.
19
+ A directly returned TaskResult is awaited once before release; arrays and nested
20
+ Tasks are not recursively awaited. See [structured output and scope](structured-output-and-scope.md).
21
+
22
+ invoke waits for the final Result. invoke_async returns TaskResult. A pipeline
23
+ admits one invocation at a time; independent concurrent conversations need
24
+ independent pipelines. Workflow state, approvals and cancellation are per invocation.
25
+ config is forwarded to Workflow. Only the private cancellation token and any
26
+ InvocationContext are forwarded to Agent operations, not Workflow-specific
27
+ identity or settings.
28
+
29
+ on_approval receives an Approval with invocation_id, phase (:draft/:review) and
30
+ request (Agent::ToolApprovalRequest). It is a short, thread-safe notification,
31
+ not a blocking UI callback or a function whose return value authorizes a Tool.
32
+ Use approval.respond_async(approved: true/false), or enqueue the Approval for an
33
+ external UI to answer later. The response Task observes the resumed Agent's
34
+ terminal outcome and returns nil. Do not wait for it inside the callback.
35
+
36
+ Without a callback, approval_requests exposes notifications issued in the current
37
+ invocation, including answered ones. It is not a mirror of current durable
38
+ approval state. Suspension is not an error and no implicit approval or denial is
39
+ performed. Expiry and cancellation are application choices. Stale invocation
40
+ handles fail; Agent validates execution/request identity and duplicate decisions.
41
+ Invalid replies fail their own Task without cancelling the original invocation.
42
+ Callback exceptions cancel the owned invocation and remain its primary error.
43
+
44
+ cancel_async requests cancellation without mutating a caller-owned cancellation
45
+ token. It can also address a known Agent execution after a Handoff outcome.
46
+ Completion acknowledges the cancellation operations, not physical quiescence;
47
+ observe the invocation result separately. release! delegates safety to Agent's
48
+ unload!, preserves saved history and does not close the Store. It rejects active
49
+ invocations rather than cancelling or denying approvals implicitly.
50
+
51
+ All owners are attempted during release. Successful owners are removed; failed
52
+ ones remain available for cancellation or another release attempt. Partial release
53
+ cannot be followed by a new invocation. Successful release is idempotent (true,
54
+ then false). CleanupError exposes pipeline, failures, result and primary_error;
55
+ when both application work and cleanup fail, neither exception nor the recovery
56
+ handle is lost. Its cause is the primary exception, if present.
57
+
58
+ Handoff coordination is outside this composition's contract. A Handoff result
59
+ raises generation.unsupported_handoff rather than leaving the Workflow pending.
60
+ Agent retains authority over durable coordination and safe release; Generation
61
+ never purges unfinished work or clears retention to make cleanup appear successful.
62
+ Reconstructing an in-flight GeneratorVerifier after process restart is not offered.
63
+
64
+ ## Agent cancellation during approval suspension
65
+
66
+ An approval suspension ends the current FSM session, but the logical execution
67
+ and its original Task remain pending. C04 now retains the cancellation connection
68
+ through this suspension, routes cancellation to its existing quiescence/terminal
69
+ commit barrier, and closes the subscription on resume or termination. Generation
70
+ does not implement this Agent transition itself. Cancellation and approval racing
71
+ at the suspension boundary must not create competing terminal/resume commits.
72
+
73
+ ## Results and decisions
74
+
75
+ Result is immutable, including nested data. It contains output, confidence,
76
+ confidence_threshold, review_approved, citations, iterations, review_notes,
77
+ termination_reason, interrupted_phase and failure. accepted? describes satisfaction of the
78
+ configured evaluation conditions, not an application's decision to publish.
79
+
80
+ Confidence is the lower final draft/review score. A completed evaluation is accepted
81
+ only when review_approved is true and confidence meets the threshold. At the
82
+ iteration limit, acceptance wins if both conditions hold; otherwise the reason is
83
+ iteration_limit. Scores and threshold are finite 0..1; max_iterations is a positive
84
+ Integer. Agent validates the fixed output schema without fallback or normalization.
85
+
86
+ A Tool denial is a normal Agent rejected outcome, not a parser input or automatic
87
+ retry. Result reports tool_rejected and the interrupted phase. Confidence and
88
+ review_approved are nil because the evaluation did not complete. Rejection during
89
+ draft has no output; rejection during review retains the draft. iterations counts
90
+ completed drafts.
91
+
92
+ to_h includes all fields. Result.from_h accepts that Hash and its JSON round trip,
93
+ rejecting unknown fields, invalid types and contradictory reasons. Citation keys
94
+ are normalized to strings. Equality and hash use all semantic fields, so a change
95
+ in review decision or threshold is observable. Applications own storage/transport
96
+ choices and any intentional projection of these fields.
97
+
98
+ raise_if_unaccepted defaults to false. When explicitly true, an unaccepted Result
99
+ raises GeneratorVerifier::UnacceptedResultError with result and code. Provider,
100
+ cancellation and callback errors do not become evaluation failures. An Agent
101
+ OutputError becomes output_failed with its phase and diagnostic; there is no automatic
102
+ retry. raise_if_unaccepted preserves that OutputError as the cause.
103
+
104
+ ## Ownership boundaries
105
+
106
+ C03 depends on C04 Agent, C02 Workflow and C13 Execution. C04 uses C17
107
+ OutputParser to implement its output promise; C03 has no C17 dependency. Cancellation subscriptions use the existing execution abstraction.
108
+ No Agent registry, private Scope construction, Storage operation or Engine
109
+ implementation is accessed from Generation.
110
+
111
+ Private AgentOperation connects public Agent commands and notifications.
112
+ Invocation owns one call's child observations, subscription lifetime and approval
113
+ routing. WorkflowBuilder/AgentResultReceiver/PipelineState retain graph, event
114
+ conversion and correlated state application. Result owns evaluation facts and
115
+ their invariants.
116
+
117
+ ## Application migration
118
+
119
+ Use open around the complete application operation, including validation retries.
120
+ Use accepted? and termination_reason, not score-only trusted?. For an external
121
+ approval UI, start invoke_async, enqueue on_approval notifications, respond through
122
+ the original Approval handle, and observe the final Task before release.
123
+
124
+ examples/19_trust_pipeline is the direct consumer. examples/27_issue_analyzer only
125
+ mentions GeneratorVerifier in descriptive classification text and requires no API
126
+ migration. Full old/new runnable sources are available through the source diff.
@@ -1,111 +1,20 @@
1
1
  # GeneratorVerifier ownership and event contract
2
2
 
3
- ## Role and problem
4
-
5
- GeneratorVerifier exposes the draft/review/retry pattern. An application supplies
6
- two Agent classes, prompt builders, optional result parsers and convergence
7
- settings. invoke runs the Workflow and returns the existing Result, optionally
8
- raising LowConfidenceError. Agent completion arrives as Workflow events; Workflow
9
- entry actions return context immediately rather than awaiting Agent Tasks.
10
-
11
- R08 and D08 describe the same issue: a 199-line build_workflow mixes the graph
12
- with Agent startup, response parsing/normalization and failure notification.
13
- Nested PipelineState also occupies the public facade's source file. Separating
14
- only the file without removing receiver-to-facade private calls would leave the
15
- responsibility problem in place.
16
-
17
- ## Owners and reading order
18
-
19
- | Owner | Responsibility |
20
- |---|---|
21
- | GeneratorVerifier | Public settings and Result; cached Workflow assembly, invocation, default JSON parser fallbacks and final confidence/raise policy. |
22
- | GeneratorVerifier::WorkflowBuilder | Draft/review/finalize/failed graph, request IDs, prompt creation, Agent startup and convergence guard. |
23
- | GeneratorVerifier::AgentResultReceiver | Separate draft/review payload conversion, common terminal-event classification and failure notification to the supplied Workflow. |
24
- | GeneratorVerifier::PipelineState | Existing Workflow fields, request correlation, stale/duplicate rejection and EventLoop-owned state mutation. |
25
-
26
- The last three constants are private. WorkflowBuilder and AgentResultReceiver
27
- are new internal classes; PipelineState is the existing class moved with its
28
- body and canonical name unchanged. The implementation lives in three files under
29
- generation/generator_verifier. The public facade and Result keep their names and
30
- locations; no loader changes or compatibility aliases are introduced.
31
-
32
- The initial D08 sketch suggested multi_agent/generator_verifier_workflow.rb.
33
- Since the prior layout work established generation as this pattern's owner,
34
- the internal files now stay there instead of creating a second feature owner.
35
- The new directory adds a module in directory-based analysis, not a public API.
36
-
37
- WorkflowBuilder#build reads as a graph: named entries, then ordered transitions.
38
- start_draft/start_review describe request preparation and dispatch. The receiver's
39
- draft_payload and review_payload retain different meanings. Their common listener
40
- has only the two internally selected phases; it is not a public configurable
41
- pipeline. It shares the same terminal/error rules rather than combining response
42
- formats into an options-driven generic parser.
43
-
44
- Default parsers remain explicit callables captured by the facade. Clamp and
45
- citation normalization move to their receiver owner. The old __send__ calls back
46
- to facade helpers disappear. The existing private Agent event-sink entry point
47
- is still called via send; changing that cooperation API is outside this scope.
48
-
49
- ## Preserved ordering and failure contract
50
-
51
- - Each Agent is constructed once, lazily during the first successful Workflow
52
- build. The cached Workflow and Agent instances are reused. Each invoke gets a
53
- fresh Workflow context. No locking or new concurrency policy is introduced.
54
- - Each request gets a new UUID and a merged context before prompt construction.
55
- The listener captures that request ID and the context's stable Workflow ID.
56
- Workflow closures see the completed Workflow assignment before any entry runs.
57
- - Entry actions start asynchronous Agent work and return next_state, never a
58
- TaskResult. Inline completion remains valid because signal queues a Workflow
59
- event. Unknown/nonterminal Agent events are ignored.
60
- - done parses output, normalizes the phase-specific payload and signals the
61
- matching completed event. StandardError from parsing, normalization or success
62
- notification is signalled as the corresponding failed event with the same
63
- exception object. Failure-notification exceptions are not retried or wrapped.
64
- - error/timeout/cancelled use the payload error or the same phase-specific
65
- fallback message. approval_required remains a pipeline failure. A false return
66
- from signal is returned without a retry or a new failure event.
67
- - PipelineState accepts only the current request ID. It clears the accepted ID,
68
- increments iteration after draft completion, appends feedback after review,
69
- and consumes old/duplicate results before any transition. The receiver does
70
- not mutate context or add its own deduplication; stale done payloads can still
71
- be parsed before the state rejects their correlated event.
72
- - Review completes when both the lower normalized score meets threshold and
73
- approval is literal true, or when the iteration limit has been reached.
74
- Otherwise it returns to draft and passes the last feedback to its prompt.
75
- - Final Result trust remains confidence >= threshold. At the iteration limit,
76
- high confidence with approved=false can therefore still produce trusted=true.
77
- A nonpositive limit still executes one draft/review cycle. These are observed
78
- existing semantics, not behavior fixes hidden in this extraction.
79
-
80
- Public initialize/invoke parameters, Result fields/trusted? alias, parser
81
- fallbacks, score/citation normalization, error messages and PipelineState fields
82
- remain unchanged. Agent/Workflow/Engine/Storage behavior and SQL are not modified.
83
- Private clamp/normalize_citations implementations relocate; private overrides are
84
- not treated as additional supported public APIs.
85
-
86
- ## Verification and trade-off
87
-
88
- Thirty-nine behavior examples pass unchanged on Refactor 40 and the candidate.
89
- They cover inline completion, ignored progress, pending Task returns, both phases'
90
- error/timeout/cancelled/approval failures, parser and notification failures,
91
- request correlation, duplicate/old callbacks, clamping, literal approval, final
92
- trust, iteration limits, caching, fresh state and config forwarding. Existing
93
- delayed-callback and WebMock integration examples cover the real Workflow path.
94
-
95
- A separate process comparison preserves public methods, parameters, constants,
96
- Result shape, the private state name/field schema/defaults and Runtime inactivity.
97
- Stable/Beta snapshots do not list GeneratorVerifier, so this explicit comparison
98
- supplements those existing gates. Core/integration/examples/real SQLite, type,
99
- style, annotations and isolated gem checks are included in distribution evidence.
100
- Live PostgreSQL, live LLM, candidate CI and performance are not run for this scope.
101
-
102
- The facade is 369 -> 118 lines. Workflow definition is 199 -> 34 lines, with a
103
- 12-line facade assembly method. The three internal files are 112/89/57 lines,
104
- for 376 total, seven more than before. Two new classes and one moved class cost
105
- additional files; they give graph, receiver and state independent reading units.
106
- Directory-level dependency counts increase with that physical partition; cycle
107
- membership is unchanged. This is responsibility separation, not cycle removal.
108
-
109
- Refactor 41 is applied and independently verified at core e87eb77f, tree
110
- 14dc8a546a4fd96e19e0713f7480ca2efa3c331a. All nine files and the full tree match.
111
- R08/D08 is closed. The published diagram is applied41-01. R09 and R10 remain.
3
+ The current contract is
4
+ [GeneratorVerifier: approval, lifetime and result](generator-verifier-lifecycle-result.md).
5
+
6
+ GeneratorVerifier is the public owner of draft/review Agent composition and its
7
+ evaluation Result. AgentOperation delegates to public Agent commands. Invocation
8
+ owns per-call approval routing, cancellation subscriptions and child observations.
9
+ WorkflowBuilder builds the graph; AgentResultReceiver converts results;
10
+ PipelineState applies correlated events; Result owns immutable evaluation facts.
11
+
12
+ Both owned Agents are acquired at pipeline construction and reused until release.
13
+ Workflow contexts are fresh for each invocation. Concurrent use of one pipeline
14
+ is rejected. Approval suspension remains pending and is exposed to the application;
15
+ it is not converted to failure. Tool denial is a tool_rejected Result.
16
+ Handoff is explicitly unsupported instead of silently leaving completion pending.
17
+
18
+ The older R08/D08 and R8 unit15 documents record the historical extraction. Their
19
+ preserved score-only trust, approval-failure and lazy Agent acquisition behavior
20
+ has been superseded by the current contract.