phronomy 0.26.0 → 0.28.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/.mutant.yml +9 -9
- data/CHANGELOG.md +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -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 +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- 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/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -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 +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -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 +104 -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 +153 -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 +83 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +52 -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/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- 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/knowledge_item.rb +13 -0
- 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 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- 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 +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- 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/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -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/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +107 -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/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -139
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +12 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +24 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +3 -185
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/storage/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- 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/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
|
@@ -1,179 +1,165 @@
|
|
|
1
|
-
# Persistence
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Agent
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- EventLoop queue contents;
|
|
37
|
-
- Runtime Workflow admission entries;
|
|
38
|
-
- in-flight provider operations.
|
|
39
|
-
|
|
40
|
-
Persistence operations are synchronous. Framework-owned blocking Persistence I/O
|
|
41
|
-
is submitted to the Runtime OffloadPool by Phronomy; a backend must not post
|
|
42
|
-
EventLoop events or introduce `load_async` / `save_async` variants into this
|
|
43
|
-
contract.
|
|
44
|
-
|
|
45
|
-
## Required root surface
|
|
46
|
-
|
|
47
|
-
A Persistence backend exposes eight durable repositories:
|
|
48
|
-
|
|
49
|
-
```text
|
|
50
|
-
contents
|
|
51
|
-
agents
|
|
52
|
-
journals
|
|
53
|
-
executions
|
|
54
|
-
workflow_states
|
|
55
|
-
handoff_states
|
|
56
|
-
teams
|
|
57
|
-
team_executions
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
and two root operations:
|
|
1
|
+
# Persistence and neutral Storage SPI 2
|
|
2
|
+
|
|
3
|
+
[ADR-058](decisions/058-neutral-storage-primitives.md) defines the current
|
|
4
|
+
extension contract. This is an intentional breaking replacement of the old
|
|
5
|
+
fixed-repository Backend SPI. Application construction now uses
|
|
6
|
+
`PersistenceComposition.build(backend:)` or `.in_memory`; pass its `agent`, `team`,
|
|
7
|
+
or `workflow` component to that domain. `Persistence.new(backend:)` provides only
|
|
8
|
+
the neutral coordinator. The old eight-repository facade and its queries were
|
|
9
|
+
removed; see [r8 unit 3](architecture/r8-unit3.md).
|
|
10
|
+
|
|
11
|
+
## Ownership and composition
|
|
12
|
+
|
|
13
|
+
Storage owns `Resource`, `Backend`, `View`, `Records`, `Streams`, `Blobs`, immutable
|
|
14
|
+
`Entry` values, guards, conditions and neutral exceptions. It has no Agent/Team
|
|
15
|
+
record types, active-execution policy, content digest algorithm or watermark API.
|
|
16
|
+
|
|
17
|
+
Agent, Team, Workflow and ContentStore declare their own resource schemas.
|
|
18
|
+
`PersistenceComposition::StorageSchema` gathers those declarations and
|
|
19
|
+
`PersistenceComposition` binds domain record adapters to one coordinator.
|
|
20
|
+
Team and Workflow schemas live in their existing `persistence/` implementation
|
|
21
|
+
directories. An isolated Agent uses `PersistenceComposition.agent` and does not
|
|
22
|
+
construct Team or Workflow stores.
|
|
23
|
+
`ContentStore::StoredContents` owns SHA-256 identity and digest verification.
|
|
24
|
+
The Agent-owned `Watermark` composes guarded revision and stream-head conditions.
|
|
25
|
+
|
|
26
|
+
InMemory receives `resources:` and supplies one Monitor/snapshot transaction
|
|
27
|
+
across the catalog. SQL reference composition supplies the same catalog and a
|
|
28
|
+
separate physical table/column mapping to the neutral driver in examples
|
|
29
|
+
`shared/storage`. Table names, columns, indexes, DurableRecord envelopes, payloads,
|
|
30
|
+
format versions and content identities remain unchanged.
|
|
31
|
+
|
|
32
|
+
The [S3 closure review](architecture/refactoring-closure.md) records why these
|
|
33
|
+
names and placements remain and separates applied evidence from the candidate.
|
|
34
|
+
|
|
35
|
+
## Backend and View
|
|
61
36
|
|
|
62
37
|
```ruby
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
38
|
+
backend = Phronomy::Storage::Backends::InMemory.new(resources: resources)
|
|
39
|
+
backend.view.records(resource)
|
|
40
|
+
backend.view.streams(resource)
|
|
41
|
+
backend.view.blobs(resource)
|
|
42
|
+
backend.transaction { |view| ... }
|
|
43
|
+
view.check!(guards: guards, conditions: conditions)
|
|
69
44
|
```
|
|
70
45
|
|
|
71
|
-
The
|
|
72
|
-
|
|
73
|
-
`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
46
|
+
The backend declares `spi_version: 2` and true values for `atomic_resources`,
|
|
47
|
+
`record_cas`, `stream_cas`, `conditional_unique`, `guarded_checks` and
|
|
48
|
+
`nested_savepoints`. Persistence validates backend capabilities; composition
|
|
49
|
+
validates the required domain resource declarations before binding adapters.
|
|
50
|
+
Unsupported backends raise `Persistence::UnsupportedBackendError`. There is no
|
|
51
|
+
eight-slot compatibility adapter or duplicated facade capability API.
|
|
52
|
+
|
|
53
|
+
A root handle routes to the current transaction on the same backend/thread.
|
|
54
|
+
A bound view and all its handles expire on commit or rollback and reject use from
|
|
55
|
+
another thread. Explicit nested transactions use savepoints on the same SQL
|
|
56
|
+
connection, or nested InMemory snapshots. Inner success depends on outer commit;
|
|
57
|
+
an inner exception is re-raised after rollback and can be caught by the outer
|
|
58
|
+
scope. `ActiveRecord::Rollback` also propagates.
|
|
59
|
+
|
|
60
|
+
A failure during physical work marks the scope failed. Catching it inside that
|
|
61
|
+
same scope does not permit further operations or successful commit. Establish an
|
|
62
|
+
explicit inner transaction before a recoverable operation and catch outside it.
|
|
63
|
+
Input validation occurs before physical work. A successful optional read returning
|
|
64
|
+
nil is not a failed physical operation; domain required-load errors may be raised
|
|
65
|
+
after that read. Domain decode/returned-metadata failures stay inside the atomic
|
|
66
|
+
boundary so a corrupt response after a write causes rollback.
|
|
67
|
+
|
|
68
|
+
Transaction blocks must finish normally. `return`, `break` and `throw` escaping
|
|
69
|
+
them cause rollback and `TransactionError`. These exits are not commit controls.
|
|
70
|
+
Commit/rollback transport failures remain database failures; this contract does
|
|
71
|
+
not promise exactly-once external effects or infer commit certainty.
|
|
72
|
+
|
|
73
|
+
## Resource declarations
|
|
74
|
+
|
|
75
|
+
A `Resource` is an immutable value with `id`, `kind`, `attributes`,
|
|
76
|
+
`immutable_attributes`, `indexes`, `unique` and optional `guard`. Attributes use
|
|
77
|
+
`:string`, `:integer`, `:boolean` and their explicit `:nullable_*` forms. Keys and
|
|
78
|
+
text attributes use valid UTF-8 without NUL; keys are nonempty. Binary data belongs
|
|
79
|
+
to Blobs. No Proc, SQL expression or payload predicate is accepted.
|
|
80
|
+
|
|
81
|
+
Named equality indexes specify exact fields. Records may declare conditional
|
|
82
|
+
unique constraints with a symbol name, fields and equality `where` values. Null
|
|
83
|
+
unique-key fields are distinct, matching the default SQL unique-index semantics.
|
|
84
|
+
Streams have no indexed attributes; Blobs have attributes but no indexes/guards.
|
|
85
|
+
A record guard uses its key or an immutable non-null string attribute. A stream
|
|
86
|
+
guard uses its stream ID. Its anchor must be a registered Records resource.
|
|
87
|
+
Required anchors must exist; missing parents raise `NotFoundError` in all drivers.
|
|
88
|
+
|
|
89
|
+
## Records
|
|
90
|
+
|
|
91
|
+
| Operation | Contract |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `insert(key:, revision:, attributes:, record:)` | Insert only when absent; return an independent immutable `Entry::Record`. Primary duplicates and named unique failures differ. |
|
|
94
|
+
| `read(key)` / `fetch(key)` | Optional nil / required `NotFoundError`. |
|
|
95
|
+
| `replace(key:, expected_revision:, next_revision:, attributes:, record:, expected_attributes: {})` | Check existence, revision, expected attributes, immutable fields and uniqueness atomically; next revision must equal expected + 1. Replace all attributes. |
|
|
96
|
+
| `delete(key:, expected_revision: Records::UNCHECKED)` | Unchecked deletion is idempotent. A supplied revision requires existence and equality. Return nil. |
|
|
97
|
+
| `scan(index:, equals:, after: nil, limit: nil)` | Exact named-index fields, UTF-8 byte order, exclusive key cursor, positive limit or nil for all. |
|
|
98
|
+
| `delete_matching(index:, equals:)` | Delete equality matches atomically; a parent-guarded resource must constrain the guard attribute. Return nil. |
|
|
99
|
+
|
|
100
|
+
An Entry carries key, revision, attributes and an opaque `DurableRecord`.
|
|
101
|
+
The driver does not reconstruct metadata from payload. Domain wrappers verify
|
|
102
|
+
that returned identity, revision and attributes agree with decoded domain data.
|
|
103
|
+
Workflow/Handoff choose initial revision 1 and route nil expected revision to
|
|
104
|
+
insert; nil is never an unchecked update. Team terminal-to-active rejection is a
|
|
105
|
+
domain `expected_attributes: {active: true}` precondition when saving active state.
|
|
106
|
+
|
|
107
|
+
## Streams and Blobs
|
|
108
|
+
|
|
109
|
+
Streams expose `append(stream:, expected_head:, entries:)`,
|
|
110
|
+
`read(stream:, after: 0, limit: nil)`, `head(stream:)`, and `delete(stream:)`.
|
|
111
|
+
Append takes `Entry::Append(id:, record:)`, validates the entire batch before any
|
|
112
|
+
write, enforces unique entry IDs within a stream, assigns contiguous positions
|
|
113
|
+
and updates the head atomically. Empty append still checks the expected head.
|
|
114
|
+
Reads return immutable `Entry::Stream(position:, id:, record:)` values in position
|
|
115
|
+
order. Delete removes head and entries together. The domain Journal wrapper
|
|
116
|
+
preserves its existing `limit: 0` empty-result behavior without a raw zero-limit
|
|
117
|
+
operation.
|
|
118
|
+
|
|
119
|
+
Blobs expose `put_if_absent(key:, bytes:, attributes:)`, `fetch(key)` and
|
|
120
|
+
`exist?(key)`. Same bytes retain the first attributes; different bytes for an
|
|
121
|
+
existing key raise `BlobConflictError`. Entry bytes are independent immutable
|
|
122
|
+
binary strings. Blob keys are arbitrary storage keys; ContentStore adds the
|
|
123
|
+
`sha256:<digest>` contract and maps integrity failures to its own `IntegrityError`.
|
|
124
|
+
|
|
125
|
+
## Guards, conditions and errors
|
|
126
|
+
|
|
127
|
+
`GuardRef(resource:, key:)` names a stable existing parent record. `View#check!`
|
|
128
|
+
acquires guards in resource/key byte order before evaluating the closed condition
|
|
129
|
+
set: `RevisionIs`, `StreamHeadIs`, `NoRows`. A condition must include the guard
|
|
130
|
+
required by its resource and scope. PostgreSQL locks parents before child heads
|
|
131
|
+
or records; SQLite relies on the transaction/CAS/unique constraints and must not
|
|
132
|
+
claim a SELECT alone reserves a writer. Transactions spanning multiple owners
|
|
133
|
+
must establish a consistent owner lock order; database deadlocks remain database
|
|
134
|
+
errors, never optimistic conflicts.
|
|
135
|
+
|
|
136
|
+
`UniqueConstraintError < ConflictError` carries the resource ID and constraint
|
|
137
|
+
name. Agent/Team translate only their exact `one_active_owner` constraint to
|
|
138
|
+
`AgentBusyError`. Their idle checks translate their own `NoRows` condition failure.
|
|
139
|
+
`ConditionFailedError` carries the failed condition. Duplicate identities,
|
|
140
|
+
stale revisions and ordinary constraint conflicts remain `ConflictError`.
|
|
141
|
+
`NotFoundError`, `SerializationError`, `UnsupportedBackendError` retain their
|
|
142
|
+
meanings; `TransactionError` identifies invalid scope use. The removed
|
|
143
|
+
`ActiveExecutionConflictError` and `Storage::Repositories` have no aliases.
|
|
144
|
+
|
|
145
|
+
The following sections describe the **domain-owned record protocols**, used by
|
|
146
|
+
domain implementations and backend conformance tests. They are not a shared
|
|
147
|
+
application facade. Unqualified
|
|
148
|
+
ConflictError, NotFoundError, SerializationError and UnsupportedBackendError in
|
|
149
|
+
these domain sections refer to `Phronomy::Persistence` errors. Repository exits
|
|
150
|
+
translate the four raw Storage categories, preserving `cause`, message and trace.
|
|
151
|
+
Backend implementation code continues to raise `Phronomy::Storage` errors as
|
|
152
|
+
specified above. Unknown/transport errors and Storage::TransactionError are not
|
|
153
|
+
translated. See [ADR-061](decisions/061-persistence-failure-contracts.md) and the
|
|
154
|
+
[application migration](migrations/persistence-failure-contracts.md).
|
|
171
155
|
|
|
172
156
|
## Contents repository
|
|
173
157
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
`ContentStore::StoredContents < ContentStore::Base` supplies text/JSON helpers
|
|
159
|
+
and canonical content-ID calculation over neutral Blobs. The private
|
|
160
|
+
Persistence::ContentRepository adapts its primitive methods to the upper failure
|
|
161
|
+
contract when accessed through `persistence.contents`. Direct StoredContents
|
|
162
|
+
callers retain the raw Storage error surface.
|
|
177
163
|
|
|
178
164
|
Required primitive surface:
|
|
179
165
|
|
|
@@ -188,7 +174,7 @@ Required semantics:
|
|
|
188
174
|
- content is immutable and content-addressed;
|
|
189
175
|
- writing identical bytes is idempotent and returns the same content ID;
|
|
190
176
|
- `fetch` returns a binary `String` isolated from caller mutation;
|
|
191
|
-
- a missing content ID raises `Persistence::NotFoundError`;
|
|
177
|
+
- a missing content ID through `persistence.contents` raises `Persistence::NotFoundError`;
|
|
192
178
|
- one content ID must never resolve to different bytes; a digest-integrity
|
|
193
179
|
violation raises `ContentStore::IntegrityError`.
|
|
194
180
|
|
|
@@ -374,10 +360,9 @@ honestly rather than converting them into `ConflictError`.
|
|
|
374
360
|
|
|
375
361
|
### Workflow value serialization
|
|
376
362
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
`Proc`, IO objects, sockets, or runtime callbacks.
|
|
363
|
+
The Workflow domain codec accepts canonical JSON-compatible snapshot values in
|
|
364
|
+
all drivers, including InMemory. Proc, IO, sockets and runtime callbacks are not
|
|
365
|
+
durable snapshot values.
|
|
381
366
|
|
|
382
367
|
A JSON/JSONB backend should document its supported value domain. A recommended
|
|
383
368
|
domain is:
|
|
@@ -391,8 +376,9 @@ Array of supported values
|
|
|
391
376
|
Hash with String/Symbol keys and supported values
|
|
392
377
|
```
|
|
393
378
|
|
|
394
|
-
If a value cannot be represented,
|
|
395
|
-
|
|
379
|
+
If a value cannot be represented, the backend/codec raises
|
|
380
|
+
`Storage::SerializationError`; the domain repository exposes
|
|
381
|
+
`Persistence::SerializationError`. Do not silently convert it into a lossy form. JSON backends may return String
|
|
396
382
|
keys after decoding; `WorkflowRunner` deliberately accepts String and Symbol keys
|
|
397
383
|
and normalizes them when comparing durable snapshots.
|
|
398
384
|
|
|
@@ -401,8 +387,8 @@ particular database backend accept arbitrary Workflow values.
|
|
|
401
387
|
|
|
402
388
|
## Durable Agent watermark
|
|
403
389
|
|
|
404
|
-
`assert_agent_watermark!` is a
|
|
405
|
-
|
|
390
|
+
`assert_agent_watermark!` is a domain Persistence operation. Agent-owned
|
|
391
|
+
`Watermark` composes a parent guard, root revision and Journal head conditions.
|
|
406
392
|
|
|
407
393
|
Phronomy uses it at durable barriers because a hydrated live Agent owns the
|
|
408
394
|
current logical state and Phronomy deliberately does not reload mutable Agent
|
|
@@ -421,16 +407,17 @@ differs, raise `ConflictError`. On success return `true`.
|
|
|
421
407
|
The operation must not return a replacement AgentRoot or Journal. A mismatch is a
|
|
422
408
|
conflict, not a request to reload/merge mutable state.
|
|
423
409
|
|
|
424
|
-
When used inside `
|
|
410
|
+
When used inside `Agent::Store#transaction`, a SQL backend should perform the
|
|
425
411
|
watermark check in the same database transaction as the subsequent durable
|
|
426
412
|
write.
|
|
427
413
|
|
|
428
414
|
## Transaction contract
|
|
429
415
|
|
|
430
|
-
A transaction
|
|
416
|
+
A domain transaction combines its own record operations. Multiple domains join
|
|
417
|
+
one coordinator scope explicitly; each receives only its own record protocol:
|
|
431
418
|
|
|
432
419
|
```ruby
|
|
433
|
-
|
|
420
|
+
stores.agent.transaction do |tx|
|
|
434
421
|
tx.assert_agent_watermark!(...)
|
|
435
422
|
content_ref = tx.contents.put_text("...")
|
|
436
423
|
tx.journals.append(...)
|
|
@@ -482,9 +469,9 @@ This is a targeted migration rule for the removed generic identity field. It doe
|
|
|
482
469
|
not establish a general unknown-field or long-term codec/schema-versioning
|
|
483
470
|
policy.
|
|
484
471
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
472
|
+
Domain codecs own canonical Hash representation. The raw driver stores the
|
|
473
|
+
DurableRecord envelope and separately supplied metadata without interpreting
|
|
474
|
+
domain payload fields.
|
|
488
475
|
|
|
489
476
|
## Conformance tests
|
|
490
477
|
|
|
@@ -508,6 +495,11 @@ an Agent repository
|
|
|
508
495
|
a Journal repository
|
|
509
496
|
an Execution repository
|
|
510
497
|
a workflow state repository
|
|
498
|
+
a Handoff state repository
|
|
499
|
+
a Team repository
|
|
500
|
+
a Team execution repository
|
|
501
|
+
neutral storage primitives
|
|
502
|
+
storage transaction boundaries
|
|
511
503
|
a Persistence backend
|
|
512
504
|
```
|
|
513
505
|
|
|
@@ -518,7 +510,7 @@ require "phronomy"
|
|
|
518
510
|
require "phronomy/testing/persistence_contract"
|
|
519
511
|
|
|
520
512
|
RSpec.describe MyPersistenceBackend do
|
|
521
|
-
let(:
|
|
513
|
+
let(:stores) { Phronomy::PersistenceComposition.build(backend: described_class.new(...)) }
|
|
522
514
|
|
|
523
515
|
it_behaves_like "a persistence content store"
|
|
524
516
|
it_behaves_like "an Agent repository"
|
|
@@ -532,7 +524,7 @@ RSpec.describe MyPersistenceBackend do
|
|
|
532
524
|
end
|
|
533
525
|
```
|
|
534
526
|
|
|
535
|
-
`
|
|
527
|
+
`PersistenceComposition.in_memory` is run through the same shipped contract source in
|
|
536
528
|
Phronomy CI. The files under `spec/support/shared_examples/` are compatibility
|
|
537
529
|
require wrappers only; the authoritative shared-example implementations live
|
|
538
530
|
under `lib/phronomy/testing/persistence_contract/` so the core suite and external
|
|
@@ -560,34 +552,17 @@ may use combinations of:
|
|
|
560
552
|
Backend-specific database exceptions should be translated to the Phronomy error
|
|
561
553
|
contract where their meaning is known.
|
|
562
554
|
|
|
563
|
-
##
|
|
555
|
+
## Coordination domain repositories
|
|
564
556
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
`
|
|
568
|
-
|
|
569
|
-
|
|
557
|
+
Handoff starts at revision 1 with expected revision nil; later saves advance one
|
|
558
|
+
revision. Team roots and executions begin at revision 0. Team admission retains
|
|
559
|
+
`AgentBusyError`; stale CAS and duplicate identities use `ConflictError`.
|
|
560
|
+
Missing Team/execution loads raise `NotFoundError`; absent Handoff state returns
|
|
561
|
+
nil. The unchanged record types are `phronomy.handoff_state`, `phronomy.team_root`
|
|
562
|
+
and `phronomy.team_execution`, all version `0.1`.
|
|
570
563
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
| `executions` extension | `list(agent_id, after: nil, limit: 100)` for retained active and terminal records |
|
|
577
|
-
|
|
578
|
-
Handoff starts at revision 1 with expected revision nil; later saves advance
|
|
579
|
-
exactly one revision. Team roots and executions begin at revision 0. Team
|
|
580
|
-
admission has the existing `AgentBusyError` contract for an already active owner;
|
|
581
|
-
stale CAS/duplicate identity uses `ConflictError`. Missing Team/execution loads
|
|
582
|
-
raise `NotFoundError`; absent Handoff state returns nil. Unavailable reads and
|
|
583
|
-
codec errors must propagate separately.
|
|
584
|
-
|
|
585
|
-
Lists sort IDs lexically, use an exclusive ID cursor, return at most the positive
|
|
586
|
-
integer limit, include only the requested owner, and return immutable copies.
|
|
587
|
-
Backends define retention; enumeration does not implement input correlation or
|
|
588
|
-
request deduplication. Terminal-to-active rewrites are invalid.
|
|
589
|
-
|
|
590
|
-
For F1, atomic commit does not imply a known response. Phronomy reads back the
|
|
591
|
-
same intended ID/fact before advancing. A backend must provide authoritative
|
|
592
|
-
reads/CAS; failure of readback is returned without new semantic work. No callback
|
|
593
|
-
ACK/index is part of the SPI. Existing cross-process exclusion limitations remain.
|
|
564
|
+
Backend authors should also read the [SPI 2 migration guide](migrations/neutral-storage-spi.md).
|
|
565
|
+
The Stable/Beta product API snapshot and the explicit Storage SPI 2 signature
|
|
566
|
+
snapshot are separate gates. Live PostgreSQL locking and failure tests must run
|
|
567
|
+
against the candidate core and examples revisions; earlier SPI results do not
|
|
568
|
+
satisfy this gate.
|