phronomy 0.25.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 +71 -0
- data/CONTRIBUTING.md +4 -4
- data/README.md +14 -7
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/benchmark/bench_regression.rb +1 -1
- 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 +5 -3
- 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 +29 -6
- 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 +4 -4
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/async-composition.md +205 -0
- data/docs/decisions/010-cooperative-first-concurrency.md +23 -23
- 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 +21 -12
- data/docs/getting-started.md +10 -10
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +7 -1
- 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 +205 -60
- 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 +55 -251
- 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} +20 -96
- 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} +12 -13
- 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} +22 -73
- 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 +36 -41
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +9 -13
- 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/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +2 -2
- 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} +15 -8
- 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 +29 -16
- data/lib/phronomy/engine/concurrency/operation_binding.rb +43 -0
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +4 -62
- data/lib/phronomy/engine/concurrency/result_collector.rb +99 -0
- data/lib/phronomy/engine/concurrency/result_composition.rb +145 -0
- data/lib/phronomy/engine/concurrency/subscriptions.rb +68 -0
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +160 -626
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/engine/execution.rb +229 -0
- data/lib/phronomy/engine/execution_cancellation_error.rb +14 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/engine/execution_timeout_error.rb +14 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +38 -31
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +3 -1
- data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +13 -1
- 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/timer_queue.rb +11 -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.rb → task_result.rb} +101 -42
- 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_adapter/base.rb +2 -2
- 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 +8 -8
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +36 -68
- 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/eval/scorer/llm_judge.rb +5 -3
- data/lib/phronomy/testing/fake_clock.rb +13 -9
- 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 +6 -6
- data/lib/phronomy/vector_store/async_backend.rb +5 -5
- data/lib/phronomy/vector_store/embeddings/base.rb +2 -2
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- 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} +126 -75
- 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 +8 -8
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +4 -2
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +4 -5
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/extensions.rbs +5 -5
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +19 -3
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/runtime.rbs +34 -7
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +11 -2
- data/sig/phronomy/workflow.rbs +1 -1
- metadata +236 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3138
- 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/fan_out_invocation.rb +0 -137
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +0 -118
- 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/{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/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -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.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ADR-049: Initial 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
|
+
[047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md)
|
|
8
|
+
and [048-dispatch-preparation-worker-ownership](048-dispatch-preparation-worker-ownership.md).
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
ExecutionCoordinator still owned initial durable admission, application filtering,
|
|
13
|
+
Context preparation, failure persistence and replay of saved preparation inputs.
|
|
14
|
+
Its ordinary start and preparing-recovery paths shared persistence work with
|
|
15
|
+
EventLoop admission, live state and completion delivery. Simply relocating that
|
|
16
|
+
work would leave long methods mixing representation details and control steps.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
### Preparation owns persistence; the execution owner owns live control
|
|
21
|
+
|
|
22
|
+
`Agent::InitialPreparation` owns `prepare(Command)` and `recover(RecoveryCommand)`.
|
|
23
|
+
Its Command and Result retain the fields of the former Coordinator-owned types.
|
|
24
|
+
Coordinator keeps their old constant paths as aliases. Their canonical Ruby names
|
|
25
|
+
change; these are internal process-local types, not saved or public API records.
|
|
26
|
+
|
|
27
|
+
Coordinator retains replay-eligibility capture, Runtime admission, live-owner
|
|
28
|
+
checks, Offload submission, completion messages, stale-result validation, live
|
|
29
|
+
Agent/Registry updates, session registration and Task/listener settlement.
|
|
30
|
+
Preparing recovery captures the execution, root and journal records in an explicit
|
|
31
|
+
RecoveryCommand. Result tasks and load completion handles stay on EventLoop.
|
|
32
|
+
The worker does not call back into Coordinator or initiate Provider/Tool dispatch.
|
|
33
|
+
|
|
34
|
+
The worker is marked `WorkerInputRestricted` and holds existing Agent services and
|
|
35
|
+
Persistence. It keeps operation state in local variables. This does not remove all
|
|
36
|
+
Agent service references or make arbitrary application input/config values deeply
|
|
37
|
+
immutable. Approval snapshot isolation remains a later stage.
|
|
38
|
+
|
|
39
|
+
### Preserve admission outcomes and operation-specific failure boundaries
|
|
40
|
+
|
|
41
|
+
Input extraction precedes durable admission. An extraction failure returns
|
|
42
|
+
`not_established`. During admission, durable busy returns `recovery_required`;
|
|
43
|
+
the existing known-conflict/validation exception set returns `not_established`;
|
|
44
|
+
other exceptions return `outcome_unknown`. The execution owner preserves the
|
|
45
|
+
corresponding Runtime admission release or fail-closed behavior.
|
|
46
|
+
|
|
47
|
+
Durable admission still creates Execution and advances AgentRoot together, after
|
|
48
|
+
validating Team/Subagent/Handoff reservation ownership in the same transaction.
|
|
49
|
+
Splitting those ownership checks into named helpers changes no acceptance rule.
|
|
50
|
+
|
|
51
|
+
For admitted preparation the body reads as filter, stage input, prepare Context,
|
|
52
|
+
commit, and materialize. Hooks and ContextPolicy run outside the commit
|
|
53
|
+
transaction. Cancellation is checked before filtering, before assembly and after
|
|
54
|
+
Policy. The commit revalidates the Agent watermark after Policy returns.
|
|
55
|
+
|
|
56
|
+
The failure base advances from preparing Execution to active Execution only after
|
|
57
|
+
the active commit has returned successfully. A lost active-commit response leaves
|
|
58
|
+
the old revision as the failure base. If the active commit did occur, the existing
|
|
59
|
+
failure transaction conflicts and rolls back its Journal append; the worker does
|
|
60
|
+
not return an unconfirmed terminal result or add a new retry/readback policy.
|
|
61
|
+
|
|
62
|
+
A post-commit materialization error is terminalized from the known active
|
|
63
|
+
revision. Failure persistence atomically appends audit records, saves terminal
|
|
64
|
+
Execution and advances AgentRoot. A failure in that transaction, including lost
|
|
65
|
+
response, escapes to the execution owner; it is not converted into a confirmed
|
|
66
|
+
terminal Result. This initial-preparation contract intentionally differs from
|
|
67
|
+
DispatchPreparation's operation-specific uncertain-outcome reconciliation.
|
|
68
|
+
|
|
69
|
+
`Agent::ExecutionFailure` owns the existing pure error-to-status and status-to-audit
|
|
70
|
+
kind mappings. Both initial failure persistence and the remaining terminal worker
|
|
71
|
+
use it, avoiding duplicated classification rules. It owns no transaction, live
|
|
72
|
+
state or result delivery. Normal/Handoff terminal transaction behavior is unchanged.
|
|
73
|
+
|
|
74
|
+
### Recovery consumes saved inputs without admitting another execution
|
|
75
|
+
|
|
76
|
+
The worker first checks the replayable marker, reads current input and rebuilds
|
|
77
|
+
config from the saved invocation mode, coordination services and optional canonical
|
|
78
|
+
Hash durable_context. The recovered config and durable context remain frozen.
|
|
79
|
+
Read failures or invalid durable context escape before admitted preparation;
|
|
80
|
+
they do not trigger a failure commit or a speculative retry.
|
|
81
|
+
|
|
82
|
+
Ordinary start and recovery use the same admitted-preparation operation and Result.
|
|
83
|
+
Recovery keeps the same execution identity and does not call durable admission.
|
|
84
|
+
Runtime-only approval/listener state is not reconstructed. Existing Coordinator
|
|
85
|
+
validation and recovery delivery remain authoritative.
|
|
86
|
+
|
|
87
|
+
## Guarantees and verification
|
|
88
|
+
|
|
89
|
+
Using [018-durability-guarantees-and-failure-model](018-durability-guarantees-and-failure-model.md):
|
|
90
|
+
|
|
91
|
+
| Subject / property | Provider | Failure / boundary | Result |
|
|
92
|
+
| --- | --- | --- | --- |
|
|
93
|
+
| Same-process Agent starts: fail closed for uncertain/busy admission | Existing Runtime registry plus worker outcome classification | F0/F1/F4; before framework Provider/Tool X0 | YES, unchanged |
|
|
94
|
+
| Initial preparation: stale root/revision rejection | Persistence watermark/CAS and failure transaction rollback | F1/F2/F3; before framework Provider/Tool X0 | CONDITIONAL on conforming Persistence |
|
|
95
|
+
| Failure transition: Journal, Execution and Root commit together | Existing Persistence transaction | F0/F1; atomicity does not imply response certainty | CONDITIONAL on conforming backend; no new certainty guarantee |
|
|
96
|
+
| Preparing recovery: continue the same execution | Existing replay eligibility, saved inputs and owner validation | F4; application preparation may run again | CONDITIONAL on the existing replay-safe preparation contract |
|
|
97
|
+
| Arbitrary application or external effects: exactly once | No new exclusion/idempotency protocol | F1/F4 across application-defined X0 | NO new guarantee |
|
|
98
|
+
|
|
99
|
+
Behavioral tests cover transaction ordering, pre-admission extraction failure,
|
|
100
|
+
known admission failure, lost admission/active/failure commit responses,
|
|
101
|
+
post-commit materialization failure, cancellation and watermark changes after
|
|
102
|
+
Policy, blocked input and replay input restoration/validation/read failure.
|
|
103
|
+
Architecture guards include all new worker helpers and exclude control/delivery
|
|
104
|
+
dependencies. Existing normal execution, Team/Subagent/Handoff and recovery suites
|
|
105
|
+
cover the owner wiring and unchanged coordination checks.
|
|
106
|
+
|
|
107
|
+
## Remaining work
|
|
108
|
+
|
|
109
|
+
Approval snapshot isolation and approval persistence, followed by normal/Handoff
|
|
110
|
+
outcome worker extraction, remain separate stages. This stage does not finish
|
|
111
|
+
Coordinator decomposition or remove all Agent dependency cycles.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# ADR-050: Approval Resume Snapshot and Commit 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
|
+
[047-recovered-execution-continuation-contract](047-recovered-execution-continuation-contract.md)
|
|
7
|
+
and [049-initial-preparation-worker-ownership](049-initial-preparation-worker-ownership.md).
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Approval resume captured a Tool batch into a Coordinator Hash keyed by execution
|
|
12
|
+
ID before validating the current approval request. A worker later removed that
|
|
13
|
+
entry under a Mutex. Two queued requests could overwrite or consume each other's
|
|
14
|
+
snapshot even though the worker commands belonged to distinct operations.
|
|
15
|
+
The outer frozen Array also shared mutable String values with the invocation.
|
|
16
|
+
The command did not completely describe the recovery facts it would persist.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
### Validate and capture on EventLoop
|
|
21
|
+
|
|
22
|
+
ExecutionCoordinator validates the live Agent owner, suspended Execution and
|
|
23
|
+
approval request ID before capturing the invocation's canonical Tool snapshot.
|
|
24
|
+
It copies and recursively freezes that value tree with `Values::Immutable.copy`
|
|
25
|
+
and stores it directly in the operation's `tool_batch_snapshot` field. An absent
|
|
26
|
+
invocation gives nil; a present invocation with no children gives a frozen empty
|
|
27
|
+
Array. No shared approval snapshot Hash or Mutex remains in either owner.
|
|
28
|
+
|
|
29
|
+
This applies to canonical recovery facts only. Arbitrary application objects in
|
|
30
|
+
config, listener or callback state do not acquire a new deep-copy contract.
|
|
31
|
+
The general RecoverySupport snapshot builder keeps its existing contract.
|
|
32
|
+
|
|
33
|
+
Snapshot isolation was implemented and behaviorally verified before extracting
|
|
34
|
+
the persistence operation, so the concurrency fix can be reviewed independently
|
|
35
|
+
of the ownership change.
|
|
36
|
+
|
|
37
|
+
### Persist the captured decision in an operation worker
|
|
38
|
+
|
|
39
|
+
`Agent::ApprovalResumeCommit#commit` reads as validate the approval target, stage
|
|
40
|
+
the captured recovery facts, then persist the decision. Its helpers own record
|
|
41
|
+
fields, metadata representation and repository calls. It holds only the Agent ID
|
|
42
|
+
and Persistence; all operation state is local. It is `WorkerInputRestricted`.
|
|
43
|
+
|
|
44
|
+
The existing transaction still writes approval decision Content, adds the
|
|
45
|
+
approval_decided record to Execution working records, saves active/resuming
|
|
46
|
+
Execution with its expected revision and advances AgentRoot with its expected
|
|
47
|
+
revision. Staging metadata preserves the original execution revision so the
|
|
48
|
+
durable transition advances it exactly once. A nil snapshot preserves existing
|
|
49
|
+
metadata; an empty snapshot replaces the previous Tool batch.
|
|
50
|
+
|
|
51
|
+
Coordinator retains Runtime admission, Offload submission, completion messages,
|
|
52
|
+
late-result validation, live-state replacement, completion waiters, tracing and
|
|
53
|
+
SessionRunner resumption. Rejection before Offload submission restores suspended
|
|
54
|
+
admission. A commit error keeps admission recovery_required. A late result for
|
|
55
|
+
an advanced or cancelled Execution fails only the approval observer and does not
|
|
56
|
+
start a session. Existing duplicate-approval acceptance and CAS rules are unchanged.
|
|
57
|
+
|
|
58
|
+
The worker does not read back uncertain results, retry a transaction, contact
|
|
59
|
+
Provider/Tool adapters or invoke application callbacks. A lost commit response
|
|
60
|
+
escapes as an error; the owner follows the existing recovery-required path.
|
|
61
|
+
Persistence atomicity must not be presented as certainty about that response.
|
|
62
|
+
|
|
63
|
+
### Internal type compatibility
|
|
64
|
+
|
|
65
|
+
Command and Result are owned by ApprovalResumeCommit. Coordinator retains
|
|
66
|
+
`ResumeCommitCommand` and `ResumeCommitResult` as aliases to those exact classes.
|
|
67
|
+
Their canonical Ruby names change to `ApprovalResumeCommit::Command` and
|
|
68
|
+
`ApprovalResumeCommit::Result`. Command appends the required internal field
|
|
69
|
+
`tool_batch_snapshot`; Result retains its existing members. Internal positional
|
|
70
|
+
construction/reflection is consequently not unchanged. These types are neither
|
|
71
|
+
public API nor durable records. Saved schema and public approval API stay unchanged.
|
|
72
|
+
|
|
73
|
+
## Guarantees and verification
|
|
74
|
+
|
|
75
|
+
Using [ADR-018](018-durability-guarantees-and-failure-model.md):
|
|
76
|
+
|
|
77
|
+
| Subject / property | Provider | Failure / boundary | Result |
|
|
78
|
+
| --- | --- | --- | --- |
|
|
79
|
+
| Queued approval operation retains its captured facts | EventLoop validation and independent immutable Command values | F0/F2/F3; before Provider/Tool X0 | YES for canonical Tool snapshot values |
|
|
80
|
+
| Approval transition is atomic (G5), stale writes are rejected (G8) | Existing Persistence transaction and Execution/Root CAS | F2; no framework X0 in this worker | CONDITIONAL on conforming backend |
|
|
81
|
+
| Unknown approval commit is not treated as confirmed resumption | Worker error propagation and owner recovery_required admission | F1; no new worker X0 | YES; no new commit certainty or readback guarantee |
|
|
82
|
+
| Restored approval waiting continues the same execution (G4) | Existing recovery reconstruction and validated resume path | F4; later Tool work may cross X0 | CONDITIONAL on existing recovery/integration contracts |
|
|
83
|
+
| Cross-process exclusion (G7), external duplicate prevention (G9), exactly once (G10) | No new protocol supplied by this change | F1/F2/F4 across X0 | NO new guarantee |
|
|
84
|
+
|
|
85
|
+
Tests cover mutation after capture, reversed worker order, stale and invalid
|
|
86
|
+
requests, rejected submission, empty/absent batches, successive approvals,
|
|
87
|
+
approval/denial persistence, Execution and Root conflicts, transaction response
|
|
88
|
+
loss and stale results after cancellation or a newer approval. Existing recovery
|
|
89
|
+
integration tests restore approval waiting and resume both approval and denial.
|
|
90
|
+
The unit fault injections are not a hard-process-loss or real external-service test.
|
|
91
|
+
Architecture guards inspect the worker helpers for live-control dependencies.
|
|
92
|
+
|
|
93
|
+
## Remaining work
|
|
94
|
+
|
|
95
|
+
Normal terminal persistence and Handoff terminal persistence remain coupled in
|
|
96
|
+
ExecutionCoordinator and its subclass. They are the next extraction stage and
|
|
97
|
+
must be designed together. This change does not complete Coordinator decomposition
|
|
98
|
+
or eliminate Agent dependency cycles.
|