phronomy 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +4 -0
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +89 -0
- data/docs/architecture/before-llm-input.md +6 -0
- data/docs/architecture/context-management.md +30 -0
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-metadata-and-values.md +80 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +8 -2
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +27 -4
- data/docs/architecture/refactoring-closure.md +334 -0
- data/docs/architecture/remaining-refactoring-plan.md +374 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/tool-schema-recording-gap.md +50 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
- data/docs/decisions/032-storage-backend-composition.md +80 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
- data/docs/decisions/040-configuration-default-composition.md +94 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +78 -0
- data/docs/decisions/README.md +35 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/features.md +9 -3
- data/docs/getting-started.md +4 -4
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
- data/docs/migrations/handoff-runner-multi-agent.md +35 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/persistence-backends.md +162 -204
- data/docs/runtime-and-concurrency.md +117 -32
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/agent/async_event_api.rb +2 -2
- data/lib/phronomy/agent/base.rb +51 -241
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context/capability/base.rb +67 -26
- data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
- data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
- data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
- data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
- data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
- data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
- data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
- data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
- data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
- data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
- data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
- data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
- data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
- data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
- data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
- data/lib/phronomy/agent/persistence/codec.rb +358 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
- data/lib/phronomy/agent/persistence/queries.rb +61 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
- data/lib/phronomy/agent/persistence/watermark.rb +27 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
- data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
- data/lib/phronomy/agent/selection/candidate.rb +1 -1
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
- data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
- data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
- data/lib/phronomy/configuration/global_configuration.rb +26 -0
- data/lib/phronomy/content_store/storage_schema.rb +11 -0
- data/lib/phronomy/content_store/stored_contents.rb +43 -0
- data/lib/phronomy/engine/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
- data/lib/phronomy/engine/cancellation_error.rb +7 -0
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
- data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +158 -624
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
- data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +35 -28
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
- data/lib/phronomy/engine/runtime.rb +80 -84
- data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/engine/task_result.rb +4 -0
- data/lib/phronomy/engine/timeout_error.rb +7 -0
- data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
- data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
- data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
- data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
- data/lib/phronomy/llm_contract/transport_error.rb +7 -0
- data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
- data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
- data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
- data/lib/phronomy/persistence/api/persistence.rb +140 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
- data/lib/phronomy/persistence_composition/repositories.rb +77 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/storage/backend.rb +101 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/contract/tool_error.rb +7 -0
- data/lib/phronomy/tools/agent.rb +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
- data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
- data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +1 -1
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +0 -1
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +18 -0
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +10 -1
- metadata +230 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_executor.rb +0 -55
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/workflow_recovery.rb +0 -123
- /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
- /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
- /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
- /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# ADR-043: Storage-Owned Execution Constraint Notifications
|
|
2
|
+
|
|
3
|
+
The raw fixed-repository/error portions are amended by [ADR-058](058-neutral-storage-primitives.md). Domain ownership and applicable transaction/uncertainty decisions remain in force.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
**Status**: Accepted
|
|
7
|
+
**Date**: 2026-09-21
|
|
8
|
+
**Refines**: [033-domain-persistence-ownership](033-domain-persistence-ownership.md) and the Beta Backend error contract in [persistence-backends](../persistence-backends.md)
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
InMemory and the SQLite/PostgreSQL reference backends raised `AgentBusyError`
|
|
13
|
+
for stored nonterminal-execution constraints. A storage implementation therefore
|
|
14
|
+
selected a feature lifecycle exception, even though domain repositories already
|
|
15
|
+
owned record conversion and public operation semantics.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
`Storage::ActiveExecutionConflictError < Storage::ConflictError` identifies only
|
|
20
|
+
a stored nonterminal execution preventing another admission or an idle-only
|
|
21
|
+
operation for the same owner. It contains no Agent/Team class, domain object,
|
|
22
|
+
callback, or new persisted field. Generic duplicate IDs, stale revisions and
|
|
23
|
+
other precondition failures remain ordinary `Storage::ConflictError` values;
|
|
24
|
+
connection errors and uncertain outcomes retain their existing error types.
|
|
25
|
+
|
|
26
|
+
Raw execution repositories use this subtype from `create_active`, `assert_idle!`,
|
|
27
|
+
and any existing `save` branch that detects the same active-owner constraint.
|
|
28
|
+
Agent and MultiAgent execution repository facades translate only this subtype
|
|
29
|
+
to the existing public `AgentBusyError`, preserving its message and Ruby cause.
|
|
30
|
+
They do not catch every `ConflictError` or reinterpret database exceptions.
|
|
31
|
+
The existing Team public error remains unchanged; choosing a different Team
|
|
32
|
+
lifecycle exception would be a separate API decision.
|
|
33
|
+
|
|
34
|
+
Constraint detection and the write remain inside the same backend consistency
|
|
35
|
+
boundary. No preflight lookup is moved above the backend. Domain translation
|
|
36
|
+
occurs inside the existing transaction block so the mapped exception still
|
|
37
|
+
causes rollback before commit. SQL statements, lock order, connection binding,
|
|
38
|
+
indexes, persisted records and transaction boundaries are unchanged.
|
|
39
|
+
|
|
40
|
+
## Compatibility and migration
|
|
41
|
+
|
|
42
|
+
This intentionally changes the Beta raw Backend error contract. Callers of
|
|
43
|
+
`backend.executions` and `backend.team_executions` must catch the new storage
|
|
44
|
+
subtype instead of `AgentBusyError`. Callers of the domain-facing Persistence,
|
|
45
|
+
Agent and Team APIs continue receiving `AgentBusyError` for that condition.
|
|
46
|
+
|
|
47
|
+
The core, InMemory and both reference SQL backends are migrated together. Apply
|
|
48
|
+
the core before the new SQL sources; the new backend sources require the new
|
|
49
|
+
storage constant. An old backend that still raises `AgentBusyError` passes
|
|
50
|
+
through an updated domain facade unchanged, but no longer conforms to the new
|
|
51
|
+
raw SPI. There is no fallback alias in Storage or Engine.
|
|
52
|
+
|
|
53
|
+
The shared backend suite now checks raw notifications separately from domain
|
|
54
|
+
exceptions, including duplicate-ID and stale-revision distinction and rollback.
|
|
55
|
+
The storage isolation guard removes its former Agent lifecycle file exemption.
|
|
56
|
+
|
|
57
|
+
## Guarantees and limits
|
|
58
|
+
|
|
59
|
+
| Subject/property | Provider | Failure/boundary | Result |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| One nonterminal execution per stored owner | Existing atomic admission, backend lock/transaction/DB constraint | F2; no X0 | CONDITIONAL on the backend satisfying the existing atomic_admission contract; no new cross-process owner lease |
|
|
62
|
+
| Uncommitted writes roll back when a mapped constraint escapes | Existing storage transaction with domain conversion inside its block | F0/F2; no X0 | YES for an ordinary known pre-commit constraint failure; not a claim about uncertain commits |
|
|
63
|
+
| Caller-facing busy errors retain their meaning | Feature repository translation of the dedicated subtype | F0/F2; no X0 | YES for the documented domain repository paths; unrelated conflicts are not converted |
|
|
64
|
+
|
|
65
|
+
This change adds no F1 outcome reconciliation, F4 rehydration, external-effect
|
|
66
|
+
rollback or exactly-once guarantee. The eight repository names and Agent
|
|
67
|
+
watermark that ADR-033 leaves in Storage remain an explicit intermediate state.
|
|
68
|
+
Removing the exception dependency does not make the entire SPI domain-neutral.
|
|
69
|
+
|
|
70
|
+
## Rejected alternatives
|
|
71
|
+
|
|
72
|
+
- Move `AgentBusyError` into common definitions or alias it from Storage.
|
|
73
|
+
- Translate every `ConflictError` into a busy error.
|
|
74
|
+
- Check active state in the domain facade before performing a separate write.
|
|
75
|
+
- Migrate only InMemory and silently leave the SQL raw contract inconsistent.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR-044: Agent Default and One-Shot Composition
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted on the architecture refactoring branch
|
|
4
|
+
**Date**: 2026-09-21
|
|
5
|
+
**Refines**: [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
|
|
6
|
+
and [040-configuration-default-composition](040-configuration-default-composition.md)
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
`Agent::Base` selected the concrete default with `Persistence.in_memory`.
|
|
11
|
+
`Agent.run_once`, defined beside Agent lifecycle namespace loading, also selected
|
|
12
|
+
and constructed Persistence. Both files therefore depended on the higher-level
|
|
13
|
+
Persistence composition API, but for different reasons.
|
|
14
|
+
|
|
15
|
+
Default selection is an injected construction dependency of Agent execution.
|
|
16
|
+
One-shot execution is itself a composition API. Treating both as lower-level
|
|
17
|
+
Agent execution obscured those responsibilities. Replacing one-shot storage with
|
|
18
|
+
the configured shared Persistence would change isolation and existing behavior.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
1. Agent owns the private `Agent::DefaultPersistence` construction contract. A
|
|
23
|
+
zero-argument factory is bound once and frozen during application loading.
|
|
24
|
+
`Base` invokes it only after the explicit `persistence:` and configured
|
|
25
|
+
`configuration.persistence` values have both been excluded, preserving their
|
|
26
|
+
existing truthy fallback order. It neither caches the constructed instance
|
|
27
|
+
nor writes it to configuration. Subclasses consume the same binding.
|
|
28
|
+
2. `runtime_composition/agent_defaults.rb` selects `Persistence.in_memory` in
|
|
29
|
+
that factory. Binding does not invoke it, create settings, or start Runtime.
|
|
30
|
+
The binding is not a public plugin registry, an alternative application
|
|
31
|
+
configuration mechanism, or a new Runtime service locator. Runtime and
|
|
32
|
+
configuration resets do not replace or reinstall it.
|
|
33
|
+
3. `agent/composition/run_once.rb` owns the actual `Agent.run_once` definition.
|
|
34
|
+
It remains `Phronomy::Agent.run_once`, with the same parameters, forwarding,
|
|
35
|
+
result, and exception behavior. It constructs fresh ephemeral Persistence on
|
|
36
|
+
every call even when global Persistence is configured. It does not consume
|
|
37
|
+
Base's fallback factory. Rejecting simultaneous `on_event:` and a block still
|
|
38
|
+
occurs before storage or Agent construction.
|
|
39
|
+
4. `agent/api/agent.rb` retains `StreamEvent` and lifecycle extension loading.
|
|
40
|
+
It has no delegate to the higher-level one-shot implementation. The
|
|
41
|
+
application entry explicitly requires the namespace extensions, factory
|
|
42
|
+
binding, and one-shot method definition after Zeitwerk setup and global
|
|
43
|
+
configuration access installation. The composition directory and binding
|
|
44
|
+
file are ignored by Zeitwerk because they wire existing constants rather
|
|
45
|
+
than introduce matching public namespaces. Agent execution never requires
|
|
46
|
+
the application entry or either composition file.
|
|
47
|
+
5. Normal application loading, first constant access, repeated requires, and
|
|
48
|
+
eager loading retain constant identity and exactly one lifecycle extension.
|
|
49
|
+
Explicit namespace loading also handles the internal unbound factory
|
|
50
|
+
contract having been loaded first. Arbitrary partial loading of framework
|
|
51
|
+
implementation files remains outside the public application-loading API.
|
|
52
|
+
|
|
53
|
+
## Compatibility and guarantees
|
|
54
|
+
|
|
55
|
+
The public constructor and one-shot API, event callback paths, stored records,
|
|
56
|
+
transaction boundaries, Agent ownership, and shutdown behavior are unchanged.
|
|
57
|
+
The existing API snapshot is not regenerated. It does not cover the `run_once`
|
|
58
|
+
singleton signature, which is checked explicitly along with real invocation,
|
|
59
|
+
creation-time context/Knowledge, and both forms of event listener.
|
|
60
|
+
|
|
61
|
+
The factory's exception propagates unchanged through the existing Agent
|
|
62
|
+
creation/ownership handling. Classified failures such as ConfigurationError
|
|
63
|
+
release the reservation; unclassified failures such as IOError retain the
|
|
64
|
+
existing fail-closed recovery-required reservation. Do not make every factory
|
|
65
|
+
failure retryable as a side effect of moving default selection. Existing
|
|
66
|
+
explicit Persistence injection works without invoking the factory.
|
|
67
|
+
|
|
68
|
+
This is source dependency inversion, not removal of the runtime construction
|
|
69
|
+
call: `Base` still invokes the bound factory when a default is needed. The
|
|
70
|
+
one-shot implementation still depends explicitly on Agent and Persistence.
|
|
71
|
+
Both selecting and invoking the one-shot composition remain above execution.
|
|
72
|
+
The private factory slot is immutable after boot; returned Persistence
|
|
73
|
+
instances remain separate, mutable storage instances.
|
|
74
|
+
|
|
75
|
+
No F1 uncertain-commit reconciliation, F4 recovery guarantee, external-effect
|
|
76
|
+
rollback, or exactly-once guarantee is added. The previous in-memory durability
|
|
77
|
+
limits remain. Runtime retains created Agents according to existing ownership
|
|
78
|
+
rules; moving `run_once` does not introduce new cleanup or lifetime behavior.
|
|
79
|
+
|
|
80
|
+
## Dependency interpretation
|
|
81
|
+
|
|
82
|
+
Remove `agent -> persistence/api` and `agent/api -> persistence/api`.
|
|
83
|
+
Add explicit composition dependencies from `runtime_composition` to Agent and
|
|
84
|
+
Persistence and from `agent/composition` to Persistence. One-shot composition
|
|
85
|
+
also calls the injected Agent definition's `create` method; namespace declarations
|
|
86
|
+
and injected calls do not count as static constant-reference edges. The
|
|
87
|
+
`Tools::Agent` call to `Agent.run_once` now targets its actual definition in
|
|
88
|
+
`agent/composition`. Treat the new Agent composition directory as B1; it is
|
|
89
|
+
not a new Ruby namespace.
|
|
90
|
+
The internal Base-to-factory reference remains within Agent's directory.
|
|
91
|
+
|
|
92
|
+
The remaining reverse dependency `agent -> workflow/execution` is the separate
|
|
93
|
+
worker-input classification change (E). This change does not resolve all
|
|
94
|
+
directory/file cycles, Storage's domain-specific repository names, or the
|
|
95
|
+
Engine FSM terminal persistence responsibilities.
|
|
96
|
+
|
|
97
|
+
## Verification
|
|
98
|
+
|
|
99
|
+
Cover fallback precedence, fresh instances and subclass construction, resets,
|
|
100
|
+
classified failure/retry and unclassified fail-closed behavior, supplied-factory
|
|
101
|
+
independence, normal/preloaded/eager
|
|
102
|
+
loading, one-shot isolation from global Persistence, argument/result/exception
|
|
103
|
+
identity, context/Knowledge forwarding, callback conflict ordering, and both
|
|
104
|
+
event listener forms. Run existing Agent ownership/event tests, default and
|
|
105
|
+
integration suites, style, API snapshot, RBS, and annotation gates. Verify
|
|
106
|
+
examples against the changed core without modifying their public call sites.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ADR-045: Worker Input Restriction Ownership
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted on the architecture refactoring branch
|
|
4
|
+
**Date**: 2026-09-21
|
|
5
|
+
**Refines**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md)
|
|
6
|
+
and [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
Tool authorization snapshots exclude framework-managed live objects from value
|
|
11
|
+
data and application behavior handles. `ToolInvocation` identified those objects
|
|
12
|
+
by enumerating 18 Agent, Workflow and execution types. The Agent-side boundary
|
|
13
|
+
therefore depended on concrete Workflow types solely to reject them as input.
|
|
14
|
+
|
|
15
|
+
Moving that list into a shared helper would retain the wrong ownership. Treating
|
|
16
|
+
all frozen objects or all callables as safe would weaken the existing boundary;
|
|
17
|
+
rejecting all opaque application objects would break ACS-11.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
1. The execution boundary owns the internal, methodless
|
|
22
|
+
`Concurrency::WorkerInputRestricted` marker in `engine/concurrency/`.
|
|
23
|
+
It declares that instances of an owning type are excluded from the existing
|
|
24
|
+
authorization worker value/behavior boundary. It contains no feature list,
|
|
25
|
+
registration mechanism, class-name matching, constant lookup or callbacks.
|
|
26
|
+
2. Each previously restricted type directly includes the marker in its own
|
|
27
|
+
definition. WorkflowContext includes it as a module so implementations and
|
|
28
|
+
their subclasses retain the restriction. Other classes pass it through
|
|
29
|
+
normal Ruby inheritance. Nested types are marked only when they were in
|
|
30
|
+
the original rejection set; marking FSMSession does not mark every nested
|
|
31
|
+
class, so its EventSink opts in separately.
|
|
32
|
+
3. `ToolInvocation` checks only `is_a?(Concurrency::WorkerInputRestricted)`.
|
|
33
|
+
Recursive Hash-key/value and Array traversal, String copying/freezing,
|
|
34
|
+
exception type/messages and behavior-handle checks remain unchanged.
|
|
35
|
+
Frozen marked instances remain restricted.
|
|
36
|
+
4. Each owner explicitly requires the small marker file. The marker can load
|
|
37
|
+
alone without Agent, Workflow, Runtime or application bootstrap. Normal,
|
|
38
|
+
preloaded, repeated and eager application loading retain marker identity
|
|
39
|
+
and do not create settings or start Runtime. The marker adds no methods to
|
|
40
|
+
public APIs; the existing API snapshot is not regenerated.
|
|
41
|
+
|
|
42
|
+
## Existing restriction inventory
|
|
43
|
+
|
|
44
|
+
| Owner | Types that include the marker |
|
|
45
|
+
|---|---|
|
|
46
|
+
| Agent execution/state | `Agent::Base`, `AgentRoot`, `AgentExecution`, `AgentInvocation`, `ToolInvocation`, `JournalProjection`, `ExecutionCoordinator` (all under Agent) |
|
|
47
|
+
| Agent capability | `Agent::Context::Capability::Base` |
|
|
48
|
+
| Workflow | `Workflow`, `WorkflowRunner`, `WorkflowContext` |
|
|
49
|
+
| Execution | `Runtime`, `TaskResult`, `EventLoop`, `FSMSession`, `FSMSession::EventSink` |
|
|
50
|
+
| Concurrency | `Concurrency::CancellationToken`, `Concurrency::OffloadPool` |
|
|
51
|
+
|
|
52
|
+
These are the same 18 root types as the previous predicate. Regression tests
|
|
53
|
+
retain this inventory to prevent an accidentally unmarked existing type from
|
|
54
|
+
becoming admissible. Future framework types subject to this boundary must opt
|
|
55
|
+
in at their owning definition and add an input-boundary test.
|
|
56
|
+
|
|
57
|
+
## Scope and compatibility
|
|
58
|
+
|
|
59
|
+
This marker is internal classification, not a public safety certification or
|
|
60
|
+
serialization SPI. It does not reject all Runnable implementations, all
|
|
61
|
+
Phronomy objects, class/module objects, or every worker command. Other operation
|
|
62
|
+
snapshots and OffloadPool APIs are unchanged.
|
|
63
|
+
|
|
64
|
+
Application-owned opaque values and behavior handles remain permitted by
|
|
65
|
+
identity. Their instance variables, callback receivers and closure captures
|
|
66
|
+
are not traversed. Applications remain responsible for worker safety under
|
|
67
|
+
the existing ACS-11 contract. The marker is not a security sandbox.
|
|
68
|
+
|
|
69
|
+
The ancestry of the 18 types intentionally gains one methodless module.
|
|
70
|
+
Public methods, constructors, value copying, events, ownership, cancellation,
|
|
71
|
+
transaction boundaries, persistence formats and exception behavior are
|
|
72
|
+
unchanged. No F1 uncertain-commit reconciliation, F4 recovery, external-effect
|
|
73
|
+
rollback or exactly-once guarantee is added.
|
|
74
|
+
|
|
75
|
+
## Dependency interpretation
|
|
76
|
+
|
|
77
|
+
The Agent-to-Workflow execution dependency disappears. Workflow types and
|
|
78
|
+
Agent types instead refer downward to an execution-boundary contract.
|
|
79
|
+
Existing Engine types refer within the execution foundation. Explicit
|
|
80
|
+
marker loading adds require edges; it does not move feature references into
|
|
81
|
+
the marker. A methodless include is still a real source dependency.
|
|
82
|
+
|
|
83
|
+
This completes the specific A-E reverse-dependency proposals. It does not
|
|
84
|
+
eliminate all directory/file cycles, Storage's domain-specific repository
|
|
85
|
+
names, or FSMSession's Workflow terminal persistence responsibilities.
|
|
86
|
+
|
|
87
|
+
## Verification
|
|
88
|
+
|
|
89
|
+
Verify the 18-type inventory for direct instances and subclasses, frozen and
|
|
90
|
+
unfrozen, root values, Hash keys/values, nested Arrays and all three behavior
|
|
91
|
+
handle names. Exercise actual authorization command capture for Workflow,
|
|
92
|
+
WorkflowRunner and WorkflowContext via context/policy/facts/requirement paths,
|
|
93
|
+
and reject a marked value returned from a facts callable. Preserve opaque
|
|
94
|
+
application data, callables and unmarked Runnable/Outcome values.
|
|
95
|
+
|
|
96
|
+
Also verify a new marked type without changing the evaluator, standalone and
|
|
97
|
+
normal/preloaded/eager loading, no Workflow type references in Agent source,
|
|
98
|
+
and the marker's absence of feature knowledge. Run the existing snapshot
|
|
99
|
+
boundary suite, full and integration tests, style, unchanged API snapshot,
|
|
100
|
+
RBS, annotations and examples against the candidate core.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# ADR-046: Agent Responsibility Layout and Shared Records
|
|
2
|
+
|
|
3
|
+
**Status**: Amended on the architecture refactoring branch
|
|
4
|
+
**SharedState amendment**: [053-shared-state-coordination-ownership](053-shared-state-coordination-ownership.md) moves the deferred multi-Agent coordination responsibility to MultiAgent. The original placement below is retained as history.
|
|
5
|
+
**Continuation amendment**: [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md) removes the remaining Recovery-to-private-control calls and shares session registration. The original extraction scope below is retained as history.
|
|
6
|
+
**Date**: 2026-09-21
|
|
7
|
+
**Refines**: [038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
|
|
8
|
+
and [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md)
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
Agent's direct directory contained 41 files with unrelated responsibilities.
|
|
13
|
+
Recovery also obtained the ordinary execution coordinator solely to invoke its
|
|
14
|
+
private record encoder and content writer. RecoverySupport mixed recovery
|
|
15
|
+
vocabulary, durable reads, and construction of live invocations.
|
|
16
|
+
|
|
17
|
+
Moving files alone does not resolve that coupling. Moving transaction decisions
|
|
18
|
+
into a shared encoder would instead give it too many responsibilities.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Apply and verify the changes in two stages: source placement first, then shared
|
|
23
|
+
record generation and restoration responsibilities.
|
|
24
|
+
|
|
25
|
+
### Source placement
|
|
26
|
+
|
|
27
|
+
Collapse the following directories with Zeitwerk, preserving existing Agent
|
|
28
|
+
constant names and the application entry point `require "phronomy"`:
|
|
29
|
+
|
|
30
|
+
| Directory | Responsibility |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `agent/lifecycle/` | Agent identity, ownership and fallback construction contract |
|
|
33
|
+
| `agent/execution/` | Invocation, phase transitions, admission and execution coordination |
|
|
34
|
+
| `agent/tool_execution/` | Tool invocation, authorization, approval and interception |
|
|
35
|
+
| `agent/context_assembly/` | Candidate selection, budgeting and Provider context materialization |
|
|
36
|
+
| `agent/journal/` | Canonical execution records and their projection |
|
|
37
|
+
| `agent/handoff/` | Agent-owned Handoff contracts, state and execution coordination |
|
|
38
|
+
| `agent/recovery/` | Durable recovery classification, resolution and live restoration |
|
|
39
|
+
|
|
40
|
+
The three files below `recovery_coordinator/` move with their owning class into
|
|
41
|
+
`recovery/recovery_coordinator/`. That directory retains its real nested Ruby
|
|
42
|
+
namespace; it is not independently collapsed. Existing API, composition,
|
|
43
|
+
context contracts, capabilities, concerns and persistence directories retain
|
|
44
|
+
their responsibilities. `base.rb`, `async_event_api.rb` and `shared_state.rb`
|
|
45
|
+
remain directly under Agent. SharedState's multi-Agent orchestration is a
|
|
46
|
+
separate future ownership decision, not evidence of generic shared data.
|
|
47
|
+
|
|
48
|
+
Update relative requires and source-reading regression guards. Do not provide
|
|
49
|
+
old-path forwarding shims: arbitrary implementation-file requires are not a
|
|
50
|
+
public partial-loading API. Existing constant identities, method signatures,
|
|
51
|
+
ancestry, lifecycle extension installation and stored class names are preserved.
|
|
52
|
+
|
|
53
|
+
### Shared record generation
|
|
54
|
+
|
|
55
|
+
`Agent::RuntimeRecordEncoder` under `execution/` receives execution facts, the
|
|
56
|
+
Agent identity, root generation, context eligibility and an existing transaction.
|
|
57
|
+
It writes content and returns JournalRecord and LLMCallRecord values. Ordinary
|
|
58
|
+
execution and recovery resolution both call it directly. It has no Agent live
|
|
59
|
+
object, coordinator, Runtime lookup, transaction opener, journal append, execution
|
|
60
|
+
save, commit/reconciliation decision or task settlement responsibility.
|
|
61
|
+
The encoder belongs to execution because it interprets Tool interception and
|
|
62
|
+
Provider settlement; the journal value/projection directory must not depend on
|
|
63
|
+
Tool execution solely to classify those facts.
|
|
64
|
+
|
|
65
|
+
Keep call sequence numbering, interception-as-success, abandoned call recording,
|
|
66
|
+
Tool message duplicate detection, context eligibility, content formats and
|
|
67
|
+
canonical-value failure messages. The caller still owns the transaction and
|
|
68
|
+
F1 outcome reconciliation. Encoding is not a pure function because it writes
|
|
69
|
+
content using that caller-owned transaction.
|
|
70
|
+
|
|
71
|
+
### Saved context and live restoration
|
|
72
|
+
|
|
73
|
+
`Agent::SavedContextReader` under `context_assembly/` reads persisted manifests,
|
|
74
|
+
materializes projections, and obtains saved Provider output/usage. Execution
|
|
75
|
+
preparation reconciliation, Handoff and Recovery consume this common reader.
|
|
76
|
+
Reading occurs in the same worker/caller preparation phase as before, outside
|
|
77
|
+
EventLoop state application.
|
|
78
|
+
|
|
79
|
+
`Agent::InvocationRestorer` under `recovery/` constructs live Chat, AgentInvocation
|
|
80
|
+
and ToolInvocation state from already-materialized facts. Suspended and resolved
|
|
81
|
+
continuations share the same invocation/chat construction sequence. Tool status,
|
|
82
|
+
approval facts, listener, invocation mode, coordination configuration, cancellation
|
|
83
|
+
and message order are preserved. It does not perform persistence reads or decide
|
|
84
|
+
which continuation to execute. RecoverySupport retains recovery metadata and
|
|
85
|
+
subject vocabulary rather than forwarding to the extracted helpers.
|
|
86
|
+
|
|
87
|
+
## Compatibility and limits
|
|
88
|
+
|
|
89
|
+
No public API or extension SPI is changed, and the existing API snapshot is not
|
|
90
|
+
regenerated. These three helpers are internal (`@api private`). Removed methods
|
|
91
|
+
on the internal RecoverySupport module and ExecutionCoordinator are not public
|
|
92
|
+
application contracts. No persistence schema, durability level, exactly-once
|
|
93
|
+
claim, cancellation rule, external effect replay rule or ownership rule changes.
|
|
94
|
+
|
|
95
|
+
ExecutionCoordinator still owns several operations and remains large. Recovery
|
|
96
|
+
still calls its private continuation/terminal entry points; this decision only
|
|
97
|
+
removes the record-generation and content-writing coupling. Moving directories
|
|
98
|
+
reveals previously internal cycles; counts across different grouping schemes are
|
|
99
|
+
not directly comparable. No claim of eliminating all Agent cycles is made.
|
|
100
|
+
|
|
101
|
+
## Verification
|
|
102
|
+
|
|
103
|
+
Verify the placement-only checkpoint before extracting helpers. Run ordinary
|
|
104
|
+
and recovery tests, F1 response-loss and I/O-boundary checks, restart/output-filter
|
|
105
|
+
coverage, full and integration suites, API byte comparison, RBS, annotations,
|
|
106
|
+
style and examples. Compare the complete encoded records and captured content
|
|
107
|
+
against the baseline under fixed timestamps and IDs, including success, structured
|
|
108
|
+
output, failure, interception, abandoned calls, duplicate Tool messages and
|
|
109
|
+
context eligibility. Verify gem packaging includes every moved/new source and
|
|
110
|
+
loads without the old implementation paths.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# ADR-047: Recovered Execution Continuation Contract
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted on the architecture refactoring branch
|
|
4
|
+
**Date**: 2026-09-21
|
|
5
|
+
**Refines**: [046-agent-responsibility-layout-and-shared-records](046-agent-responsibility-layout-and-shared-records.md),
|
|
6
|
+
[024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md)
|
|
7
|
+
and [028-preparing-recovery-replay-contract](028-preparing-recovery-replay-contract.md).
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Recovery chose a continuation from saved facts but also registered its Agent FSM
|
|
12
|
+
and reached five private ExecutionCoordinator entry points through `send`.
|
|
13
|
+
Initial execution, approval resume, framework Tool recovery and resolved-result
|
|
14
|
+
recovery independently wired parent session completion. Recovery therefore had
|
|
15
|
+
to understand the execution owner's terminal and session implementation.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
### Recovery describes the continuation; execution validates and starts it
|
|
20
|
+
|
|
21
|
+
Use the execution owner's existing `deliver_on_event_loop` boundary with two
|
|
22
|
+
internal command types:
|
|
23
|
+
|
|
24
|
+
- `RecoverPreparationCommand` identifies the installed execution and expected
|
|
25
|
+
revision, with separate execution and load completion observers.
|
|
26
|
+
- `ContinueRecoveredCommand` supplies that identity/revision, a semantic
|
|
27
|
+
continuation, the restored invocation, its materialized projection, the
|
|
28
|
+
execution observer and a failure when applicable.
|
|
29
|
+
|
|
30
|
+
Recovery still classifies saved facts, requests factual resolution, materializes
|
|
31
|
+
saved inputs off EventLoop and restores live invocations on EventLoop. It no
|
|
32
|
+
longer registers an Agent FSM, supplies FSM state/event names, owns its terminal
|
|
33
|
+
callback, or names private execution-control methods.
|
|
34
|
+
|
|
35
|
+
The execution owner validates EventLoop affinity, coordinator/Agent ownership,
|
|
36
|
+
current revision, nonterminal execution and absence of an installed FSM. It
|
|
37
|
+
also checks the continuation's saved phase and invocation identity before any
|
|
38
|
+
replacement, admission change or session dispatch. A mismatched continuation
|
|
39
|
+
must not replace or release another lifecycle's state. Stale initial preparation
|
|
40
|
+
fails both observers without releasing that newer lifecycle.
|
|
41
|
+
|
|
42
|
+
The execution owner interprets the six continuation intents:
|
|
43
|
+
|
|
44
|
+
| Intent | Execution-owned action |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| Approval rejection | Resume the saved rejected approval and its Tool sessions |
|
|
47
|
+
| Framework Tool batch | Resume the authorized framework Tool sessions |
|
|
48
|
+
| Saved framework calls | Enter ordinary Provider-result handling |
|
|
49
|
+
| Saved Provider output | Enter ordinary Provider-result/output-filter handling |
|
|
50
|
+
| Saved Tool results | Enter ordinary Tool-result/follow-up handling |
|
|
51
|
+
| Resolved failure | Enter the ordinary terminal persistence barrier |
|
|
52
|
+
|
|
53
|
+
Delivery is synchronous within the existing EventLoop turn. This is an internal
|
|
54
|
+
handover, not an additional queued turn or an Offload operation. Commands are
|
|
55
|
+
process-local controls, not persisted records or an application extension SPI.
|
|
56
|
+
The existing private terminal/preparation methods remain private.
|
|
57
|
+
|
|
58
|
+
### One owner wires Agent and Tool sessions
|
|
59
|
+
|
|
60
|
+
`Agent::ExecutionSessionRunner` owns parent session registration, resumed session
|
|
61
|
+
construction, resumed child registration and parent completion wiring. Ordinary
|
|
62
|
+
initial execution, approval resume and recovery use it. It reports completion
|
|
63
|
+
through an execution-owner-supplied callback; the owner delivers an internal
|
|
64
|
+
`SessionFinishedCommand` to itself, including the concrete FSM incarnation and
|
|
65
|
+
invocation. This preserves Handoff coordinator dispatch without making the
|
|
66
|
+
runner depend on the coordinator's concrete class or command definitions.
|
|
67
|
+
|
|
68
|
+
The coordinator retains admission, completion observers, stale-callback checks,
|
|
69
|
+
physical-quiescence waiting, terminal snapshot creation and persistence. The
|
|
70
|
+
runner performs no persistence I/O, saved-fact interpretation, retry decision or
|
|
71
|
+
caller-facing result settlement. Parent registration precedes child registration
|
|
72
|
+
as before. Its live Runtime/callback handles carry the existing worker-input
|
|
73
|
+
restriction marker.
|
|
74
|
+
|
|
75
|
+
## Compatibility and failure model
|
|
76
|
+
|
|
77
|
+
Public API snapshots, RBS contracts, stored formats, recovery subjects, approval
|
|
78
|
+
decisions and external replay eligibility remain unchanged. A restored rejected
|
|
79
|
+
approval now registers its internal execution observer at the same pre-session
|
|
80
|
+
boundary used by the other continuation intents; terminal settlement still
|
|
81
|
+
occurs only through the coordinator's existing barrier.
|
|
82
|
+
|
|
83
|
+
Under [018-durability-guarantees-and-failure-model](018-durability-guarantees-and-failure-model.md):
|
|
84
|
+
|
|
85
|
+
| Subject / property | Provider and condition | Failure / boundary | Result |
|
|
86
|
+
| --- | --- | --- | --- |
|
|
87
|
+
| Current live execution: reject stale or foreign continuation before state replacement | Execution-owner identity/revision/FSM validation on EventLoop | F2/F3; no X0 dispatch by rejected command | YES |
|
|
88
|
+
| Caller completion follows the authoritative terminal outcome | Existing coordinator terminal barrier and quiescence checks; runner reports FSM completion only | F0/F1/F3; existing external effects remain outside transaction | YES under the existing Persistence contract |
|
|
89
|
+
| Confirmed recovery state: resume the same logical execution | Recovery classification/materialization plus the execution owner; replay-safe preparation or resolved external facts are required | F1/F4; X0 may have occurred before recovery | CONDITIONAL on the existing operation-specific recovery contract |
|
|
90
|
+
| Arbitrary external-effect exactly-once execution | No new external idempotency or transaction protocol | F1/F4 across X0 | NO new guarantee |
|
|
91
|
+
|
|
92
|
+
The command boundary is process-local validation, not cross-process exclusion.
|
|
93
|
+
No broader exactly-once or automatic replay claim follows from this change.
|
|
94
|
+
|
|
95
|
+
## Verification and remaining work
|
|
96
|
+
|
|
97
|
+
Exercise rejected commands on a real EventLoop/ExecutionRegistry: wrong owner,
|
|
98
|
+
coordinator, revision, installed FSM, terminal state, invocation identity and
|
|
99
|
+
continuation phase. Verify both preparation observers and a late session
|
|
100
|
+
completion. Existing recovery, approval, F1/F4, output-filter, cancellation,
|
|
101
|
+
Handoff and full-suite tests cover valid dispatch and terminal behavior.
|
|
102
|
+
|
|
103
|
+
ExecutionCoordinator still owns preparation, causal barriers, admission and
|
|
104
|
+
terminal operations. This decision removes Recovery's private control coupling
|
|
105
|
+
and duplicated session wiring; it does not complete decomposition of that class.
|
|
106
|
+
Tool snapshot restoration's direct field coupling, SharedState ownership,
|
|
107
|
+
Storage domain responsibilities and Workflow terminal persistence remain
|
|
108
|
+
separate work.
|
|
109
|
+
|
|
110
|
+
## Tool restoration amendment (2026-09-22)
|
|
111
|
+
|
|
112
|
+
[052-tool-invocation-restoration-ownership](052-tool-invocation-restoration-ownership.md)
|
|
113
|
+
completes the direct Tool field coupling item above. ToolInvocation owns saved
|
|
114
|
+
state application; Recovery retains decoding and identity matching. The original
|
|
115
|
+
scope and historical remaining-work statement above are preserved. SharedState,
|
|
116
|
+
Storage domain responsibilities and Workflow terminal persistence remain open.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# ADR-048: Dispatch Preparation Worker Ownership
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted on the architecture refactoring branch
|
|
4
|
+
**Date**: 2026-09-22
|
|
5
|
+
**Refines**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md),
|
|
6
|
+
[042-feature-owned-execution-state](042-feature-owned-execution-state.md)
|
|
7
|
+
and [047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md).
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
ExecutionCoordinator mixed EventLoop ownership and application result delivery
|
|
12
|
+
with the Persistence operations required before Provider/Tool dispatch. Moving
|
|
13
|
+
whole methods alone would leave the Provider operation mixing control steps,
|
|
14
|
+
metadata representation, transactions, application policy and materialization.
|
|
15
|
+
Its worker also used input/result types nested under the execution owner, which
|
|
16
|
+
would create a reverse dependency after extraction.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
### The execution owner captures, submits and applies
|
|
21
|
+
|
|
22
|
+
ExecutionCoordinator keeps current Agent/invocation/FSM validation, operation
|
|
23
|
+
capture, Offload submission, Ready delivery, semantic revision/phase checks,
|
|
24
|
+
snapshot acknowledgment and physical Provider/Tool dispatch. These responsibilities
|
|
25
|
+
remain on their existing threads and in their existing order. Ordinary and
|
|
26
|
+
recovered execution keep the same owner and continuation contract.
|
|
27
|
+
|
|
28
|
+
`Agent::DispatchPreparation` owns four internal worker operations:
|
|
29
|
+
|
|
30
|
+
- `prepare_provider`: stage the next call, encode runtime records, prepare the
|
|
31
|
+
Context, commit prerequisites and materialize the confirmed input.
|
|
32
|
+
- `prepare_tools`: encode runtime records and save pending Tool recovery facts.
|
|
33
|
+
- `reconcile_provider`: read back the execution, classify the exact saved state
|
|
34
|
+
and restore input only for the confirmed intended result.
|
|
35
|
+
- `reconcile_tools`: read back and classify the exact saved state.
|
|
36
|
+
|
|
37
|
+
The worker returns values and never schedules work, advances the live Registry,
|
|
38
|
+
acknowledges invocation snapshots, starts Provider/Tool calls, or settles Tasks.
|
|
39
|
+
It does not refer back to ExecutionCoordinator or its private methods. The
|
|
40
|
+
Coordinator creates the worker with the existing Agent service context and its
|
|
41
|
+
Persistence instance. Agent hooks, ContextAssembler and coordination preparation
|
|
42
|
+
retain their existing service contracts; this is not removal of all Agent
|
|
43
|
+
references. The worker is marked `WorkerInputRestricted` because it retains
|
|
44
|
+
framework service handles. Per-operation inputs/results remain local variables.
|
|
45
|
+
|
|
46
|
+
### Preserve the operation-specific persistence boundaries
|
|
47
|
+
|
|
48
|
+
Provider preparation has two transactions with application hooks/ContextPolicy
|
|
49
|
+
between them. The first can encode content-addressed records but does not advance
|
|
50
|
+
Execution. The second rechecks the local Agent watermark, finalizes the Manifest
|
|
51
|
+
and saves the Execution. Cancellation is checked after Policy and before that
|
|
52
|
+
second transaction. Tool preparation retains its single transaction.
|
|
53
|
+
|
|
54
|
+
Only an exception in the guarded save transaction, after an intended result was
|
|
55
|
+
captured and outside the existing known-failure classes, becomes an unknown
|
|
56
|
+
outcome. Encoding failures and application Policy failures do not become an
|
|
57
|
+
uncertain execution save. Post-commit materialization failure returns the saved
|
|
58
|
+
Execution together with an error, so the owner still applies/acknowledges the
|
|
59
|
+
known committed snapshot before reporting setup failure.
|
|
60
|
+
|
|
61
|
+
Reconciliation compares both revision and complete execution contents. It returns
|
|
62
|
+
`committed` only for the intended state, `not_committed` only for the exact original
|
|
63
|
+
state, and `conflict` otherwise. Read failures propagate. It never retries the
|
|
64
|
+
write or authorizes dispatch by itself. Terminal and coordination-wait
|
|
65
|
+
reconciliation remain separate and retain their different acceptance criteria.
|
|
66
|
+
|
|
67
|
+
The watermark rule remains owned by Persistence's `assert_agent_watermark!`.
|
|
68
|
+
The worker and the remaining initial-preparation code only map captured Root
|
|
69
|
+
fields to that contract; neither implements a second version of the rule.
|
|
70
|
+
|
|
71
|
+
### Names describe the purpose; bodies describe its immediate steps
|
|
72
|
+
|
|
73
|
+
The Provider entry reads as stage, encode, prepare Context, commit, materialize.
|
|
74
|
+
Private helpers own metadata keys, record fields and value construction. The
|
|
75
|
+
Coordinator's dispatch entry reads as validate, capture, submit. Transaction
|
|
76
|
+
and rescue scopes stay explicit in the operations that own them. We do not use
|
|
77
|
+
one-line delegation chains or a generic execution framework to reduce line counts.
|
|
78
|
+
|
|
79
|
+
### Types belong to the operation boundary
|
|
80
|
+
|
|
81
|
+
DispatchPreparation owns Provider/Tool commands, reconciliation commands and
|
|
82
|
+
their results. ExecutionCoordinator retains its existing eight nested constant
|
|
83
|
+
paths as aliases to these types; Ready messages and live delivery remain in the
|
|
84
|
+
Coordinator. Its private uncertainty exception constant is also an alias.
|
|
85
|
+
|
|
86
|
+
The aliases preserve construction, members and shared type identity, but the
|
|
87
|
+
canonical Ruby class names now belong to DispatchPreparation. These are internal,
|
|
88
|
+
process-local types, not persisted records or public extension contracts. This
|
|
89
|
+
change makes no promise to preserve their former reflected names. Product API
|
|
90
|
+
snapshots, saved formats, public exception contracts and recovery eligibility
|
|
91
|
+
remain unchanged.
|
|
92
|
+
|
|
93
|
+
## Failure model and verification
|
|
94
|
+
|
|
95
|
+
Using [018-durability-guarantees-and-failure-model](018-durability-guarantees-and-failure-model.md):
|
|
96
|
+
|
|
97
|
+
| Subject / property | Provider and condition | Failure / boundary | Result |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| Current execution: apply only a matching preparation result | Existing EventLoop owner, revision and FSM checks | F2/F3; no new X0 dispatch from stale results | YES, unchanged |
|
|
100
|
+
| Provider/Tool dispatch: confirmed durable prerequisites precede physical dispatch | Operation-specific commit/readback plus owner apply; conforming Persistence is required | F0/F1; this barrier precedes the next X0 call | CONDITIONAL on confirming the intended durable state and current live ownership |
|
|
101
|
+
| Application Policy: no preparation transaction held while it executes | Explicit separation of encoding, Policy and save | F0/F3; application-defined side effects remain its responsibility | YES for the framework-owned transaction boundary |
|
|
102
|
+
| Confirmed records: restart readability and continuation | Existing Persistence and Recovery contracts | F4; replay or resolution depends on the external operation | CONDITIONAL, no broader resumption guarantee |
|
|
103
|
+
| Arbitrary external-effect exactly-once execution | No new external protocol or distributed exclusion | F1/F4 across X0 | NO new guarantee |
|
|
104
|
+
|
|
105
|
+
Existing causal-durability tests now invoke the owning worker instead of private
|
|
106
|
+
Coordinator persistence methods. Architecture guards inspect the complete worker,
|
|
107
|
+
including helpers, while keeping checks on EventLoop apply. Behavioral probes
|
|
108
|
+
exercise Policy outside transactions, post-Policy watermark/cancellation checks,
|
|
109
|
+
encoding-response loss, post-commit materialization failure, reconciliation read
|
|
110
|
+
failure, materialization failure after confirmation, and same-revision content
|
|
111
|
+
conflicts. Existing valid Agent/Tool execution, Recovery and Handoff suites cover
|
|
112
|
+
the production wiring. Public API, loading and gem-content checks remain required.
|
|
113
|
+
|
|
114
|
+
## Remaining work
|
|
115
|
+
|
|
116
|
+
Initial preparation, approval snapshot isolation, and normal/Handoff outcome
|
|
117
|
+
persistence are separate stages. The approval Hash/Mutex is not moved into this
|
|
118
|
+
worker. The existing Coordinator and Handoff terminal machinery are unchanged.
|
|
119
|
+
This stage improves responsibility and abstraction boundaries; it does not claim
|
|
120
|
+
to remove every Agent dependency cycle or finish Coordinator decomposition.
|