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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +107 -1
- data/README.md +17 -6
- data/docs/architecture/agent-configuration-and-tool-binding.md +8 -0
- data/docs/architecture/agent-iteration-budget.md +81 -0
- data/docs/architecture/dx05-agent-scope.md +9 -2
- data/docs/architecture/error-api-review.md +4 -4
- data/docs/architecture/generator-verifier-lifecycle-result.md +126 -0
- data/docs/architecture/generator-verifier-ownership.md +18 -109
- data/docs/architecture/llm-continuation-metadata.md +88 -0
- data/docs/architecture/lm-studio-output-failures.md +111 -0
- data/docs/architecture/r8-unit1.md +68 -68
- data/docs/architecture/r8-unit15.md +3 -0
- data/docs/architecture/r8-unit2.md +34 -34
- data/docs/architecture/r8-unit7.md +2 -1
- data/docs/architecture/structured-output-and-scope.md +183 -0
- data/docs/architecture/tracing.md +22 -4
- data/docs/architecture/transport-error-boundaries.md +103 -0
- data/docs/architecture/trust-acceptance-followup.md +87 -0
- data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +1 -1
- data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +23 -23
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +99 -99
- data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +135 -136
- data/docs/features.md +10 -1
- data/docs/migrations/r8-unit15.md +3 -0
- data/gemfiles/http_minimum.gemfile +6 -1
- data/lib/phronomy/agent/admission.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +16 -7
- data/lib/phronomy/agent/base.rb +8 -1
- data/lib/phronomy/agent/context_importer.rb +4 -0
- data/lib/phronomy/agent/execution/agent_invocation.rb +6 -2
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +6 -1
- data/lib/phronomy/agent/execution/exact_execution.rb +9 -0
- data/lib/phronomy/agent/execution/execution_cancellation.rb +1 -1
- data/lib/phronomy/agent/execution/execution_coordinator.rb +63 -5
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +24 -4
- data/lib/phronomy/agent/execution/execution_registry.rb +5 -1
- data/lib/phronomy/agent/execution/initial_preparation.rb +10 -1
- data/lib/phronomy/agent/execution/invocation_actions.rb +1 -1
- data/lib/phronomy/agent/execution/invocation_transitions.rb +4 -4
- data/lib/phronomy/agent/execution/iteration_budget.rb +35 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +3 -0
- data/lib/phronomy/agent/execution_observation.rb +5 -5
- data/lib/phronomy/agent/lifecycle/scope.rb +33 -23
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +4 -2
- data/lib/phronomy/agent/output_contract.rb +64 -0
- data/lib/phronomy/agent/output_error.rb +53 -0
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +3 -3
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +1 -0
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +1 -1
- data/lib/phronomy/agent/recovery/recovery_support.rb +5 -1
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +2 -1
- data/lib/phronomy/agent/runtime_input.rb +15 -4
- data/lib/phronomy/agent/saved_context_reader.rb +2 -1
- data/lib/phronomy/agent/store.rb +10 -2
- data/lib/phronomy/common/values/immutable.rb +33 -0
- data/lib/phronomy/context/assembly.rb +7 -4
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +3 -1
- data/lib/phronomy/engine/fsm_session.rb +16 -1
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +28 -5
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +14 -27
- data/lib/phronomy/generation/generator_verifier/approval.rb +30 -0
- data/lib/phronomy/generation/generator_verifier/cleanup_error.rb +15 -0
- data/lib/phronomy/generation/generator_verifier/invocation.rb +147 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +16 -0
- data/lib/phronomy/generation/generator_verifier/protocol.rb +55 -0
- data/lib/phronomy/generation/generator_verifier/result.rb +121 -0
- data/lib/phronomy/generation/generator_verifier/unaccepted_result_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +13 -11
- data/lib/phronomy/generation/generator_verifier.rb +170 -85
- data/lib/phronomy/llm_adapter/backends/lm_studio.rb +139 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +134 -12
- data/lib/phronomy/llm_adapter/backends/structured_response_capture.rb +41 -0
- data/lib/phronomy/llm_adapter/message.rb +4 -0
- data/lib/phronomy/llm_adapter/request.rb +17 -2
- data/lib/phronomy/llm_adapter/response.rb +11 -4
- data/lib/phronomy/llm_adapter/structured_output_unsupported_error.rb +14 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +4 -0
- data/lib/phronomy/multi_agent/shared_state.rb +1 -0
- data/lib/phronomy/output_parser/json_schema_parser.rb +113 -0
- data/lib/phronomy/output_parser/parse_error.rb +10 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +4 -0
- data/lib/phronomy/tracing/automatic.rb +14 -8
- data/lib/phronomy/tracing/base.rb +2 -1
- data/lib/phronomy/tracing/langfuse_tracer.rb +3 -3
- data/lib/phronomy/tracing/observation.rb +2 -1
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +3 -3
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy.rb +1 -2
- data/scripts/api_snapshot.rb +3 -0
- data/scripts/migrate_generator_verifier_result.rb +18 -0
- data/sig/phronomy/agent/execution_contract.rbs +4 -3
- data/sig/phronomy/agent.rbs +20 -7
- data/sig/phronomy/extensions.rbs +8 -3
- data/sig/phronomy/generation/generator_verifier.rbs +70 -0
- data/sig/phronomy/llm_adapter/backends/lm_studio.rbs +8 -0
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +1 -0
- data/sig/phronomy/llm_adapter/values.rbs +8 -2
- data/sig/phronomy/tracing/observation.rbs +1 -1
- data/sig/phronomy.rbs +2 -0
- metadata +59 -8
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +0 -27
- data/lib/phronomy/generation/low_confidence_error.rb +0 -14
- data/lib/phronomy/runtime_composition/generation_defaults.rb +0 -6
- data/sig/phronomy/generation/low_confidence_error.rbs +0 -6
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# LLM continuation metadata preservation
|
|
2
|
+
|
|
3
|
+
This correction follows [ADR-066](../decisions/066-llm-and-tool-operation-contracts.md).
|
|
4
|
+
It does not change the LLM/Tool/Agent ownership decision, add a dependency to the
|
|
5
|
+
contract, or teach Agent a provider wire format.
|
|
6
|
+
|
|
7
|
+
## Problem and contract
|
|
8
|
+
|
|
9
|
+
The RubyLLM backend converted SDK responses into Phronomy Response values without
|
|
10
|
+
their reasoning data. A later model turn therefore lost information RubyLLM could
|
|
11
|
+
otherwise replay. Saved assistant records already contained Message metadata, but
|
|
12
|
+
RuntimeInput reconstructed only model_id. Explicit Message imports dropped the
|
|
13
|
+
metadata too. These were separate losses along the same conversation path.
|
|
14
|
+
|
|
15
|
+
C09 Message/Response metadata remains immutable canonical JSON. Backend-owned
|
|
16
|
+
continuation data accompanies its assistant message through a Tool round, Journal
|
|
17
|
+
serialization, recovery and explicit Phronomy-message import. Agent preserves the
|
|
18
|
+
value without inspecting its backend keys. Metadata does not authorize a Tool,
|
|
19
|
+
change context selection, become answer content or become arbitrary SDK options.
|
|
20
|
+
Record/control metadata remains distinct from metadata inside an assistant payload.
|
|
21
|
+
|
|
22
|
+
The concrete RubyLLM backend owns the ruby_llm_continuation namespace, format
|
|
23
|
+
version 1, and conversion of SDK thinking text/signature and raw_reasoning. It
|
|
24
|
+
reconstructs those SDK options only inside the backend. RubyLLM still owns wire
|
|
25
|
+
format rendering; no SDK class or provider registry is patched for this fix.
|
|
26
|
+
No thinking toggle, extra model call, fallback, retry, sampling change or Tool
|
|
27
|
+
execution is introduced. Existing Tool thought_signature behavior is unchanged.
|
|
28
|
+
|
|
29
|
+
Streaming's final Response retains the same continuation data. Public content
|
|
30
|
+
chunks remain answer chunks; this change adds no reasoning-display event API.
|
|
31
|
+
SDK token usage semantics are unchanged.
|
|
32
|
+
|
|
33
|
+
## Replay compatibility
|
|
34
|
+
|
|
35
|
+
The backend records the requested model ID, provider, selected protocol name and
|
|
36
|
+
a SHA-256 of the API base. The endpoint itself and credentials are not stored in
|
|
37
|
+
the envelope. Reuse requires an exact match of this scope and format version.
|
|
38
|
+
An incompatible scope/version raises ConfigurationError before sending the next
|
|
39
|
+
model request. State is neither silently discarded nor sent to a different target.
|
|
40
|
+
Malformed envelopes fail explicitly. Legacy messages without the envelope remain
|
|
41
|
+
usable; missing historical reasoning cannot be reconstructed.
|
|
42
|
+
|
|
43
|
+
This conservative equality check is not a claim that all identical endpoints are
|
|
44
|
+
semantically unchanged, or that different models can never share reasoning. It
|
|
45
|
+
does not detect server runtime/template changes behind one URL, account changes,
|
|
46
|
+
or model replacements behind one model ID. Cross-model/provider reasoning
|
|
47
|
+
conversion is outside this correction.
|
|
48
|
+
|
|
49
|
+
Handoff continues to use its existing context selection policy. If a selected
|
|
50
|
+
assistant message carries this state, a same-scope target can replay it and a
|
|
51
|
+
different-scope RubyLLM target rejects it. Applications requiring transfer across
|
|
52
|
+
incompatible scopes must intentionally select/derive portable context using the
|
|
53
|
+
existing context policy; they must not forge the scope or treat metadata as
|
|
54
|
+
authority. Other backend implementations own their own metadata conventions.
|
|
55
|
+
|
|
56
|
+
## Persistence and compatibility
|
|
57
|
+
|
|
58
|
+
There is no DB schema change, new SDK object in storage, or changed public method
|
|
59
|
+
signature. RuntimeInput restores the canonical metadata Hash and uses historical
|
|
60
|
+
top-level model_id only when metadata does not already contain it. Canonical
|
|
61
|
+
metadata is authoritative if both representations exist. Missing metadata means
|
|
62
|
+
an empty Hash; malformed non-Hash metadata is rejected.
|
|
63
|
+
|
|
64
|
+
ContextImporter preserves explicitly supplied canonical Message metadata. It does
|
|
65
|
+
not inspect an externally supplied RubyLLM object's thinking field; converting
|
|
66
|
+
SDK-specific external histories remains a backend-boundary concern, not a new
|
|
67
|
+
Agent SDK dependency. Signed/provider-shaped values stay unmodified and subject
|
|
68
|
+
to the SDK 2.0.0 representation supported by the existing dependency constraint.
|
|
69
|
+
This is not general preservation of every SDK field (for example server-tool
|
|
70
|
+
raw_content), nor a promise of compatibility with untested providers.
|
|
71
|
+
|
|
72
|
+
## Evidence and acceptance
|
|
73
|
+
|
|
74
|
+
The compatibility regressions are:
|
|
75
|
+
|
|
76
|
+
- spec/phronomy/llm_adapter/continuation_metadata_spec.rb: actual SDK parsing and
|
|
77
|
+
rendered HTTP for text/signature, streaming, Responses encrypted state and
|
|
78
|
+
provider-shaped OpenRouter reasoning; scope/version rejection and legacy input.
|
|
79
|
+
- spec/phronomy/agent/continuation_metadata_spec.rb: real Agent Tool loops,
|
|
80
|
+
subsequent invocation, approval across reconstructed storage/Runtime, Handoff,
|
|
81
|
+
and an independent backend with opaque metadata and no RubyLLM calls.
|
|
82
|
+
- spec/phronomy/agent/ruby_llm_materializer_spec.rb: historical model_id reading.
|
|
83
|
+
|
|
84
|
+
The LM Studio gpt-oss-20b reasoning omission was independently reproduced before
|
|
85
|
+
this change. The prior isolated A/B diagnostic returned HTTP 200 in all four
|
|
86
|
+
trials. This correction therefore does not claim to explain or fix peg-native
|
|
87
|
+
HTTP 400, nor to complete Example 22. Actual model behavior and the entire
|
|
88
|
+
application acceptance remain separate verification stages.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# LM Studio provider output failures
|
|
2
|
+
|
|
3
|
+
## Scope and ownership
|
|
4
|
+
|
|
5
|
+
`Phronomy::LLMAdapter::LMStudio` is an explicitly selected concrete RubyLLM
|
|
6
|
+
backend. It handles rejection of the provider's generated conversation format.
|
|
7
|
+
It does not parse or repair partial model output, change prompts or sampling
|
|
8
|
+
parameters, split Tool/schema requests, or replay Agent/Workflow/Tool work.
|
|
9
|
+
The default `RubyLLM` backend and SDK global configuration remain unchanged.
|
|
10
|
+
|
|
11
|
+
C09 owns the meaning of the public `TransportError` reason code
|
|
12
|
+
`llm.provider_output_parse_failed`. A code does not itself authorize retry.
|
|
13
|
+
The concrete backend owns HTTP-body decoding and its transport policy under
|
|
14
|
+
ADR-011/066. Application composition selects that backend and its retry limit.
|
|
15
|
+
No new Contract or Contract dependency is required.
|
|
16
|
+
|
|
17
|
+
## Recognition and evidence limits
|
|
18
|
+
|
|
19
|
+
Recognition requires a RubyLLM BadRequestError/ServerError with its own HTTP
|
|
20
|
+
response and actual HTTP status 400 or 500. The JSON body must contain `error`
|
|
21
|
+
with the exact engine code 500, type `server_error`, and message:
|
|
22
|
+
|
|
23
|
+
The model produced output that does not match the expected peg-native format
|
|
24
|
+
|
|
25
|
+
The engine object may be direct, or JSON after the exact LM Studio prefix
|
|
26
|
+
`Engine protocol predict stream returned an error: `, carried in an error string
|
|
27
|
+
or the error object's message field. No search through arbitrary strings or
|
|
28
|
+
nested exception causes is performed. A malformed/unknown envelope, missing
|
|
29
|
+
response or different status/type/code/message remains an ordinary SDK failure.
|
|
30
|
+
The original SDK exception remains `cause`; the original HTTP status and body
|
|
31
|
+
are not rewritten. The engine's internal 500 is not treated as HTTP 500.
|
|
32
|
+
|
|
33
|
+
The offline HTTP fixture envelopes are synthetic. Follow-up real LM Studio
|
|
34
|
+
acceptance on 2026-10-24 captured the actual HTTP 400 error body and confirmed
|
|
35
|
+
classification as `llm.provider_output_parse_failed`. The eligible request was
|
|
36
|
+
resent once with identical bytes. After the retry also failed, the adapter
|
|
37
|
+
returned the final TransportError at its own limit; the measurement guard did
|
|
38
|
+
not trigger, and Agent/Workflow work was not replayed. A separate application
|
|
39
|
+
invocation completed initially with HTTP 200. Total: two application invocations
|
|
40
|
+
and three generation POSTs; this is not recovery within the failed invocation.
|
|
41
|
+
|
|
42
|
+
Live error identification and bounded retry are verified. Recovery by retry,
|
|
43
|
+
recovery rate, the PEG root cause and recurrence resolution remain unverified.
|
|
44
|
+
The unrecognized-envelope branch was not observed live. The failing host logs
|
|
45
|
+
contain parser suffixes but do not establish the complete pre-parser token
|
|
46
|
+
stream. Unknown formats fail closed without retry.
|
|
47
|
+
|
|
48
|
+
## Retry policy and configuration
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
context = RubyLLM.context do |config|
|
|
52
|
+
config.openai_api_base = "http://127.0.0.1:1234/v1"
|
|
53
|
+
config.openai_api_key = "lm-studio"
|
|
54
|
+
config.openai_protocol = :chat_completions
|
|
55
|
+
config.request_timeout = 120
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Phronomy.configure do |config|
|
|
59
|
+
config.llm_adapter = Phronomy::LLMAdapter::LMStudio.new(
|
|
60
|
+
context: context,
|
|
61
|
+
max_parse_retries: 1 # default 0; only 0 or 1 is accepted
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The selected backend snapshots the RubyLLM 2.0 context and disables SDK retries
|
|
67
|
+
within that snapshot. **Ordinary 429/500/network retries are also disabled for
|
|
68
|
+
this backend.** Applications needing the existing SDK transport policy should
|
|
69
|
+
keep the default RubyLLM backend; this selection is deliberately explicit.
|
|
70
|
+
The caller's context and global settings are not modified. The frozen snapshot
|
|
71
|
+
prevents later scalar context changes from increasing the retry budget.
|
|
72
|
+
|
|
73
|
+
Only a recognized parse failure can trigger the optional second attempt. It is
|
|
74
|
+
limited to non-streaming `complete`, provider `openai`, model
|
|
75
|
+
`openai/gpt-oss-20b`, no advertised Tools, no response schema, and no Tool messages
|
|
76
|
+
or pending Tool calls in the supplied history. The same immutable request is
|
|
77
|
+
used to construct a fresh operation-local SDK chat. Total generation POSTs are
|
|
78
|
+
at most two. The one-additional-attempt limit is a conservative policy choice,
|
|
79
|
+
not an empirically established optimum or a reliability guarantee.
|
|
80
|
+
|
|
81
|
+
Streaming is never replayed. Delivered chunks and application callback exception
|
|
82
|
+
identity are preserved. Cancellation is checked before another attempt; existing
|
|
83
|
+
worker lifetime and deadline behavior remain unchanged. A final failure exposes
|
|
84
|
+
the final SDK exception as cause. No successful result is synthesized from an
|
|
85
|
+
error body.
|
|
86
|
+
|
|
87
|
+
## Observation
|
|
88
|
+
|
|
89
|
+
The existing logical `llm.call` span covers the complete adapter operation.
|
|
90
|
+
There is no new trace identity or dependency from C09 to C15. Individual SDK HTTP
|
|
91
|
+
requests continue to use RubyLLM's instrumentation. Every failed adapter attempt
|
|
92
|
+
also emits a safe warning containing attempt number, reason code, actual HTTP
|
|
93
|
+
status when known, and whether a retry was selected; no prompt, raw body or
|
|
94
|
+
credential is included. Therefore a later success does not erase the first
|
|
95
|
+
failure from the diagnostic record.
|
|
96
|
+
|
|
97
|
+
An optional concrete-backend `on_attempt:` callable receives these safe fields
|
|
98
|
+
plus `outcome`. It must support concurrent calls if an adapter is shared and is
|
|
99
|
+
not a durable audit store. Logging/callback failures cannot replace the business
|
|
100
|
+
result or original exception. Applications can use SDK instrumentation when they
|
|
101
|
+
need request correlation or detailed HTTP evidence. Successful response events
|
|
102
|
+
do not invent an HTTP status that the public response does not expose.
|
|
103
|
+
|
|
104
|
+
## Verification
|
|
105
|
+
|
|
106
|
+
Offline contracts use a real loopback TCP HTTP server and the actual
|
|
107
|
+
RubyLLM/Faraday stack. They verify status/cause fidelity, default no retry, exactly
|
|
108
|
+
two POSTs for opt-in recovery/exhaustion, unchanged request JSON, excluded
|
|
109
|
+
requests, cancellation between attempts, callback identity, unknown responses,
|
|
110
|
+
configuration isolation and unchanged default-backend SDK retries. They do not
|
|
111
|
+
invoke a live model.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# r8
|
|
1
|
+
# r8 unit 1: Shared Context, Tool, and Execution foundations
|
|
2
2
|
|
|
3
3
|
> Current boundary: r8 unit 6 supersedes the historical Chat/SDK ownership and
|
|
4
4
|
> adapter extension SPI below. Agent uses LLMAdapter values; the backend owns SDK
|
|
@@ -6,99 +6,99 @@
|
|
|
6
6
|
> `docs/architecture/r8-unit6.md` and `docs/migrations/r8-unit6.md`.
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The baseline is main `653e255a34fe4062d4beca47530e084fdd3a6ba9`, checked on 2026-09-30. The design references are the revised r8 specification and directory layout documents updated that day. This change is the first implementation unit; it does not mean that all of r8, every S1 item, or every S2/S3 acceptance condition is complete. Compatibility aliases and old APIs that only forward calls are not retained.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Implemented responsibilities and operations
|
|
12
12
|
|
|
13
|
-
|
|
|
13
|
+
| Owner | What the shared foundation does | What remains with the caller |
|
|
14
14
|
|---|---|---|
|
|
15
|
-
| `Context::Assembly` | Policy
|
|
16
|
-
| `Tool::DefinitionSet` | Tool
|
|
17
|
-
| `Tool::Authorization` |
|
|
18
|
-
| `Tool::ToolExecutor` | cooperative/offloaded
|
|
19
|
-
| `LLMAdapter::Base` / `RubyLLM` |
|
|
20
|
-
| `Execution` | submit
|
|
21
|
-
| `Tracing::Observation` |
|
|
15
|
+
| `Context::Assembly` | Calls the Policy once and validates the Plan; normalizes content, checks the final budget, and stores the Manifest | Agent obtains the snapshot, rechecks the watermark immediately before persistence, and updates the Agent execution record |
|
|
16
|
+
| `Tool::DefinitionSet` | Normalizes Tool definitions and validates name uniqueness and consistency with persisted definitions | Agent prepares its own Tool aliases, decorators, and runtime instances |
|
|
17
|
+
| `Tool::Authorization` | Evaluates facts -> requirement -> policy -> decision using immutable arguments and authorization context | Agent manages execution matching, approval state, admission results, and supervision |
|
|
18
|
+
| `Tool::ToolExecutor` | Handles cooperative/offloaded execution rules and results | Pool selection in Engine is delegated to the Execution mechanism binding |
|
|
19
|
+
| `LLMAdapter::Base` / `RubyLLM` | Handles synchronous generation and chat construction/configuration, message construction, and tool-call construction; the RubyLLM implementation translates SDK failures | Agent validates its persisted data and Tool definitions before passing them to the adapter |
|
|
20
|
+
| `Execution` | Owns the submit entry point, fan-out/fan-in, result composition, and cancellation/deadline/waiting rules | Engine implements pools, timers, Runtime, and FSM mechanisms |
|
|
21
|
+
| `Tracing::Observation` | Provides explicit observation and PII redaction without changing the actual return value | Runnable owns the invoke/stream/batch execution contract |
|
|
22
22
|
|
|
23
|
-
Context
|
|
23
|
+
Context and Tool own real shared procedures, rather than merely housing thin Agent type definitions. However, current Context inputs still contain Agent-derived identifiers and provenance. This is not a redesign into a fully generic domain model. The Tool DSL base continues to use RubyLLM::Tool.
|
|
24
24
|
|
|
25
|
-
`Context::Assembly#prepare(input:, policy:, adapter_identity:)`
|
|
25
|
+
`Context::Assembly#prepare(input:, policy:, adapter_identity:)` calls the Policy outside the persistence transaction. `#store(prepared, contents:)` uses only the contents repository supplied by the caller from the same transaction; it does not start an independent transaction. Initial and follow-up Agent commits store the Manifest and the AgentExecution reference within the existing transaction. If a later write fails, the Manifest is rolled back too, and the Policy is not executed again. The existing ordering, which applies the live projection after commit, is preserved. This validates the Agent/Context part of P02; it does not complete participation in outer transactions for arbitrary domains.
|
|
26
26
|
|
|
27
|
-
Execution
|
|
27
|
+
Execution depends on the operations of `_ExecutionBackend`. Composition configures a deferred provider, and Engine's `ExecutionBinding` implements submit/after. Creating settled TaskResults, using map/flat_map/all_settled, and checking waiting constraints do not load Engine. Runtime is obtained only when work is actually submitted or a timer is registered. Mechanism references are consolidated here, rather than merely being renamed or nominally reassigned.
|
|
28
28
|
|
|
29
|
-
Workflow
|
|
29
|
+
`Workflow::Completion` owns Workflow FINISH and completion values, and WorkflowRunner translates them into the FSM representation. Event/FSMProtocol/TerminalDecision, which are specific to FSMSession, live in Engine. Full separation of Agent and MultiAgent FSM-specific bindings remains outstanding.
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## Public names and layout changes
|
|
32
32
|
|
|
33
|
-
|
|
|
33
|
+
| Previous location/API | New location/API | Rationale |
|
|
34
34
|
|---|---|---|
|
|
35
|
-
| `agent/context_contract/`
|
|
36
|
-
| `agent/context_policies/default.rb` | `context/default_policy.rb
|
|
37
|
-
| `Agent::Context::Instruction::PromptTemplate` | `Context::PromptTemplate` | Agent
|
|
38
|
-
| `Agent::Selection::{Candidate,Constraint}` | `Context::{Candidate,Constraint}` |
|
|
39
|
-
| `Agent::LLMInputPatch` / `LLMInputBuildContext` | `Context::LLMInputPatch` / `LLMInputBuildContext` | hook
|
|
40
|
-
| `ContextBudgetExceededError` | `Context::BudgetExceededError` | LLM
|
|
41
|
-
| `agent/context_assembly/` | `context/assembly.rb`
|
|
42
|
-
| `Agent::Context::Capability::Base` |
|
|
43
|
-
| `agent/tool_execution/tool_definition_set.rb` | `tool/definition_set.rb` |
|
|
44
|
-
| `execution_contract/`
|
|
45
|
-
| `WorkerSubmission` | `Execution.submit` |
|
|
46
|
-
| `Runnable#trace` | `Tracing::Observation.trace` |
|
|
47
|
-
| `tool/contract
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
## r8
|
|
54
|
-
|
|
55
|
-
|
|
|
35
|
+
| `agent/context_contract/` and APIs such as `Agent::ContextPolicy` | `context/` and APIs such as `Context::ContextPolicy` | Consolidate Policy, Plan, Manifest, and validation under the owner of the shared operations |
|
|
36
|
+
| `agent/context_policies/default.rb` | `context/default_policy.rb`, `Context::DefaultPolicy` | Keep the concrete Policy in the same domain |
|
|
37
|
+
| `Agent::Context::Instruction::PromptTemplate` | `Context::PromptTemplate` | Remove the old public name under Agent |
|
|
38
|
+
| `Agent::Selection::{Candidate,Constraint}` | `Context::{Candidate,Constraint}` | Remove the thin Selection subdivision |
|
|
39
|
+
| `Agent::LLMInputPatch` / `LLMInputBuildContext` | `Context::LLMInputPatch` / `LLMInputBuildContext` | Unify ownership of hook inputs and return values |
|
|
40
|
+
| `ContextBudgetExceededError` | `Context::BudgetExceededError` | Represent a Context budget failure, not an LLM provider failure |
|
|
41
|
+
| `agent/context_assembly/` | `context/assembly.rb` plus preparation, state, and input handling directly under Agent | Separate shared Context procedures, Agent snapshot/state handling, and LLM SDK handling |
|
|
42
|
+
| `Agent::Context::Capability::Base` | The actual implementation, `Tool::Base` | Remove the old name and alias |
|
|
43
|
+
| `agent/tool_execution/tool_definition_set.rb` | `tool/definition_set.rb` | Transfer shared definition handling; keep Agent-specific construction in Agent |
|
|
44
|
+
| `execution_contract/` and `execution_services/` | `execution/` and the necessary `execution/concurrency/` | Consolidate operations, values, and shared rules; do not add thin subdivisions such as `execution_result` |
|
|
45
|
+
| `WorkerSubmission` | `Execution.submit` | Remove the forwarding-only module |
|
|
46
|
+
| `Runnable#trace` | `Tracing::Observation.trace` | Remove the execution foundation's dependency on observation |
|
|
47
|
+
| `tool/contract`, `filter/contract`, `output_parser/contract`, `agent/lifecycle_contract` | Exception files under their owning domains | Remove exception-only directories; retain existing meaningful exception constant names as actual definitions |
|
|
48
|
+
|
|
49
|
+
Custom LLM adapters implement `build_chat`, `configure_chat`, `message`, and `tool_call` in addition to complete/stream. The base class supplies a default `identity` and a nil default for `input_budget`. See `sig/phronomy/llm_adapter/base.rbs` for exact keywords and return contracts. Translate SDK-specific exceptions into LLM failures at adapter operation boundaries, preserving the original cause. Do not translate application exceptions or cancellation into unrelated provider failures.
|
|
50
|
+
|
|
51
|
+
The existing `tool_result_max_size` configuration API and logger are supplied by application composition to neutral RuntimeSettings. Remove references from Tool to the composition factory while preserving default, nil, and scoped-copy semantics. Place RBS in `sig/` and `sig/_private/`, and analyze dependencies based on implementation constants and declaration ownership.
|
|
52
|
+
|
|
53
|
+
## Progress by r8 item
|
|
54
|
+
|
|
55
|
+
| Item | This implementation unit | Outstanding work |
|
|
56
56
|
|---|---|---|
|
|
57
|
-
| DP01 Context
|
|
58
|
-
| DP02 Tool
|
|
59
|
-
| DP03 Execution
|
|
60
|
-
| DP04 Persistence |
|
|
61
|
-
| DP05
|
|
62
|
-
| DP06 LLM
|
|
63
|
-
| DP07
|
|
64
|
-
| DP08
|
|
65
|
-
| DP09 Embeddings
|
|
66
|
-
| DP10 Documents |
|
|
67
|
-
| DP11 assembly/concerns
|
|
68
|
-
| DP12
|
|
69
|
-
| DP13 Recovery/Handoff |
|
|
70
|
-
| P02
|
|
71
|
-
| P01/P03
|
|
72
|
-
| 155
|
|
57
|
+
| DP01 Context consolidation | Main scope implemented | Broader domain inputs and public operations still require review |
|
|
58
|
+
| DP02 Shared Tool foundation | Shared definition, authorization evaluation, and invocation implemented | Agent FSM bindings and all parent/child authorization rules remain under DP12/P04 |
|
|
59
|
+
| DP03 Execution consolidation | Implemented | This does not complete the review of FSM use across all domains |
|
|
60
|
+
| DP04 Persistence | Not started | Facade removal and shared transaction/participation/outcome-reconciliation APIs |
|
|
61
|
+
| DP05 Domain persistence schemas | Not started | Consolidation of MultiAgent/Workflow storage_contract |
|
|
62
|
+
| DP06 LLM consolidation | Prerequisites only | Full consolidation of llm_contract and llm_adapter |
|
|
63
|
+
| DP07 Consolidation of thin failure directories | Implemented | This is not a redesign of every domain's failure vocabulary |
|
|
64
|
+
| DP08 Composition consolidation | Bindings added only | Physical consolidation of runtime/persistence/agent composition |
|
|
65
|
+
| DP09 Independent Embeddings domain | Not started | Placement independent of vector_store |
|
|
66
|
+
| DP10 Documents | Not started | Loader/splitter relocation |
|
|
67
|
+
| DP11 Decomposition of assembly/concerns | Main scope implemented | Agent-specific processing remains directly under Agent |
|
|
68
|
+
| DP12 Domain execution bindings | Partial, including the Workflow completion boundary | Full separation of Agent/ToolInvocation/Team domain decisions from FSM bindings |
|
|
69
|
+
| DP13 Recovery/Handoff | Not started | Ownership of domain recovery, Persistence outcome reconciliation, and parent/child operations |
|
|
70
|
+
| P02 Persistence participation | Validated in the existing Agent/Context transaction | Outer scopes, savepoints, and public participation APIs across multiple domains |
|
|
71
|
+
| P01/P03-P06 Persistence and parent/child coordination | Not started | The atomicity conditions below must be established |
|
|
72
|
+
| Triangle review: 155 pairs, 393 cases | Reassessment incomplete | Regenerating the diagrams does not substitute for completing the assessment |
|
|
73
73
|
|
|
74
|
-
NC/F/RM
|
|
74
|
+
No document claims that every NC/F/RM item has been addressed. Acceptance claims are limited to the implementation units in this table and the accompanying test evidence.
|
|
75
75
|
|
|
76
|
-
## Persistence
|
|
76
|
+
## Why Persistence is outside this implementation unit
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Initial Agent admission currently reads the Team reservation inside the persistence transaction. The final design should eliminate Agent's reference to Team internals, but simply moving validation to an earlier call creates this race:
|
|
79
79
|
|
|
80
|
-
1.
|
|
81
|
-
2.
|
|
82
|
-
3. Agent
|
|
80
|
+
1. The parent checks that the reservation is valid.
|
|
81
|
+
2. Another operation cancels the reservation.
|
|
82
|
+
3. Agent saves its admission record in a new transaction and starts the cancelled child.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
The next implementation unit needs a participation API that performs the parent-owned reservation check and records Agent admission within the same scope. The parent owns reservation rules, Agent owns admission rules, and Persistence owns participation and commit boundaries. Merely hiding the current reverse dependency, where Agent switches on parent kind to select Team, behind a new callback would not resolve it.
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
The unresolved details are public participation keywords/return values, races with cancellation, post-commit Runtime admission, and querying/resuming with the same child ID after interruption. Workflow halt/finish snapshots alone do not make child execution inside arbitrary actions durable. P05 must define the concrete Workflow-to-Agent/Team paths, checkpoints, and one-time result incorporation. Unknown outcomes must not be treated as success based on a single matching record; P06 must define evidence across multiple domains.
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
Therefore this change does not alter the existing Persistence facade, Team admission validation, or transaction boundaries for reservations and child records. These remain for the next specification and implementation unit, rather than introducing provisional code with weaker guarantees.
|
|
89
89
|
|
|
90
|
-
##
|
|
90
|
+
## Reading the analysis diagrams
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
Regenerate the diagrams from actual source and RBS. Preserve the existing horizontal bands, group colors, individual module boxes, and transparent text backgrounds. Arrows to M33/M41/M44 are omitted only from the display; retain them in JSON, matrices, and boundary assessments. Retire old IDs instead of reusing them.
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
These are measured diagrams of the revised candidate, not conceptual diagrams of a completed r8. Include remaining cycles and reverse dependencies. RBS alone cannot fully prove injected targets or dynamic dispatch that static analysis cannot observe. Combine it with execution tests for Engine mechanism bindings and domain persistence.
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
97
|
bundle exec rbs -I sig validate
|
|
98
98
|
python -m pip install -r tools/architecture/tools/requirements.txt
|
|
99
99
|
python -m unittest discover -s tools/architecture/tests -v
|
|
100
|
-
# lib/sig
|
|
100
|
+
# After committing lib/sig, use this option for an unpublished candidate.
|
|
101
101
|
python tools/architecture/refresh_diagram.py . tmp/architecture-r8-unit1 --candidate
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
The SHA in the supplied SVG identifies the local candidate used to generate it. It differs from the SHA created when the user applies and commits the changes, so regenerate the post-application diagrams from that checkout. See the distribution package's verification report for the Ruby environment, test counts, skip reasons, unverified environments, and application procedure.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# r8 unit15: Generation contract boundaries
|
|
2
2
|
|
|
3
|
+
> Historical contract: parser injection/fallback is superseded by
|
|
4
|
+
> [structured output and scope](../architecture/structured-output-and-scope.md).
|
|
5
|
+
|
|
3
6
|
This responsibility refactor is based on applied unit14, commit
|
|
4
7
|
`97c3d76b35594bb971a60a2932410ddf22e48d3a` (tree
|
|
5
8
|
`c326196212a4ac369294a9ccb936619813e51250`).
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
# r8 unit 2
|
|
1
|
+
# r8 unit 2: Parent reservation checks and Agent admission
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This unit implements the P02/P03 admission boundary. The core baseline is `16e6fcb2dd49836735598525114abfc256d0773e`. It does not complete all of r8 or the entire Persistence reorganization.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Responsibilities and operations
|
|
6
6
|
|
|
7
|
-
|
|
|
7
|
+
| Owner | Public operations and shared rules | Dependencies and internal implementation |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
|
|
|
10
|
-
| Agent | `Admission#accept_in
|
|
11
|
-
| MultiAgent | `ReservedChildAdmission#admit
|
|
12
|
-
|
|
|
9
|
+
| Shared Persistence | `atomic`, `Transaction#participate`, and `committed?`; validates the same Persistence instance, synchronous thread, active scope, savepoints, and commit acknowledgements | Uses Storage's public transaction/scope rules; does not select Agent, Team, Workflow, or composition components |
|
|
10
|
+
| Agent | `Admission#accept_in`; persists input records, execution identity, Agent admission, and revision updates in the same scope | Uses Agent's persistence component; does not interpret parent kind, Team assignments, subagent snapshots, or Handoff reservations |
|
|
11
|
+
| MultiAgent | `ReservedChildAdmission#admit`; validates parent cancellation, slot, and child ID, and has Agent admission participate in the same scope | Its own persistence component reads the reservation evidence and acquires the required guards |
|
|
12
|
+
| Persistence components | `Agent::Persistence::Admission`, `MultiAgent::Persistence::Reservation` | Uses raw Storage views at this boundary; does not pass raw views to domain operations |
|
|
13
13
|
|
|
14
|
-
Agent
|
|
14
|
+
The existing ExecutionCoordinator continues to own acquiring Agent Runtime admission, starting the FSM, and applying live state. The new Admission covers only the short persistence phase; it does not place the entire Agent execution inside the parent's database transaction. Ordinary applications do not need to implement or instantiate the new participation classes.
|
|
15
15
|
|
|
16
|
-
`phronomy_admission`
|
|
16
|
+
`phronomy_admission` is current internal wiring; no Proc or object is persisted. Only the existing correlation information and parent/child IDs are stored. The Team/subagent/Handoff start and resume components supply the current participant. Reconnecting to a persisted execution preserves ExactExecution's same-ID matching and recovery rules.
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Persistence ordering and failures
|
|
19
19
|
|
|
20
|
-
1. Agent
|
|
21
|
-
2. MultiAgent
|
|
22
|
-
3.
|
|
23
|
-
4. commit
|
|
20
|
+
1. Agent acquires the existing Runtime admission and begins the persistence operation on a synchronous worker.
|
|
21
|
+
2. MultiAgent opens a short `atomic` scope, acquires the parent guard, and then rereads the reservation.
|
|
22
|
+
3. It validates parent cancellation, assignment, and child identity. Agent stores the input content, AgentExecution, and AgentRoot in the same scope.
|
|
23
|
+
4. Only after a successful commit acknowledgement does the Agent execution component obtain the admission result and continue preparation, FSM execution, and live-state updates.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
A participation operation's return value does not mean that commit succeeded. `accept_in` returns nil, and the Agent-internal `result` is unavailable until the outer commit. Duplicate participation, a different Persistence instance, a different thread, or an expired scope is rejected. Separate Persistence instances wrapping the same backend are not treated as the same scope. Code inside a raw backend transaction cannot claim to be a new commit owner.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Explicit nesting uses savepoints. Inner success is not committed if the outer transaction rolls back. Even if an exception during participation is caught inside the scope, earlier changes in that failed scope cannot be committed. Do not perform LLM calls, Tool calls, scheduling, external callbacks, or asynchronous waits inside participation operations.
|
|
28
28
|
|
|
29
|
-
Storage
|
|
29
|
+
Storage scope violations are translated into `Persistence::TransactionError`, preserving cause and backtrace. This naming change does not assert that the persistence outcome is known. Persistence failures with unknown outcomes, such as IOError, are neither translated nor automatically retried; they pass to existing Agent recovery rules. This unit does not add a generalized persistence-outcome reconciliation API.
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## Races with cancellation
|
|
32
32
|
|
|
33
|
-
Team
|
|
33
|
+
Team uses TeamRoot as its guard, subagents use the parent AgentRoot, and Handoff uses the routing record. On PostgreSQL, row locks on the guard and existing conditional updates order cancellation/changes against child admission. Ordinary reads on the child admission path alone did not provide this guarantee.
|
|
34
34
|
|
|
35
|
-
Handoff
|
|
35
|
+
The Handoff cancellation path first observes the child state. If the child is incomplete or absent, it locks routing and **reads the child state again**. If child admission committed while it was waiting, it discards the stale absence observation and sends public cancellation to the admitted child. Cancellation of an already terminal execution continues to act as result access.
|
|
36
36
|
|
|
37
|
-
InMemory
|
|
37
|
+
InMemory uses a shared Monitor, SQLite uses transactions and write-conflict detection, and PostgreSQL uses row locks. A conflicting SQLite transaction is not considered successful; its backend failure is handled. No distributed lease or exactly-once guarantee for external effects is added. Actual PostgreSQL waiting order is to be checked by the three new concurrency tests in examples; it must not be inferred from successful InMemory/SQLite tests.
|
|
38
38
|
|
|
39
|
-
## API
|
|
39
|
+
## API and file changes
|
|
40
40
|
|
|
41
|
-
- Agent::InitialPreparation
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
- Persistence
|
|
45
|
-
- RBS
|
|
46
|
-
-
|
|
47
|
-
-
|
|
41
|
+
- Remove Agent::InitialPreparation's `build_admitted_execution`, its three kinds of parent validation, and its parent-kind branches. Separate shared Agent admission handling from MultiAgent reservation decisions.
|
|
42
|
+
- Place the shared participation framework in `persistence/transaction.rb`, Agent admission in `agent/admission.rb`, and persistence bindings in each domain's existing `persistence/`. No new directory or independent Contract is added.
|
|
43
|
+
- Add synchronous-context `transaction_open?` to the Storage SPI. Implementations inheriting Storage::Backend receive a shared implementation. Custom SPI implementations that do not inherit it must also provide this operation. Update RBS, the SPI snapshot, and duck-typed backend validation.
|
|
44
|
+
- Unify the public type for known Persistence scope failures as `Persistence::TransactionError`; do not add an alias for the old Storage exception. Code using Storage directly continues to use the existing Storage exception.
|
|
45
|
+
- Place RBS in sig and declare participant factories, Agent admission, and MultiAgent dependencies. Track dynamic `phronomy_admission` injection in this document and at its use sites.
|
|
46
|
+
- Separate dependency analysis rules for the Execution boundary from the diagram's revision display name. Add a regression test ensuring that changing the unit name does not disable the prohibition on synchronous-backend dependencies on Execution.
|
|
47
|
+
- Preserve persisted record formats, IDs, and revision rules. No data migration is required.
|
|
48
48
|
|
|
49
|
-
##
|
|
49
|
+
## Outstanding work
|
|
50
50
|
|
|
51
|
-
`Persistence#transaction
|
|
51
|
+
`Persistence#transaction` and the combined domain-repository facade still have callers and are not fully removed in this unit. This does not add new aliases for old names to preserve compatibility, but the P01/DP04 completion conditions remain unmet. Next, move domain queries and persistence composition, then remove unnecessary entry points together with their callers.
|
|
52
52
|
|
|
53
|
-
MultiAgent
|
|
53
|
+
MultiAgent's persistence component still references Agent execution/Handoff repositories. Ownership of reservation validation has been clarified, but this does not mean that public parent/child projections, authorization queries, and result incorporation have all been encapsulated. Under the remaining P04/F02/F03 work, establish the operations Agent should expose and the persistence formats MultiAgent should own. Adding a generic metadata getter alone is not a solution.
|
|
54
54
|
|
|
55
|
-
Agent
|
|
55
|
+
Agent/Team FSM bindings, durable Workflow child coordination (P05), reconciliation of persistence outcomes across multiple records (P06), the remaining layout reorganization, and semantic reassessment of 155 pairs/393 cases also remain outstanding. The SVG for this unit depicts the implementation candidate, not a conceptual graph claiming that all cycles have been eliminated.
|
|
@@ -6,7 +6,8 @@ all of C04, C06 or the overall target complete.
|
|
|
6
6
|
|
|
7
7
|
| Owner | Responsibility after this change |
|
|
8
8
|
| --- | --- |
|
|
9
|
-
| Agent
|
|
9
|
+
| Agent IterationBudget | Logical LLM-call limit over durable AgentExecution records; see [iteration budget](agent-iteration-budget.md) |
|
|
10
|
+
| Agent InvocationTransitions | Phase vocabulary, ordered automatic/external transitions, guard evaluation, automatic-chain safety bound |
|
|
10
11
|
| Agent InvocationActions | Prepared input, LLM dispatch, Tool batch orchestration and result/event interpretation |
|
|
11
12
|
| Agent ExecutionCoordinator / RecoveryCoordinator | Admission, durable preparation, approval, terminal outcome, recovery and causal revision checks |
|
|
12
13
|
| Agent ExecutionEnvironment | Private process-local connection required by the Agent framework; session construction/registration, serial owner and operation submission |
|