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,86 @@
|
|
|
1
|
+
# ADR-036: Context Contract Ownership
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch as C1.
|
|
6
|
+
|
|
7
|
+
This decision clarifies the ownership of existing Context contracts. The
|
|
8
|
+
Journal/Manifest authority model in ADR-012 and the Knowledge model in ADR-013
|
|
9
|
+
remain in force. It does not change either model or introduce a new public API.
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
`ContextPolicies::Default` depends on `ContextPolicy` and `ContextPolicyInput`.
|
|
14
|
+
Agent execution also consumes these contracts and selects a concrete policy.
|
|
15
|
+
Keeping the contracts beside execution classes directly under `agent/` makes
|
|
16
|
+
the policy directory appear to depend back on Agent execution.
|
|
17
|
+
|
|
18
|
+
The shared vocabulary is broader than a policy base class. Policy inputs refer
|
|
19
|
+
to the previous `LLMInputManifest`; plans and generated items have common
|
|
20
|
+
validation rules; `before_llm_input` hooks exchange typed input and result
|
|
21
|
+
values. Moving only a base class would leave those shared contracts split
|
|
22
|
+
across responsibility groups.
|
|
23
|
+
|
|
24
|
+
The Manifest constructs, validates, and converts record values. It does not
|
|
25
|
+
open transactions or write records to a backend. Its representation therefore
|
|
26
|
+
belongs with the Context contracts used by both producers and consumers.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
1. Move these seven unchanged files into `lib/phronomy/agent/context_contract/`:
|
|
31
|
+
- `context_policy.rb`
|
|
32
|
+
- `context_policy_input.rb`
|
|
33
|
+
- `context_plan.rb`
|
|
34
|
+
- `context_plan_validator.rb`
|
|
35
|
+
- `llm_input_manifest.rb`
|
|
36
|
+
- `llm_input_build_context.rb`
|
|
37
|
+
- `llm_input_patch.rb`
|
|
38
|
+
2. Collapse the directory with Zeitwerk, following the existing Engine layout
|
|
39
|
+
mechanism. Keep the existing `Phronomy::Agent::*` definitions and canonical
|
|
40
|
+
names, including nested input and Manifest value types. Add no aliases or
|
|
41
|
+
`Phronomy::Agent::ContextContract` namespace.
|
|
42
|
+
3. Keep `ContextPolicies::Default` with the concrete policy implementations.
|
|
43
|
+
Keep Context assembly, input building, candidate resolution, Runtime
|
|
44
|
+
integration, hook invocation, and persistence transactions with their
|
|
45
|
+
existing execution-side owners.
|
|
46
|
+
4. Preserve policy instance binding, the `call(input)` protocol, hook input and
|
|
47
|
+
result types, and the Manifest version and encoded representation. Add no
|
|
48
|
+
descriptor, registry, application registration step, or data migration.
|
|
49
|
+
5. Treat the contracts and concrete policies as separate responsibility groups.
|
|
50
|
+
They may share a horizontal band in a dependency view. Directory nesting and
|
|
51
|
+
Ruby namespace nesting do not determine architectural dependency direction.
|
|
52
|
+
|
|
53
|
+
## Consequences and limits
|
|
54
|
+
|
|
55
|
+
Policies and hooks reference the contract definitions in their shared group,
|
|
56
|
+
while Agent execution composes and consumes them. The contract files have no
|
|
57
|
+
static dependency on Agent execution or MultiAgent implementation classes.
|
|
58
|
+
Their dependencies on immutable values, token estimation, canonical JSON,
|
|
59
|
+
shared errors, and Storage serialization errors remain unchanged.
|
|
60
|
+
|
|
61
|
+
The source bodies and mapped file-level dependency graph are preserved. A new
|
|
62
|
+
directory group exposes edges previously internal to `agent/`, so aggregate
|
|
63
|
+
directory edge counts and strongly connected component sizes can increase.
|
|
64
|
+
This is not evidence that the existing file-level cycles were resolved.
|
|
65
|
+
|
|
66
|
+
The contracts still contain Handoff-related categories and metadata vocabulary.
|
|
67
|
+
That semantic coupling belongs to the subsequent Handoff responsibility review.
|
|
68
|
+
The mixed root directory and execution cycles also require separate work.
|
|
69
|
+
|
|
70
|
+
Applications continue to `require "phronomy"` and use the same documented
|
|
71
|
+
constants. Individual implementation paths that previously lived directly
|
|
72
|
+
under `agent/` have moved; no forwarding files are retained at those paths.
|
|
73
|
+
|
|
74
|
+
## Verification obligations
|
|
75
|
+
|
|
76
|
+
Verify ordinary lazy loading and eager loading, canonical constant names,
|
|
77
|
+
independently accessing each contract, custom Policy binding, Plan validation,
|
|
78
|
+
hook input/result handling, Manifest serialization and restoration, Context
|
|
79
|
+
assembly, and existing migration/persistence integration.
|
|
80
|
+
|
|
81
|
+
Keep the public API snapshot and RBS unchanged. Match each moved file to its
|
|
82
|
+
source when comparing dependencies. Record the removal of direct policy/hook
|
|
83
|
+
references to the execution directory without presenting aggregate graph
|
|
84
|
+
changes as the elimination of all cycles.
|
|
85
|
+
|
|
86
|
+
The implementation delivery records its test results separately.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# ADR-037: Common Definition Ownership
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch.
|
|
6
|
+
|
|
7
|
+
The initial extraction scope is amended by
|
|
8
|
+
[038-responsibility-based-source-layout](038-responsibility-based-source-layout.md),
|
|
9
|
+
which adds reviewed general definitions and relocates feature-owned definitions.
|
|
10
|
+
The common-ownership rule and public Error contract below remain in force.
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
`lib/phronomy.rb` combines the application loading entry point with shared
|
|
15
|
+
exception definitions and global configuration. Internal consumers of the
|
|
16
|
+
base `Phronomy::Error` therefore refer back to the entry point even though
|
|
17
|
+
they do not explicitly require it.
|
|
18
|
+
|
|
19
|
+
The shared base exception has no dependency on feature implementations. It
|
|
20
|
+
belongs to a common definition group. An `errors/` group would not convey the
|
|
21
|
+
distinction between general definitions and feature-owned exception contracts.
|
|
22
|
+
The same distinction applies to definitions other than exceptions.
|
|
23
|
+
|
|
24
|
+
## Decision
|
|
25
|
+
|
|
26
|
+
1. Use `lib/phronomy/common/` for general definitions shared across the
|
|
27
|
+
framework that do not belong to a particular feature. These definitions
|
|
28
|
+
must not depend on concrete Agent, Workflow, Runtime, or other feature
|
|
29
|
+
implementations. Multiple consumers alone do not establish common ownership.
|
|
30
|
+
2. Move only the base `Phronomy::Error < StandardError` definition from the
|
|
31
|
+
entry point to `common/error.rb`. Name files after their responsibility;
|
|
32
|
+
do not introduce an unbounded `common/common.rb` collection.
|
|
33
|
+
3. Collapse `common/` with Zeitwerk to retain `Phronomy::Error` as its canonical
|
|
34
|
+
name. Do not introduce a second class, alias, or `Phronomy::Common` namespace.
|
|
35
|
+
Ordinary application loading remains `require "phronomy"`.
|
|
36
|
+
4. Keep existing subclass definitions, inheritance, constructors, and rescue
|
|
37
|
+
behavior unchanged. Inheriting from `Phronomy::Error` does not require a
|
|
38
|
+
feature-owned exception to live in `common/`.
|
|
39
|
+
5. Keep the common base independent of entry-point loading. Reading its
|
|
40
|
+
implementation file in isolation must not initialize the full framework.
|
|
41
|
+
This is an internal architecture check, not a new public partial-loading API.
|
|
42
|
+
|
|
43
|
+
## Consequences and limits
|
|
44
|
+
|
|
45
|
+
Internal references to the shared base resolve to its common definition rather
|
|
46
|
+
than to the loading entry point. The application-facing constant and signatures
|
|
47
|
+
remain unchanged. No new explicit require of the application entry point is
|
|
48
|
+
introduced.
|
|
49
|
+
|
|
50
|
+
Other exceptions still defined in the entry point, global configuration, and
|
|
51
|
+
the mixed root implementation group need separate ownership reviews. This
|
|
52
|
+
single extraction does not establish that all file or directory cycles have
|
|
53
|
+
been eliminated.
|
|
54
|
+
|
|
55
|
+
## Verification obligations
|
|
56
|
+
|
|
57
|
+
Verify isolated base-definition loading, ordinary application loading, and eager
|
|
58
|
+
loading. Check ordinary and preloaded-base initialization orders, canonical
|
|
59
|
+
constant identity, representative subclass hierarchies, and catching a domain
|
|
60
|
+
exception with `rescue Phronomy::Error`. Preserve the public API snapshot and
|
|
61
|
+
RBS signatures. Compare dependency targets against the applied baseline and
|
|
62
|
+
report remaining entry-point references separately.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# ADR-038: Responsibility-Based Source Layout
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch; configuration/runtime lifecycle
|
|
6
|
+
ownership amended by
|
|
7
|
+
[039-runtime-configuration-lifecycle-ownership](039-runtime-configuration-lifecycle-ownership.md).
|
|
8
|
+
Concrete-default ownership is subsequently amended by
|
|
9
|
+
[040-configuration-default-composition](040-configuration-default-composition.md).
|
|
10
|
+
Agent default selection and one-shot placement are subsequently refined by
|
|
11
|
+
[044-agent-default-and-one-shot-composition](044-agent-default-and-one-shot-composition.md).
|
|
12
|
+
The Workflow recovery prepend requirement is subsequently replaced by direct
|
|
13
|
+
Runner ownership in
|
|
14
|
+
[054-workflow-terminal-save-single-owner](054-workflow-terminal-save-single-owner.md).
|
|
15
|
+
|
|
16
|
+
Amends the extraction scope of
|
|
17
|
+
[037-common-definition-ownership](037-common-definition-ownership.md).
|
|
18
|
+
Its common-ownership rule and public Error contract remain unchanged.
|
|
19
|
+
|
|
20
|
+
## Context
|
|
21
|
+
|
|
22
|
+
The application loading entry point and the direct `lib/phronomy/` directory
|
|
23
|
+
were distinct sources of mixed ownership. After the base Error extraction,
|
|
24
|
+
the entry point still defined twenty feature/shared exceptions and global
|
|
25
|
+
configuration operations. Thirty-nine direct Ruby files still included
|
|
26
|
+
Workflow implementation, execution contracts, recovery, configuration, and
|
|
27
|
+
general values alongside namespace declarations.
|
|
28
|
+
|
|
29
|
+
A directory's location must describe its responsibility. A public constant
|
|
30
|
+
under `Phronomy` does not require its implementation file to remain directly
|
|
31
|
+
under `lib/phronomy/`. Conversely, a shared consumer list does not make a
|
|
32
|
+
feature contract a general common definition.
|
|
33
|
+
|
|
34
|
+
## Decision
|
|
35
|
+
|
|
36
|
+
1. Reserve `lib/phronomy.rb` for application loading and explicit initialization
|
|
37
|
+
wiring. Production implementation files under `lib/phronomy/` must not require that entry
|
|
38
|
+
point. Use Zeitwerk for canonical constant loading and precise requires for
|
|
39
|
+
dependencies and initialization side effects.
|
|
40
|
+
The existing `testing/persistence_contract.rb` is a separately documented
|
|
41
|
+
public entry point for external backend authors. It remains allowed to load
|
|
42
|
+
Phronomy and RSpec, and remains excluded from production automatic loading.
|
|
43
|
+
This single named exception does not exempt other files under `testing/`.
|
|
44
|
+
2. Allow only these direct Ruby files under `lib/phronomy/`:
|
|
45
|
+
|
|
46
|
+
| File | Reason |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `version.rb` | Bundler/gem metadata convention and Zeitwerk GemInflector's version-file convention |
|
|
49
|
+
| `llm_adapter.rb` | Explicit namespace and extension-SPI documentation |
|
|
50
|
+
| `tool.rb` | Explicit public Tool authoring namespace and documentation |
|
|
51
|
+
| `vector_store.rb` | Explicit namespace and backend-SPI documentation |
|
|
52
|
+
| `output_parser.rb` | Explicit parser namespace declaration |
|
|
53
|
+
| `testing.rb` | Explicit test-support namespace declaration |
|
|
54
|
+
| `filter.rb` | Existing Filter convenience loading entry, without feature implementation |
|
|
55
|
+
| `tracing.rb` | Existing Tracing convenience loading entry, without feature implementation |
|
|
56
|
+
|
|
57
|
+
Namespace files must not accumulate method bodies or concrete classes.
|
|
58
|
+
RubyGems does not mandate this entire allowlist. Keeping these small files
|
|
59
|
+
is an explicit Phronomy design/compatibility choice. New exceptions to the
|
|
60
|
+
allowlist require an ownership reason and an amendment to this decision.
|
|
61
|
+
3. Extend `common/` with `CanonicalJSON`, `ConfigurationError`, and
|
|
62
|
+
`Values::Immutable`. Preserve their canonical names, JSON representation,
|
|
63
|
+
copy/freeze behavior, and exception hierarchy. The three equivalent private
|
|
64
|
+
Agent copy helpers delegate to `Values::Immutable.copy`; domain-specific
|
|
65
|
+
command copying and Workflow copying keep their different contracts.
|
|
66
|
+
4. Move the remaining implementation and exception definitions to their owners:
|
|
67
|
+
|
|
68
|
+
| Directory | Responsibility |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `engine/` | EventLoop/FSM communication, synchronous callback constraints, execution composition, cancellation/deadlines, runtime diagnostics, invocation and runnable execution contracts |
|
|
71
|
+
| `recovery/` | Shared recovery vocabulary and execution rehydration requirement; no concrete Agent/Workflow orchestration |
|
|
72
|
+
| `workflow/execution/` | Workflow DSL, context ownership, runner, and terminal persistence recovery |
|
|
73
|
+
| `agent/api/` | Agent namespace/event loading and lifecycle extension installation |
|
|
74
|
+
| `agent/composition/` | One-shot Agent/Persistence composition; method definition loaded by the application entry (ADR-044) |
|
|
75
|
+
| `agent/lifecycle_contract/` | Agent ownership, handoff, and application stream-delivery exceptions |
|
|
76
|
+
| `configuration/` | Global configuration values, defaults, accessors, replacement, and scoped overrides |
|
|
77
|
+
| `runtime_composition/` | Application Runtime/configuration lifecycle coordination and concrete default bindings (ADR-039/040/044) |
|
|
78
|
+
| `llm_contract/` | Token usage, context-budget failures, and LLM call-boundary failures |
|
|
79
|
+
| `llm_adapter/ruby_llm_patches.rb` | RubyLLM version-guarded compatibility patch |
|
|
80
|
+
| `persistence/api/` | Public Persistence facade and repository composition |
|
|
81
|
+
| `generation/` | GeneratorVerifier pipeline and its confidence failure |
|
|
82
|
+
| `tool/contract/`, `filter/contract/`, `output_parser/contract/` | Feature-owned shared exceptions |
|
|
83
|
+
|
|
84
|
+
5. Preserve all existing Ruby constant names, class/module kinds, inheritance,
|
|
85
|
+
constructors, method visibility, public signatures, and persisted formats.
|
|
86
|
+
Do not add constant aliases or compatibility files at retired implementation
|
|
87
|
+
paths. Ordinary application loading remains `require "phronomy"`; arbitrary
|
|
88
|
+
internal file paths are not a new public partial-loading API.
|
|
89
|
+
6. Use existing Zeitwerk `collapse` and `push_dir(namespace: Phronomy)` facilities.
|
|
90
|
+
The named nested roots are independent of their enclosing namespace. This
|
|
91
|
+
retains top-level `Phronomy::WorkflowContext` beside existing nested
|
|
92
|
+
`Phronomy::Workflow::Persistence` without collapsing the entire Workflow
|
|
93
|
+
namespace. Load `version.rb` first to establish the non-reloadable Phronomy
|
|
94
|
+
root namespace. Do not depend on facilities introduced after Zeitwerk 2.6.
|
|
95
|
+
7. Preserve explicit lifecycle initialization. Workflow recovery must be
|
|
96
|
+
prepended during ordinary application loading. Agent event/recovery
|
|
97
|
+
extensions must be installed when Agent is loaded. Keep external patching
|
|
98
|
+
and global namespace reopening files outside automatic name inference.
|
|
99
|
+
Production eager loading must continue to exclude RSpec conformance support.
|
|
100
|
+
|
|
101
|
+
## Dependency interpretation and limits
|
|
102
|
+
|
|
103
|
+
`Event` is an Engine communication contract, not a general-purpose common
|
|
104
|
+
value. The `InvalidAsync*` family belongs to synchronous FSM callback rules:
|
|
105
|
+
Engine and Agent use the entry-action failure as well as Workflow. Moving that
|
|
106
|
+
contract into Workflow implementation would introduce the wrong dependency.
|
|
107
|
+
|
|
108
|
+
`ExecutionRehydrationRequiredError` belongs to recovery even though its name
|
|
109
|
+
starts with Execution. It is not the timeout/cancellation outcome of fan-out
|
|
110
|
+
composition. `ConfigurationError` is common; `Configuration` is not. Default
|
|
111
|
+
configuration still constructs a concrete LLM adapter and tracer. This change
|
|
112
|
+
groups that existing composition but does not make it implementation-neutral.
|
|
113
|
+
|
|
114
|
+
Directory relocation does not establish that every remaining dependency is
|
|
115
|
+
downward or that all cycles disappear. Preserve and report actual references.
|
|
116
|
+
An analyzer must resolve namespace operations to their defining implementation
|
|
117
|
+
instead of treating the shortest namespace reopening as the owner of every
|
|
118
|
+
method. Do not mistake a loading entry point for the implementation it loads.
|
|
119
|
+
|
|
120
|
+
## Verification obligations
|
|
121
|
+
|
|
122
|
+
- Check the direct-root allowlist and forbid internal entry-point requires.
|
|
123
|
+
- Verify normal and eager loading, independent common/exception loading,
|
|
124
|
+
preloaded common definitions, nested-namespace access orders, and preserved
|
|
125
|
+
lifecycle extension installation.
|
|
126
|
+
- Exercise the supported Zeitwerk lower bound and the current locked version.
|
|
127
|
+
- Preserve the API snapshot and RBS without regenerating away a difference.
|
|
128
|
+
- Run existing execution, Workflow, Agent, persistence/recovery, integration,
|
|
129
|
+
configuration, and serialization tests; run the repository style/type gates.
|
|
130
|
+
- Compare dependencies against the applied baseline with the same analyzer.
|
|
131
|
+
Mark candidate results as unapplied and identify the exact source tree.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ADR-039: Runtime and Configuration Lifecycle Ownership
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch.
|
|
6
|
+
|
|
7
|
+
Concrete-default ownership in decision 1 is amended by
|
|
8
|
+
[040-configuration-default-composition](040-configuration-default-composition.md).
|
|
9
|
+
The reset ownership and algorithm below remain current.
|
|
10
|
+
|
|
11
|
+
Amends the configuration ownership in
|
|
12
|
+
[038-responsibility-based-source-layout](038-responsibility-based-source-layout.md).
|
|
13
|
+
Its loading rules, direct-root allowlist, and compatibility requirements remain
|
|
14
|
+
in force.
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
|
|
18
|
+
`Phronomy.reset_runtime!` shuts down the default Runtime and replaces global
|
|
19
|
+
configuration only after successful Runtime cleanup. Its configuration grace
|
|
20
|
+
period preservation is application lifecycle coordination between two owners.
|
|
21
|
+
Placing this operation in `configuration/global_configuration.rb` made ordinary
|
|
22
|
+
configuration readers point to a file that also controlled Runtime shutdown.
|
|
23
|
+
|
|
24
|
+
Engine reads configuration for pool sizes, logging, and shutdown defaults.
|
|
25
|
+
Configuration access must not also own the reverse Runtime lifecycle operation.
|
|
26
|
+
Moving that operation into `common/` would give common definitions an Engine
|
|
27
|
+
dependency. Moving it into an Engine primitive would mix global configuration
|
|
28
|
+
replacement with Runtime execution mechanics.
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
1. Keep configuration values, concrete defaults, global access, replacement,
|
|
33
|
+
and scoped overrides in `configuration/`.
|
|
34
|
+
2. Place `Phronomy.reset_runtime!` in
|
|
35
|
+
`runtime_composition/global_runtime.rb`. This responsibility group coordinates
|
|
36
|
+
Runtime and configuration. Engine and configuration implementation files
|
|
37
|
+
must not require the composition file or the application entry point.
|
|
38
|
+
3. Keep this namespace-reopening file outside Zeitwerk name inference. Load it
|
|
39
|
+
explicitly from `lib/phronomy.rb` after global configuration access is
|
|
40
|
+
available. Do not introduce a `Phronomy::RuntimeComposition` constant,
|
|
41
|
+
compatibility alias, registration hook, or alternate reset API.
|
|
42
|
+
4. Preserve the reset method body and signature. In particular, Runtime cleanup
|
|
43
|
+
happens before configuration replacement; an error from cleanup propagates
|
|
44
|
+
without resetting configuration. Preserve the timeout default, previous
|
|
45
|
+
grace value handling, laziness of the default Runtime, and return value.
|
|
46
|
+
|
|
47
|
+
## Dependency interpretation and remaining work
|
|
48
|
+
|
|
49
|
+
The direct configuration-to-Runtime reference moves to the composition owner.
|
|
50
|
+
Ordinary configuration readers still resolve to the configuration accessor
|
|
51
|
+
file. Reference analysis must resolve each `Phronomy` operation to its actual
|
|
52
|
+
method definition, not assign every operation to an arbitrary namespace file.
|
|
53
|
+
|
|
54
|
+
This step does not remove the separate cycle formed by concrete defaults:
|
|
55
|
+
`Configuration` constructs `LLMAdapter::RubyLLM`, its base supplies asynchronous
|
|
56
|
+
execution through `Runtime`, and Runtime reads global configuration. The
|
|
57
|
+
default tracer is also concrete. Preserving `Configuration.new` behavior while
|
|
58
|
+
separating default construction requires a separate design decision. Do not
|
|
59
|
+
hide these references with dynamic constant lookup or claim that the entire
|
|
60
|
+
configuration/Engine cycle has disappeared.
|
|
61
|
+
|
|
62
|
+
## Verification obligations
|
|
63
|
+
|
|
64
|
+
- Guard that configuration files can be loaded without loading Engine or
|
|
65
|
+
Runtime composition, exposing the reset operation, or directly referencing
|
|
66
|
+
Runtime. This loading check does not promise a new public partial-loading API.
|
|
67
|
+
- Verify ordinary application loading exposes the existing reset API and eager
|
|
68
|
+
loading creates no synthetic composition namespace.
|
|
69
|
+
- Compare production method definitions against the preceding layout candidate;
|
|
70
|
+
this extraction changes no method bodies.
|
|
71
|
+
- Run the existing configuration, Runtime lifecycle, shutdown participant,
|
|
72
|
+
compatibility, and integration tests and repository style/type gates.
|
|
73
|
+
- Report remaining cycles with the same analyzer and distinguish the unapplied
|
|
74
|
+
candidate source tree from the applied baseline commit.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# ADR-040: Configuration Default Composition
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch.
|
|
6
|
+
|
|
7
|
+
Amends concrete-default ownership in
|
|
8
|
+
[038-responsibility-based-source-layout](038-responsibility-based-source-layout.md)
|
|
9
|
+
and [039-runtime-configuration-lifecycle-ownership](039-runtime-configuration-lifecycle-ownership.md).
|
|
10
|
+
Their loading, public compatibility, and Runtime reset rules remain in force.
|
|
11
|
+
The provider-call and framework-owned async boundary in
|
|
12
|
+
[027-llm-adapter-provider-boundary](027-llm-adapter-provider-boundary.md) is unchanged.
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
`Configuration` holds settings read by Engine and other framework components.
|
|
17
|
+
Its constructor also selected and instantiated `LLMAdapter::RubyLLM` and
|
|
18
|
+
`Tracing::NullTracer`. The adapter base uses Runtime for default asynchronous
|
|
19
|
+
execution, while Runtime reads configuration. Tracing also reads configuration.
|
|
20
|
+
Concrete default selection therefore created reverse implementation references
|
|
21
|
+
from settings to their consumers.
|
|
22
|
+
|
|
23
|
+
Simply moving the constructor into another file that reopens `Configuration`
|
|
24
|
+
would not separate these responsibilities. Changing `Configuration.new` to
|
|
25
|
+
produce unset components, replacing it with a different application factory,
|
|
26
|
+
or sharing prebuilt defaults would change existing behavior.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
1. `configuration/` owns setting values, validation, global access, replacement,
|
|
31
|
+
and scoped overrides. It must not select concrete adapters, tracers, or Runtime
|
|
32
|
+
implementations. Scalar defaults remain in `Configuration#initialize`.
|
|
33
|
+
2. `runtime_composition/configuration_defaults.rb` selects `RubyLLM` and
|
|
34
|
+
`NullTracer` with explicit constant references in zero-argument factories.
|
|
35
|
+
It binds these factories through `Configuration.install_default_factories`
|
|
36
|
+
during application loading. This method is `@api private`; it is a narrow
|
|
37
|
+
internal boot operation, not a configurable provider registry or extension SPI.
|
|
38
|
+
3. `Configuration` owns the factory slots it consumes. Binding freezes those
|
|
39
|
+
slots once. The inherited constructor uses the same binding for subclasses,
|
|
40
|
+
avoiding class-instance-variable inheritance differences. The constructor
|
|
41
|
+
invokes each factory for each configuration; it does not cache instances or
|
|
42
|
+
lazily replace attributes on access. Applications still use `tracer=` and
|
|
43
|
+
`llm_adapter=` to supply their instances.
|
|
44
|
+
4. `lib/phronomy.rb` explicitly loads the binding file after Zeitwerk setup and
|
|
45
|
+
before exposing global configuration access or loading lifecycle extensions.
|
|
46
|
+
The binding file is excluded from automatic namespace inference, as is the
|
|
47
|
+
existing Runtime coordination file. It introduces no composition namespace.
|
|
48
|
+
Merely binding factories does not create global configuration, instantiate
|
|
49
|
+
components, or start Runtime. Arbitrary internal file loading remains outside
|
|
50
|
+
the public application-loading contract.
|
|
51
|
+
5. Preserve the zero-argument constructor, fresh default instances, subclass
|
|
52
|
+
behavior, shallow scoped restoration, explicit overrides, Runtime laziness,
|
|
53
|
+
reset ordering, exception propagation, and previous grace-period handling.
|
|
54
|
+
No adapter SPI, async method, provider behavior, transport policy, public
|
|
55
|
+
signature, or persisted format changes in this step.
|
|
56
|
+
|
|
57
|
+
The binding in decision 2 is limited to default component construction. It does
|
|
58
|
+
not add a Runtime reset registration hook or amend ADR-039's reset algorithm.
|
|
59
|
+
|
|
60
|
+
## Dependency interpretation and remaining work
|
|
61
|
+
|
|
62
|
+
This is dependency inversion at the component selection boundary. The settings
|
|
63
|
+
owner defines and consumes factory slots; application composition supplies their
|
|
64
|
+
implementations. Settings can be instantiated with local factories without
|
|
65
|
+
loading Engine, adapters, or tracing. No dynamic constant lookup is used to
|
|
66
|
+
hide implementation references.
|
|
67
|
+
|
|
68
|
+
The runtime call from `Configuration#initialize` through the factory to a
|
|
69
|
+
concrete constructor still exists. Static reference graphs do not generally
|
|
70
|
+
follow this injected call; reports must distinguish source references from
|
|
71
|
+
runtime factory invocation. Construction does not call `Runtime.instance`:
|
|
72
|
+
the adapter's default pool is acquired later when an async method is called.
|
|
73
|
+
The previous graph cycle was not infinite constructor recursion.
|
|
74
|
+
|
|
75
|
+
`LLMAdapter::Base` still owns framework async wrappers and references Runtime.
|
|
76
|
+
Runtime, pools, tracing, and Agent retain other dependencies and cycles. This
|
|
77
|
+
step does not claim complete provider independence or an acyclic repository.
|
|
78
|
+
|
|
79
|
+
## Verification obligations
|
|
80
|
+
|
|
81
|
+
- Guard that configuration source does not reference Runtime, LLMAdapter, or
|
|
82
|
+
Tracing, and demonstrate settings construction with supplied local factories
|
|
83
|
+
without those implementations or application composition loaded.
|
|
84
|
+
- Verify binding and normal/eager loading do not instantiate global settings
|
|
85
|
+
or start Runtime, including after preloading configuration definitions.
|
|
86
|
+
- Preserve fresh defaults, subclass construction, explicit overrides, scoped
|
|
87
|
+
component identity restoration, and reset behavior on failed Runtime cleanup.
|
|
88
|
+
- Run the adapter contract/routing tests, configuration and Runtime lifecycle
|
|
89
|
+
tests, integration tests, and repository style/type/annotation gates.
|
|
90
|
+
- Preserve the API snapshot without regeneration and report the private boot
|
|
91
|
+
method separately; do not claim that the snapshot covers Configuration or
|
|
92
|
+
every framework singleton operation.
|
|
93
|
+
- Compare dependencies against the applied baseline with the same analyzer and
|
|
94
|
+
identify the unapplied candidate's exact source tree.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ADR-041: Feature-Owned Identity Registries
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted on the architecture refactoring branch.
|
|
6
|
+
|
|
7
|
+
Amends the implementation owner of the process-local identity registry in
|
|
8
|
+
[025-process-local-agent-ownership-and-runtime-admission](025-process-local-agent-ownership-and-runtime-admission.md).
|
|
9
|
+
The one-owner invariant, Runtime lifetime, public identity operations, durable
|
|
10
|
+
conflict defenses, and EventLoop execution admission remain in force.
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
Runtime instantiated Agent and Team identity registries, exposed their specific
|
|
15
|
+
operations, and invoked their shutdown methods directly. Agent identity
|
|
16
|
+
reservation interpreted Agent lifecycle and Storage exceptions inside Engine.
|
|
17
|
+
These are feature policies even though their lifetime is bounded by Runtime.
|
|
18
|
+
|
|
19
|
+
Moving files while preserving concrete construction, forwarding methods, or
|
|
20
|
+
aliases under Runtime would retain the same dependency. A process-global weak
|
|
21
|
+
map would also lose the required strong ownership and Runtime isolation.
|
|
22
|
+
|
|
23
|
+
## Decision
|
|
24
|
+
|
|
25
|
+
1. `Agent::OwnershipRegistry` in `agent/ownership_registry.rb` owns Agent identity
|
|
26
|
+
reservation, materialization, purge, uncertain-outcome policy, and detachment.
|
|
27
|
+
`MultiAgent::TeamOwnershipRegistry` in `multi_agent/team_ownership_registry.rb`
|
|
28
|
+
owns Team identity and construction exclusion. Feature callers use these
|
|
29
|
+
components directly. The old Runtime registry constants and forwarding
|
|
30
|
+
methods are removed; they were internal and receive no compatibility aliases.
|
|
31
|
+
2. Each registry lazily registers itself under its class key through Runtime's
|
|
32
|
+
existing internal shutdown participant contract. Candidate construction has
|
|
33
|
+
no side effects. Concurrent registration returns one authoritative instance
|
|
34
|
+
per Runtime. Runtime strongly retains it; neither GC nor execution completion
|
|
35
|
+
releases an identity. Registration after closure is rejected even for an
|
|
36
|
+
existing key.
|
|
37
|
+
3. `Runtime#__shutdown_participant(key:)` only looks up an existing participant,
|
|
38
|
+
including during/after shutdown. It does not create one or admit work.
|
|
39
|
+
Registry `for(runtime)` reuses it; each registry's gate rejects create/load/
|
|
40
|
+
purge admission after closure. `existing_for(runtime)` supports `get` without
|
|
41
|
+
creating a registry. Admitted transitions may finish through the retained
|
|
42
|
+
instance after closure. Lookup is not an admission bypass.
|
|
43
|
+
4. Participants implement `begin_draining` and `wait_until_idle(deadline)`.
|
|
44
|
+
Runtime closes every gate under its lifecycle lock, including on EventLoop
|
|
45
|
+
failure, before waiting outside that lock with one absolute monotonic deadline.
|
|
46
|
+
Closure must be short, idempotent, and must not call Runtime. For identity
|
|
47
|
+
registries, idle means no active construction/purge transition, not no live
|
|
48
|
+
objects. Stable recovery-required entries do not prevent shutdown.
|
|
49
|
+
5. Add the optional `after_runtime_shutdown` operation. Runtime invokes it outside
|
|
50
|
+
its lifecycle lock only if all participant waits succeed, no participant hook
|
|
51
|
+
has failed, EventLoop's idle/join checks pass without cancellation timeout,
|
|
52
|
+
and pools/timers shut down successfully. Existing participants without the
|
|
53
|
+
hook remain valid. The hook must be idempotent, short, and perform no I/O;
|
|
54
|
+
its return value is ignored. Agent detachment and Team directory clearing
|
|
55
|
+
implement this protocol. Runtime does not know their concrete classes.
|
|
56
|
+
6. A finalization exception does not skip later participants. Cleanup is then
|
|
57
|
+
incomplete, the first failure is retained, and default Runtime replacement
|
|
58
|
+
is prohibited. Completed releases are not rolled back. Repeated shutdown
|
|
59
|
+
returns the cached result rather than rerunning partial finalization.
|
|
60
|
+
7. Purge completion/abort/uncertainty updates the Agent object before publishing
|
|
61
|
+
the corresponding registry state, under the same registry mutex. These
|
|
62
|
+
internal Agent hooks only update local fields and cannot call Runtime or
|
|
63
|
+
perform I/O. Shutdown cannot observe a stable transition and detach the
|
|
64
|
+
Agent before that object's transition has finished.
|
|
65
|
+
|
|
66
|
+
## Guarantees and limits
|
|
67
|
+
|
|
68
|
+
| Subject | Property and provider | Failure scope / X0 | Result |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| Mutable Agent identity in one Runtime | At most one live owner per ID, reserved by Agent::OwnershipRegistry before materialization | Normal operation, F0 known failures, and F2 conflicts; X0 not crossed by the registry | YES within this Runtime; no cross-process exclusion |
|
|
71
|
+
| Uncertain Agent create/purge | Existing recovery-required policy retains the reservation | F1 durable outcome uncertainty, possibly with F0; no new X0 guarantee | YES for fail-closed local reservation; NO claim of outcome reconciliation |
|
|
72
|
+
| Old Agent references at Runtime replacement | Registry finalization detaches references before a completed reset | Normal shutdown and F0/F3 stop failures; X0 not managed here | CONDITIONAL on cleanup completion; failed/incomplete cleanup retains the Runtime |
|
|
73
|
+
| Durable facts after Runtime/process loss | Existing Persistence and recovery contracts | F4; X0 remains separate | No new guarantee; confirmed durable state and external effects are not rewritten by this change |
|
|
74
|
+
|
|
75
|
+
Team's existing conflict, Persistence identity, and construction-failure behavior
|
|
76
|
+
remain unchanged; Agent-specific recovery semantics are not imposed on Team.
|
|
77
|
+
No record format, transaction domain, public API, RBS shape, adapter SPI, or
|
|
78
|
+
worker mechanism changes. Runtime construction/shutdown of an unused instance
|
|
79
|
+
must not cause additional Agent, MultiAgent, or Storage loads; application-entry
|
|
80
|
+
bootstrap is measured separately.
|
|
81
|
+
|
|
82
|
+
EventLoop still owns Agent execution state/admission and Workflow-specific
|
|
83
|
+
control. Runtime's execution-owner and admission queries remain until a separate
|
|
84
|
+
execution-service extraction. This step does not make Engine feature-neutral.
|
|
85
|
+
Static graphs also do not follow the dynamic participant callbacks: the feature
|
|
86
|
+
implementation still runs through Engine's generic shutdown contract.
|
|
87
|
+
|
|
88
|
+
## Verification obligations
|
|
89
|
+
|
|
90
|
+
- Concurrent registration/materialization, exact-instance get/load, duplicate
|
|
91
|
+
create rejection, incompatible class/Persistence checks, and purge behavior.
|
|
92
|
+
- Lookup without registration, closed admission on shutdown and EventLoop
|
|
93
|
+
failure, admitted construction completion, and incomplete-cleanup retention.
|
|
94
|
+
- Finalization after quiescence, outside the lifecycle lock, with all later
|
|
95
|
+
participants visited after an exception and no reset after failure.
|
|
96
|
+
- Purge object/registry transitions cannot be observed as idle halfway through.
|
|
97
|
+
- Source/loading boundaries, API snapshot, RBS, annotations, and existing suites.
|
|
98
|
+
|
|
99
|
+
Tests of simulated uncertain outcomes verify reservation policy, not arbitrary
|
|
100
|
+
external-effect exactly-once behavior or cross-process ownership.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR-042: Feature-Owned Execution State on EventLoop
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted
|
|
4
|
+
**Date**: 2026-09-21
|
|
5
|
+
**Amends**: [024-event-loop-single-writer-agent-runtime](024-event-loop-single-writer-agent-runtime.md), [025-process-local-agent-ownership-and-runtime-admission](025-process-local-agent-ownership-and-runtime-admission.md), [026-workflow-runtime-admission-and-durable-terminal-barrier](026-workflow-runtime-admission-and-durable-terminal-barrier.md) for implementation ownership, delivery and shutdown admission
|
|
6
|
+
**Complements**: [041-feature-owned-identity-registries](041-feature-owned-identity-registries.md)
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
EventLoop implemented Agent admissions, execution snapshots, completion waiters,
|
|
11
|
+
physical-work tracking and recovery errors, as well as Workflow segment
|
|
12
|
+
admissions, routing and recovery-required state. Its dispatcher selected Agent
|
|
13
|
+
coordinators and Workflow runners. These are feature policies inside Engine;
|
|
14
|
+
retaining them behind dynamic calls would not remove the ownership problem.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
`Agent::ExecutionRegistry` owns Agent admission, immutable execution entries,
|
|
19
|
+
read-only owner lookup, completion waiters and physical quiescence supervision.
|
|
20
|
+
`WorkflowExecutionRegistry`, under `workflow/execution/`, owns Workflow segment
|
|
21
|
+
admission, instance-to-session routing and recovery-required admissions.
|
|
22
|
+
Both retain the existing semantic identities, exceptions and durable barriers.
|
|
23
|
+
Neither registry is the Runtime-lifetime Agent/Team identity registry of ADR-041.
|
|
24
|
+
|
|
25
|
+
All normal feature state mutation still runs on the one EventLoop thread.
|
|
26
|
+
Workers only enqueue results or physical-completion notifications. The Engine
|
|
27
|
+
`ExecutionReceiver` base defines an internal registration/delivery/lifecycle
|
|
28
|
+
contract; it is not an application callback or plugin SPI. Feature code creates
|
|
29
|
+
its receiver lazily. Engine never constructs a concrete feature receiver and
|
|
30
|
+
does not interpret feature IDs, commands, admission states or exceptions.
|
|
31
|
+
Runtime provides only a non-creating EventLoop lookup for feature inspection.
|
|
32
|
+
|
|
33
|
+
### Registration, delivery and drain
|
|
34
|
+
|
|
35
|
+
One EventLoop strongly retains one receiver per feature key. Registration is
|
|
36
|
+
allowed only while running. Existing lookup never reopens admission or creates
|
|
37
|
+
another loop. A receiver is bound to its original EventLoop.
|
|
38
|
+
|
|
39
|
+
The generic FIFO delivery contains a registered receiver, immutable command,
|
|
40
|
+
admission flag and optional caller completion. An in-memory envelope token
|
|
41
|
+
identifies only a pending delivery; it is not durable identity, a generation
|
|
42
|
+
counter, or authority for accepting a semantic result. Feature coordinators
|
|
43
|
+
continue to validate execution revisions, operation IDs and current FSM state.
|
|
44
|
+
|
|
45
|
+
Engine closes new receiver registration and new admission deliveries before
|
|
46
|
+
testing idleness. Agent start/approval resume, Recovery install/resolve and
|
|
47
|
+
Workflow start/resume are new requests at this boundary. A request queued before
|
|
48
|
+
drain remains accepted and may establish its feature admission during drain.
|
|
49
|
+
Existing worker results, physical-completion notifications, cancellation and
|
|
50
|
+
session events may continue while the loop drains. A continuation cannot claim
|
|
51
|
+
a new admission after the gate closes.
|
|
52
|
+
|
|
53
|
+
Engine counts a queued or currently dispatching receiver message until dispatch
|
|
54
|
+
finishes. This covers the interval before the feature admission exists. Feature
|
|
55
|
+
idleness covers admission-before-FSM-registration and logical-completion-before-
|
|
56
|
+
physical-quiescence intervals. Suspended/recovery-required admissions retain
|
|
57
|
+
their exclusion without indefinitely preventing shutdown.
|
|
58
|
+
|
|
59
|
+
Feature state reads/writes and Engine idleness use the same lifecycle mutex.
|
|
60
|
+
`idle?` is called with that lock held and must be short, nonblocking, and must
|
|
61
|
+
not call Runtime or reacquire the lock. Other state operations use the shared
|
|
62
|
+
synchronization helper. Delivery, TaskResult callbacks and receiver shutdown
|
|
63
|
+
execute outside the lock. Workflow routing resolves the admission and enqueues
|
|
64
|
+
to the currently admitted FSM under that same lock; it does not use a stale
|
|
65
|
+
lookup followed by an independent enqueue.
|
|
66
|
+
|
|
67
|
+
Generic FSM registration may attach its receiver. When the FSM reports
|
|
68
|
+
recovery-required retirement, Engine notifies that receiver; Workflow decides
|
|
69
|
+
how to retain its logical admission. Engine does not inspect Workflow state.
|
|
70
|
+
|
|
71
|
+
### Failure and final invalidation
|
|
72
|
+
|
|
73
|
+
On dispatcher failure, new delivery is closed. Engine fails current/queued
|
|
74
|
+
request completions and FSM waiters, then notifies every receiver with the
|
|
75
|
+
failure on the failing EventLoop thread. Each receiver clears its own state.
|
|
76
|
+
|
|
77
|
+
On normal shutdown, Engine notifies receivers only after its thread has joined.
|
|
78
|
+
This is exclusive final reference invalidation, not management-thread execution
|
|
79
|
+
progression. Agent fails retained nonterminal completion waiters with
|
|
80
|
+
`ExecutionRehydrationRequiredError`; Engine never selects that exception.
|
|
81
|
+
Workflow retains the prior behavior of not fabricating a terminal result from
|
|
82
|
+
an uncertain durable outcome. A receiver cleanup exception does not prevent
|
|
83
|
+
later receivers from being visited; cleanup is incomplete and default Runtime
|
|
84
|
+
replacement is refused. A loop still alive at timeout is not invalidated.
|
|
85
|
+
|
|
86
|
+
## Guarantees and limits
|
|
87
|
+
|
|
88
|
+
| Subject/property | Provider | Failure/boundary | Result and condition |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| Same-process exclusion and single-writer live state | Feature registries, EventLoop delivery and shared lifecycle lock | F2/F3; no X0 | YES within one Runtime; no cross-process exclusion |
|
|
91
|
+
| Clean shutdown waits for accepted admission and supervised physical work | Engine pending-delivery count plus feature idle predicates and OffloadPool completion | F0/F3; may observe work that crossed X0 | CONDITIONAL on work becoming quiescent before shutdown deadlines; no external effect rollback |
|
|
92
|
+
| Failed/terminated loop cannot continue authoritative live mutation | Closed delivery gate, loop-thread checks and exclusive receiver invalidation | F0/F3/F4; no new X0 | YES for framework-managed live state; process loss requires existing durable recovery |
|
|
93
|
+
| Uncertain durable execution is not released as successful | Existing Agent/Workflow durable barriers and recovery states | F1/F4; X0 unchanged | CONDITIONAL on the existing Persistence/reconciliation contracts; this change adds no durability or exactly-once guarantee |
|
|
94
|
+
|
|
95
|
+
Public Agent/Workflow APIs, persisted formats, TaskResult semantics and the
|
|
96
|
+
OffloadPool worker implementation are unchanged. Internal EventLoop feature
|
|
97
|
+
methods and Runtime Agent forwarding methods are removed without aliases.
|
|
98
|
+
|
|
99
|
+
## Rejected alternatives
|
|
100
|
+
|
|
101
|
+
- Move Agent errors to Engine/common while keeping Agent decisions in Engine.
|
|
102
|
+
- Have Engine construct concrete feature registries or choose coordinators.
|
|
103
|
+
- Move only static references while retaining Workflow-specific maps/dispatch.
|
|
104
|
+
- Split feature and idle locks, allowing shutdown to miss newly admitted work.
|
|
105
|
+
- Invoke arbitrary callbacks without registered receiver and lifecycle rules.
|
|
106
|
+
- Replace OffloadPool physical-completion supervision with new worker threads.
|