phronomy 0.27.0 → 0.29.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 +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +28 -7
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -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 +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- 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 +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -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 +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +12 -4
- data/docs/getting-started.md +52 -48
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -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/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +132 -103
- data/lib/phronomy/agent/base.rb +146 -113
- data/lib/phronomy/agent/before_llm_input.rb +70 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
- 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 +188 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- 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 +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
- 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/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- 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/error.rb +65 -1
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +195 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → 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_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- 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/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- 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 +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -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/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- 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/multi_agent/admission_registry.rb +1 -8
- 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/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -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 +60 -44
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- 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/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- 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 +2 -2
- 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 +9 -4
- 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 +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -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/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 +8 -2
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- 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 +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +551 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- 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/tool/tool_executor.rb +58 -0
- 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 +36 -7
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- 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/execution/workflow.rb +37 -9
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
- data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +63 -16
- data/scripts/api_snapshot.rb +47 -15
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- 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/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -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/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +29 -135
- 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 +23 -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 +1 -21
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/integrations.rbs +14 -0
- 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/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/multi_agent.rbs +6 -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 +1 -99
- 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.rbs → storage/contracts.rbs} +4 -18
- data/sig/phronomy/tool.rbs +48 -4
- 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 +15 -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 +5 -1
- data/sig/phronomy.rbs +4 -49
- metadata +365 -117
- 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 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- 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/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- 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/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Migrating to r8 unit 7
|
|
2
|
+
|
|
3
|
+
Apply after core b337fc77dae530269bc34d895365032873d544ad (r8 unit 6).
|
|
4
|
+
Public Agent invocation, streaming, approval, cancellation and recovery signatures
|
|
5
|
+
and persistence record schemas are unchanged. The current unit 6 examples need
|
|
6
|
+
no source changes. Run them against the candidate using PHRONOMY_PATH and the
|
|
7
|
+
correct BUNDLE_GEMFILE for each example directory.
|
|
8
|
+
|
|
9
|
+
There are no compatibility aliases for removed internal APIs:
|
|
10
|
+
|
|
11
|
+
| Internal use | Replacement |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| AgentInvocationSessionBuilder | AgentInvocation plus the owning ExecutionEnvironment.build_agent_session |
|
|
14
|
+
| AgentInvocationSessionBuilder entry/prepared actions | InvocationActions |
|
|
15
|
+
| ExecutionSessionRunner.new(runtime: ...) | ExecutionSessionRunner.new(environment: ...) |
|
|
16
|
+
| OwnershipRegistry.for/existing_for(Runtime) | Pass an ExecutionEnvironment; EngineEnvironment adapts Runtime |
|
|
17
|
+
| ExecutionReceiver subclasses initialize(event_loop:) | initialize(channel:), preserving super |
|
|
18
|
+
| AgentInvocation.begin_llm_call! implicit Runtime lookup | Explicit llm_call_id from durable prepared execution |
|
|
19
|
+
|
|
20
|
+
ExecutionEnvironment is a private framework connection, not an application
|
|
21
|
+
configuration feature. Do not serialize environments, channels, sessions or live
|
|
22
|
+
registries. The default composition is installed by requiring phronomy. State
|
|
23
|
+
policy loading alone does not install a runtime. Existing persisted Agent/Tool
|
|
24
|
+
identities, revisions, authorization snapshots and recovery classifications are
|
|
25
|
+
retained. This change does not add support for exchanging in-flight unit 5 or
|
|
26
|
+
older execution snapshots with unit 6/7 workers.
|
|
27
|
+
|
|
28
|
+
Stop/drain old processes using the existing deployment procedure before replacing
|
|
29
|
+
code. Deploy all internal call-site changes together. The application package
|
|
30
|
+
contains a strict-base dry-run/applier, expected-tree verification, review patch,
|
|
31
|
+
validation logs and rollback guidance. Run --verify before and after commit.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Migrating to r8 unit 8
|
|
2
|
+
|
|
3
|
+
Apply after core a601a0654ace42640f0251b129359ca1f326e3ad (unit 7).
|
|
4
|
+
This is a core-only change. The unit 6 examples at
|
|
5
|
+
ed455c52018338f7c5a4c2948ae0d7db131aa090 require no source changes.
|
|
6
|
+
|
|
7
|
+
Public Agent invocation/stream/approval/cancellation/recovery signatures and
|
|
8
|
+
Tool#call_async(args, cancellation_token:, config:) are unchanged. Tool::Operation
|
|
9
|
+
adds an explicit C06 operation entry and result transformation API; application
|
|
10
|
+
Tools do not need to adopt it. The supplied submitter applies to the default
|
|
11
|
+
offloaded operation only. Custom async implementations retain their existing
|
|
12
|
+
execution mechanism and configuration.
|
|
13
|
+
|
|
14
|
+
| Internal use | Replacement |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| Agent::ToolInvocationSessionBuilder.build / build_for_resume | The owning ExecutionEnvironment.build_tool_session, with resume_event/resume_phase when needed |
|
|
17
|
+
| ToolInvocationSessionBuilder transition constants | Agent::ToolInvocationTransitions |
|
|
18
|
+
| ToolInvocationSessionBuilder entry operations | Agent::ToolInvocationActions |
|
|
19
|
+
| ToolInvocation.start_authorization/runtime: and start_execution/runtime: | Pass environment: explicitly; never reconstruct it from a new global Runtime |
|
|
20
|
+
| Tool::ToolExecutor.call_async(runtime:) | Tool::Operation.call_async(submitter:) for contract consumers; default executor takes submitter: internally |
|
|
21
|
+
| Agent ToolBinding's custom-async detection and mapping mechanism | Tool::Operation.with_result_transform; Agent retains filter configuration |
|
|
22
|
+
|
|
23
|
+
Removed internal names have no aliases. Deploy all internal call sites together.
|
|
24
|
+
Do not persist execution environments, submitters, FSM sessions or parent sinks.
|
|
25
|
+
Durable IDs, record schemas, revision checks and saved approval evidence are
|
|
26
|
+
unchanged. The change does not certify new in-flight cross-version compatibility.
|
|
27
|
+
Use the existing stop/drain procedure before replacing running processes.
|
|
28
|
+
|
|
29
|
+
The application ZIP contains strict-base --check/--apply/--verify commands,
|
|
30
|
+
complete replacement files and deletions, expected-tree verification, a review
|
|
31
|
+
patch, validation evidence and rollback instructions. Run --verify before and
|
|
32
|
+
after commit. Verify examples with PHRONOMY_PATH pointing at the candidate and
|
|
33
|
+
unset a stale BUNDLE_GEMFILE before entering each Bundler context.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# R8 unit 9 migration
|
|
2
|
+
|
|
3
|
+
Apply to core unit 8, commit e9f6cc16da19be82a0b7777486926ba46d070def.
|
|
4
|
+
Examples source remains at unit 6, ed455c52018338f7c5a4c2948ae0d7db131aa090.
|
|
5
|
+
|
|
6
|
+
No public Tool or Agent signature, stored schema, migration CLI or example source
|
|
7
|
+
change is required. Existing configured filter order and repeated registrations
|
|
8
|
+
are preserved. The correction removes only the extra transformation caused by
|
|
9
|
+
Base's standard async-to-sync delegation under a custom async result decorator.
|
|
10
|
+
|
|
11
|
+
A custom call_async using super now passes its raw delegated result through its
|
|
12
|
+
own processing before the async decorator applies the configured transform.
|
|
13
|
+
Code accidentally relying on the duplicate/intermediate transformation should
|
|
14
|
+
express the required stages explicitly in application filter configuration.
|
|
15
|
+
|
|
16
|
+
A custom call_async explicitly calling public call retains that call's filters;
|
|
17
|
+
the enclosing async result decorator also retains its stage. Do not substitute
|
|
18
|
+
call for super when the intent is to delegate to Base's normal async execution.
|
|
19
|
+
The framework does not suppress application-authored calls or infer their intent.
|
|
20
|
+
|
|
21
|
+
Private changes: Base uses Tool::Operation.synchronous_delegate; ToolExecutor
|
|
22
|
+
accepts a bound synchronous_call keyword. Applications should not use these
|
|
23
|
+
private bridge helpers. Custom async implementations retain their existing
|
|
24
|
+
execution mechanism and keyword protocol.
|
|
25
|
+
|
|
26
|
+
Use the normal process stop/drain and restart procedure. This unit does not add
|
|
27
|
+
cross-version in-flight compatibility or rollback of external Tool side effects.
|
|
28
|
+
See docs/architecture/r8-unit9.md for operation-boundary examples.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Storage asynchronous execution and ContentStore ownership
|
|
2
|
+
|
|
3
|
+
P4 adds the public Beta `Phronomy::Storage::AsyncClient`. Synchronous storage
|
|
4
|
+
backends and `Persistence#transaction` keep their existing interfaces. Backend
|
|
5
|
+
authors implement no scheduler or async methods.
|
|
6
|
+
|
|
7
|
+
## Public transaction API
|
|
8
|
+
|
|
9
|
+
Construct `Storage::AsyncClient.new(backend: raw_backend, pool: nil)` around a
|
|
10
|
+
Storage SPI 2 backend. For an existing Persistence facade, use
|
|
11
|
+
`persistence.backend`. The transaction block receives a **raw Storage::View**,
|
|
12
|
+
not the domain repositories yielded by `Persistence#transaction`.
|
|
13
|
+
|
|
14
|
+
`transaction_async(cancellation_token: nil, timeout: nil) { |view| ... }`
|
|
15
|
+
returns the pool's original TaskResult. The complete transaction runs on one
|
|
16
|
+
worker, using the backend's normal connection and scope. Do not split a
|
|
17
|
+
transaction across workers or wait for another TaskResult inside its block.
|
|
18
|
+
Explicit synchronous `backend.transaction` calls within the block retain nested
|
|
19
|
+
savepoint behavior.
|
|
20
|
+
|
|
21
|
+
### Complete offline example
|
|
22
|
+
|
|
23
|
+
This requires a checkout containing P4; the previously released 0.27.0 gem does
|
|
24
|
+
not contain the new client. It uses only the public raw storage contract.
|
|
25
|
+
|
|
26
|
+
```ruby runnable
|
|
27
|
+
require "phronomy"
|
|
28
|
+
require "json"
|
|
29
|
+
|
|
30
|
+
resource = Phronomy::Storage::Resource.new(id: "example.notes", kind: :records)
|
|
31
|
+
backend = Phronomy::Storage::Backends::InMemory.new(resources: [resource])
|
|
32
|
+
client = Phronomy::Storage::AsyncClient.new(backend: backend)
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
record = Phronomy::Storage::DurableRecord.new(
|
|
36
|
+
record_type: "example.note", format_version: "0.1", payload: {"text" => "saved"}
|
|
37
|
+
)
|
|
38
|
+
task = client.transaction_async do |view|
|
|
39
|
+
rows = view.records(resource)
|
|
40
|
+
rows.insert(key: "note-1", revision: 1, attributes: {}, record: record)
|
|
41
|
+
rows.fetch("note-1").record.payload
|
|
42
|
+
end
|
|
43
|
+
puts JSON.generate(task.wait_result)
|
|
44
|
+
ensure
|
|
45
|
+
Phronomy.reset_runtime!
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The output is `{"text":"saved"}`. Return a value from the transaction. A bound
|
|
50
|
+
view or resource handle becomes invalid after the block finishes, even if it
|
|
51
|
+
was returned through TaskResult. Scope also rejects access from another thread.
|
|
52
|
+
Exceptions and invalid non-local block exits roll back through the existing
|
|
53
|
+
backend rules; no new transaction implementation is introduced.
|
|
54
|
+
|
|
55
|
+
## Admission, cancellation and ownership
|
|
56
|
+
|
|
57
|
+
Construction starts no Runtime. Each call resolves the current default pool,
|
|
58
|
+
unless an explicit caller-owned `pool:` was supplied. Runtime reset does not
|
|
59
|
+
transfer ownership of an injected pool.
|
|
60
|
+
|
|
61
|
+
Admission uses `on_full: :raise`: a full or stopped pool raises immediately.
|
|
62
|
+
Queue wait counts toward `timeout`. Cancellation/timeout before worker pickup
|
|
63
|
+
skips the transaction. After work starts, TaskResult may settle while the
|
|
64
|
+
physical transaction continues and commits. A timeout or cancellation is **not
|
|
65
|
+
a rollback guarantee**. The client does not kill a thread, add retries, or claim
|
|
66
|
+
that an uncertain commit succeeded or failed. Applications needing a durable
|
|
67
|
+
outcome must reconcile through their domain's identities and stored state.
|
|
68
|
+
|
|
69
|
+
## Existing Agent, Workflow and coordination operations
|
|
70
|
+
|
|
71
|
+
Internal `Storage::AsyncClient.submit(pool:)` accepts an existing synchronous
|
|
72
|
+
storage unit. This is private framework API, not an application extension API.
|
|
73
|
+
It submits exactly once and adds no transaction, token, or timeout. This lets
|
|
74
|
+
domain operations preserve their own transaction and reconciliation boundaries.
|
|
75
|
+
See the [operation inventory](../architecture/storage-execution-boundaries.md).
|
|
76
|
+
|
|
77
|
+
In particular, a Workflow runner may have only an injected repository rather
|
|
78
|
+
than access to its raw backend. Internal submit therefore accepts a callable
|
|
79
|
+
and the owner's pool; it does not require a dummy backend or a Persistence
|
|
80
|
+
facade. Public transaction_async requires a real SPI 2 backend.
|
|
81
|
+
|
|
82
|
+
Durable commit/readback policy, admission release, callback delivery and the
|
|
83
|
+
application of results to live state remain with Agent/Workflow/MultiAgent.
|
|
84
|
+
No cancellation condition is added to existing terminal saves. Tool execution,
|
|
85
|
+
authorization, tracing and application approval listeners retain their own
|
|
86
|
+
execution boundaries.
|
|
87
|
+
|
|
88
|
+
## ContentStore path and diagram
|
|
89
|
+
|
|
90
|
+
P4 originally moved `ContentStore::StoredContents` into
|
|
91
|
+
`content_store/backends/stored_contents.rb`. [ADR-062](../decisions/062-content-store-service.md)
|
|
92
|
+
supersedes that split: the implementation now lives at
|
|
93
|
+
`content_store/stored_contents.rb`, beside its shared API and owned schema.
|
|
94
|
+
The obsolete backend directory and collapse entry are deleted without a shim.
|
|
95
|
+
Its constant, synchronous operations, digest rules and exceptions are unchanged.
|
|
96
|
+
Use normal `require "phronomy"`; internal file paths are not a public load API.
|
|
97
|
+
Zeitwerk still collapses `storage/async/`. `Storage::Backends` remains a namespace.
|
|
98
|
+
ContentStore receives no artificial async API.
|
|
99
|
+
|
|
100
|
+
M10 is G57 Content Service in B3, outside Backend Contracts/Implementations;
|
|
101
|
+
M73 is retired. M69 Storage AsyncClient stays in B4. Text panels are transparent.
|
|
102
|
+
Arrows targeting M33/M41/M44 are hidden by display settings; all measured
|
|
103
|
+
dependencies, source evidence and matrix cells remain available.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# VectorStore and Embeddings: asynchronous client migration
|
|
2
|
+
|
|
3
|
+
This unreleased P3 change moves the Beta asynchronous API to explicit clients.
|
|
4
|
+
It changes the receiver used by callers. Backend authors continue to implement
|
|
5
|
+
only synchronous methods; method arguments and results remain unchanged.
|
|
6
|
+
|
|
7
|
+
| Previous receiver | New receiver |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `store.add_async(...)` | `async_store.add_async(...)` |
|
|
10
|
+
| `store.search_async(...)` | `async_store.search_async(...)` |
|
|
11
|
+
| `store.remove_async(...)` | `async_store.remove_async(...)` |
|
|
12
|
+
| `store.clear_async(...)` | `async_store.clear_async(...)` |
|
|
13
|
+
| `embeddings.embed_async(text, token, timeout: seconds)` | `async_embeddings.embed_async(text, token, timeout: seconds)` |
|
|
14
|
+
|
|
15
|
+
Construct the clients with `VectorStore::AsyncClient.new(backend: store)` and
|
|
16
|
+
`VectorStore::Embeddings::AsyncClient.new(adapter: embeddings)`. The embedding
|
|
17
|
+
cancellation token stays positional. Synchronous `add`, `search`, `remove`,
|
|
18
|
+
`clear`, `size` and `embed` stay on the existing backend objects.
|
|
19
|
+
|
|
20
|
+
`VectorStore::AsyncBackend` and inherited async methods are removed. There is
|
|
21
|
+
no include/prepend, backend `.async` factory, or automatic compatibility shim.
|
|
22
|
+
Update the receiver wherever an application previously called an async method.
|
|
23
|
+
The clients are public Beta API; their `async/` directories do not introduce
|
|
24
|
+
`Phronomy::Async` or feature-specific `Async` namespaces.
|
|
25
|
+
|
|
26
|
+
## Complete offline example
|
|
27
|
+
|
|
28
|
+
This example uses an intentionally simple local embedding adapter to demonstrate
|
|
29
|
+
the API. It needs no provider key, network request, or database server. The
|
|
30
|
+
implementation supplies only `embed`; Phronomy supplies asynchronous execution.
|
|
31
|
+
Run it against a checkout containing P3, not the previously released 0.27.0 gem.
|
|
32
|
+
|
|
33
|
+
```ruby runnable
|
|
34
|
+
require "phronomy"
|
|
35
|
+
require "json"
|
|
36
|
+
|
|
37
|
+
class KeywordEmbeddings < Phronomy::Embeddings::Base
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def perform_embed(text, cancellation_token = nil)
|
|
41
|
+
cancellation_token&.raise_if_cancelled!
|
|
42
|
+
words = text.downcase
|
|
43
|
+
[words.include?("ruby") ? 1.0 : 0.0,
|
|
44
|
+
words.include?("python") ? 1.0 : 0.0]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
store = Phronomy::VectorStore::InMemory.new(dimension: 2)
|
|
50
|
+
embeddings = KeywordEmbeddings.new
|
|
51
|
+
async_store = Phronomy::VectorStore::AsyncClient.new(backend: store)
|
|
52
|
+
async_embeddings = Phronomy::Embeddings::AsyncClient.new(adapter: embeddings)
|
|
53
|
+
|
|
54
|
+
{"ruby" => "Ruby guide", "python" => "Python guide"}.each do |id, title|
|
|
55
|
+
async_embeddings.embed_async(title).flat_map do |vector|
|
|
56
|
+
async_store.add_async(id: id, embedding: vector, metadata: {title: title})
|
|
57
|
+
end.wait_result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
matches = async_embeddings.embed_async("Ruby").flat_map do |vector|
|
|
61
|
+
async_store.search_async(query_embedding: vector, k: 1)
|
|
62
|
+
end.wait_result
|
|
63
|
+
puts JSON.generate(matches)
|
|
64
|
+
ensure
|
|
65
|
+
Phronomy.reset_runtime!
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The result contains the `ruby` document. The application waits only outside
|
|
70
|
+
worker callbacks; `flat_map` returns the next TaskResult without blocking a
|
|
71
|
+
worker on another operation. `Tools::VectorSearch` continues to call synchronous
|
|
72
|
+
`embed` and `search` inside its existing execution boundary, so this migration
|
|
73
|
+
does not add nested offload work to that Tool.
|
|
74
|
+
|
|
75
|
+
## Execution and cancellation
|
|
76
|
+
|
|
77
|
+
Both clients accept optional `pool:` injection. Usually omit it. Construction
|
|
78
|
+
starts no Runtime, and each operation resolves the current default pool. An
|
|
79
|
+
explicit pool remains caller-owned across Runtime resets; it must provide the
|
|
80
|
+
existing OffloadPool submit contract, including its timer service for timeouts.
|
|
81
|
+
|
|
82
|
+
Queue admission uses `on_full: :raise`. A full or stopped pool raises immediately;
|
|
83
|
+
an accepted operation returns the original TaskResult. Timeout includes queue
|
|
84
|
+
wait. Cancellation or timeout before worker pickup skips the operation; after
|
|
85
|
+
execution starts it settles the result without forcibly stopping I/O or undoing
|
|
86
|
+
backend effects. These are the existing OffloadPool semantics.
|
|
87
|
+
|
|
88
|
+
Synchronous RBS signatures accept the structural `_CancellationSignal`
|
|
89
|
+
interface, which requires only `raise_if_cancelled!`. Existing
|
|
90
|
+
`Concurrency::CancellationToken` objects still satisfy it. Async RBS signatures
|
|
91
|
+
live in `sig/phronomy/vector_store/async/async_client.rbs` and
|
|
92
|
+
`sig/phronomy/embeddings/async/async_client.rbs` and retain the full CancellationToken
|
|
93
|
+
and TaskResult contract needed by the execution pool. Do not substitute a
|
|
94
|
+
minimal synchronous signal for an async operation's full token.
|
|
95
|
+
|
|
96
|
+
## Source ownership
|
|
97
|
+
|
|
98
|
+
The public class names below are unchanged. Their implementation files move:
|
|
99
|
+
|
|
100
|
+
| Public class | New path under `lib/phronomy/` |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `VectorStore::InMemory` | `vector_store/backends/in_memory.rb` |
|
|
103
|
+
| `VectorStore::Pgvector` | `vector_store/backends/pgvector.rb` |
|
|
104
|
+
| `VectorStore::RedisSearch` | `vector_store/backends/redis_search.rb` |
|
|
105
|
+
| `VectorStore::Embeddings::RubyLLMEmbeddings` | `embeddings/backends/ruby_llm_embeddings.rb` |
|
|
106
|
+
|
|
107
|
+
Use `require "phronomy"` and the public constants. Direct partial-load paths are
|
|
108
|
+
not the public loading contract; no require shim is kept in the contract
|
|
109
|
+
directories. Backend Contracts have no execution or implementation dependency.
|
|
110
|
+
P4 Storage/ContentStore work remains pending.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Ordered event delivery
|
|
2
|
+
|
|
3
|
+
Load the application integration explicitly:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "phronomy"
|
|
7
|
+
require "phronomy/integrations/ordered_event_delivery"
|
|
8
|
+
|
|
9
|
+
Phronomy::Integrations::OrderedEventDelivery.open(
|
|
10
|
+
capacity: 256, batch_size: 32, flush_timeout: 30,
|
|
11
|
+
deliver: ->(payload) { send_to_application_transport(payload) }
|
|
12
|
+
) do |delivery|
|
|
13
|
+
agent = MyAgent.new(on_event: ->(event) {
|
|
14
|
+
payload = application_payload(event)
|
|
15
|
+
delivery.publish(payload) if payload
|
|
16
|
+
})
|
|
17
|
+
agent.stream("Hello")
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The block runs on the external caller, and its result is returned after drain.
|
|
22
|
+
`open` rejects EventLoop callers before executing their block. Publication only
|
|
23
|
+
snapshots and queues plain data; it does not wait for capacity or send network
|
|
24
|
+
traffic. The existing Blocking API supplies a worker only while draining. The
|
|
25
|
+
integration creates no scheduler and never shuts down the shared Runtime/pool.
|
|
26
|
+
|
|
27
|
+
The app owns authentication, destination, authorization, Rails executor wrapping,
|
|
28
|
+
event-to-payload conversion, and any token coalescing. `prepare_batch:` optionally
|
|
29
|
+
receives an immutable Array of snapshots on the drain worker and returns an Array
|
|
30
|
+
of payloads to send in order. Its default is no transformation. Example 18 passes
|
|
31
|
+
its own TokenEventBatch, preserving adjacent-token coalescing with equal metadata.
|
|
32
|
+
|
|
33
|
+
Capacity limits the waiting queue, excluding an already extracted batch. Multiple
|
|
34
|
+
publishers are ordered by queue admission, not by thread start time. Hashes,
|
|
35
|
+
Arrays, Strings and scalar plain values are copied into immutable snapshots;
|
|
36
|
+
cycles, non-finite numbers and arbitrary objects fail publication.
|
|
37
|
+
|
|
38
|
+
On overflow/invalid publication, acceptance closes and the failure remains visible
|
|
39
|
+
to final drain even if an Agent listener merely logs its exception. Previously
|
|
40
|
+
accepted work drains if transport is healthy. A failed batch conversion, delivery
|
|
41
|
+
or worker admission also closes acceptance; remaining work may not be delivered.
|
|
42
|
+
There is no silent drop-as-success, automatic retry or durable delivery guarantee.
|
|
43
|
+
|
|
44
|
+
`close_and_wait` can be called by multiple external threads. It waits for physical
|
|
45
|
+
drain completion, not just TaskResult settlement. A timeout ends only the wait;
|
|
46
|
+
a network operation can still complete afterwards. Configure transport timeouts
|
|
47
|
+
in the transport itself. Network failure does not prove that nothing was sent.
|
|
48
|
+
|
|
49
|
+
If application execution fails, its original exception, message and backtrace
|
|
50
|
+
remain primary after cleanup. A simultaneous delivery failure is reported through
|
|
51
|
+
the configured Phronomy logger. Without a logger there is no durable diagnostic
|
|
52
|
+
guarantee; logger failure falls back to stderr without replacing the primary error.
|
|
53
|
+
If only delivery fails, that failure is raised. The small final-answer-only Job
|
|
54
|
+
needs no queue and is unchanged.
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[ADR-058](decisions/058-neutral-storage-primitives.md) defines the current
|
|
4
4
|
extension contract. This is an intentional breaking replacement of the old
|
|
5
|
-
fixed-repository Backend SPI. Application
|
|
6
|
-
`
|
|
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).
|
|
7
10
|
|
|
8
11
|
## Ownership and composition
|
|
9
12
|
|
|
@@ -13,9 +16,10 @@ record types, active-execution policy, content digest algorithm or watermark API
|
|
|
13
16
|
|
|
14
17
|
Agent, Team, Workflow and ContentStore declare their own resource schemas.
|
|
15
18
|
`PersistenceComposition::StorageSchema` gathers those declarations and
|
|
16
|
-
`PersistenceComposition
|
|
17
|
-
|
|
18
|
-
|
|
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.
|
|
19
23
|
`ContentStore::StoredContents` owns SHA-256 identity and digest verification.
|
|
20
24
|
The Agent-owned `Watermark` composes guarded revision and stream-head conditions.
|
|
21
25
|
|
|
@@ -41,11 +45,10 @@ view.check!(guards: guards, conditions: conditions)
|
|
|
41
45
|
|
|
42
46
|
The backend declares `spi_version: 2` and true values for `atomic_resources`,
|
|
43
47
|
`record_cas`, `stream_cas`, `conditional_unique`, `guarded_checks` and
|
|
44
|
-
`nested_savepoints`. Persistence validates
|
|
45
|
-
resource declarations before
|
|
46
|
-
|
|
47
|
-
adapter
|
|
48
|
-
and `optimistic_revision`, derived by composition from these primitives.
|
|
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.
|
|
49
52
|
|
|
50
53
|
A root handle routes to the current transaction on the same backend/thread.
|
|
51
54
|
A bound view and all its handles expire on commit or rollback and reject use from
|
|
@@ -139,12 +142,24 @@ stale revisions and ordinary constraint conflicts remain `ConflictError`.
|
|
|
139
142
|
meanings; `TransactionError` identifies invalid scope use. The removed
|
|
140
143
|
`ActiveExecutionConflictError` and `Storage::Repositories` have no aliases.
|
|
141
144
|
|
|
142
|
-
The following sections describe the
|
|
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).
|
|
143
155
|
|
|
144
156
|
## Contents repository
|
|
145
157
|
|
|
146
|
-
`ContentStore::StoredContents < ContentStore::Base` supplies
|
|
147
|
-
|
|
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.
|
|
148
163
|
|
|
149
164
|
Required primitive surface:
|
|
150
165
|
|
|
@@ -159,7 +174,7 @@ Required semantics:
|
|
|
159
174
|
- content is immutable and content-addressed;
|
|
160
175
|
- writing identical bytes is idempotent and returns the same content ID;
|
|
161
176
|
- `fetch` returns a binary `String` isolated from caller mutation;
|
|
162
|
-
- a missing content ID raises `
|
|
177
|
+
- a missing content ID through `persistence.contents` raises `Persistence::NotFoundError`;
|
|
163
178
|
- one content ID must never resolve to different bytes; a digest-integrity
|
|
164
179
|
violation raises `ContentStore::IntegrityError`.
|
|
165
180
|
|
|
@@ -361,8 +376,9 @@ Array of supported values
|
|
|
361
376
|
Hash with String/Symbol keys and supported values
|
|
362
377
|
```
|
|
363
378
|
|
|
364
|
-
If a value cannot be represented,
|
|
365
|
-
|
|
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
|
|
366
382
|
keys after decoding; `WorkflowRunner` deliberately accepts String and Symbol keys
|
|
367
383
|
and normalizes them when comparing durable snapshots.
|
|
368
384
|
|
|
@@ -391,16 +407,17 @@ differs, raise `ConflictError`. On success return `true`.
|
|
|
391
407
|
The operation must not return a replacement AgentRoot or Journal. A mismatch is a
|
|
392
408
|
conflict, not a request to reload/merge mutable state.
|
|
393
409
|
|
|
394
|
-
When used inside `
|
|
410
|
+
When used inside `Agent::Store#transaction`, a SQL backend should perform the
|
|
395
411
|
watermark check in the same database transaction as the subsequent durable
|
|
396
412
|
write.
|
|
397
413
|
|
|
398
414
|
## Transaction contract
|
|
399
415
|
|
|
400
|
-
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:
|
|
401
418
|
|
|
402
419
|
```ruby
|
|
403
|
-
|
|
420
|
+
stores.agent.transaction do |tx|
|
|
404
421
|
tx.assert_agent_watermark!(...)
|
|
405
422
|
content_ref = tx.contents.put_text("...")
|
|
406
423
|
tx.journals.append(...)
|
|
@@ -493,7 +510,7 @@ require "phronomy"
|
|
|
493
510
|
require "phronomy/testing/persistence_contract"
|
|
494
511
|
|
|
495
512
|
RSpec.describe MyPersistenceBackend do
|
|
496
|
-
let(:
|
|
513
|
+
let(:stores) { Phronomy::PersistenceComposition.build(backend: described_class.new(...)) }
|
|
497
514
|
|
|
498
515
|
it_behaves_like "a persistence content store"
|
|
499
516
|
it_behaves_like "an Agent repository"
|
|
@@ -507,7 +524,7 @@ RSpec.describe MyPersistenceBackend do
|
|
|
507
524
|
end
|
|
508
525
|
```
|
|
509
526
|
|
|
510
|
-
`
|
|
527
|
+
`PersistenceComposition.in_memory` is run through the same shipped contract source in
|
|
511
528
|
Phronomy CI. The files under `spec/support/shared_examples/` are compatibility
|
|
512
529
|
require wrappers only; the authoritative shared-example implementations live
|
|
513
530
|
under `lib/phronomy/testing/persistence_contract/` so the core suite and external
|
|
@@ -292,7 +292,7 @@ remain durable conflict defense rather than distributed ownership.
|
|
|
292
292
|
## Tool execution modes
|
|
293
293
|
|
|
294
294
|
The default dispatch helper is the private
|
|
295
|
-
`Phronomy::
|
|
295
|
+
`Phronomy::Tool::ToolExecutor`, colocated with Capability
|
|
296
296
|
Base. Agent ToolInvocation supplies Runtime and admission policy for the
|
|
297
297
|
standard path, and owns authorization and logical result handling. Custom
|
|
298
298
|
`call_async` implementations keep the public Tool protocol. This ownership is
|
|
@@ -577,10 +577,35 @@ TaskResult reports only caller-facing settlement.
|
|
|
577
577
|
## EventLoop metrics
|
|
578
578
|
|
|
579
579
|
`Phronomy::Metrics.snapshot` also reports EventLoop queue depth and lag values.
|
|
580
|
+
|
|
581
|
+
Metrics and Diagnostics only observe resources already owned by the default
|
|
582
|
+
Runtime. Calling `Metrics.snapshot`, `Diagnostics.snapshot` or `Diagnostics.dump`
|
|
583
|
+
does not initialize a Runtime, pool, EventLoop or timer. Missing resources report
|
|
584
|
+
zero values, including `offload_pool_size: 0` when no default pool exists. Once a
|
|
585
|
+
pool exists, this field remains its configured capacity, not a live-worker count.
|
|
586
|
+
Named pools are not aggregated into the default-pool metrics.
|
|
587
|
+
|
|
588
|
+
Retained resources remain observable during and after shutdown, including
|
|
589
|
+
incomplete cleanup. Observation does not reopen admission or change a shutdown
|
|
590
|
+
result. After the default Runtime is reset, missing resources again report zero;
|
|
591
|
+
the next snapshot uses any newly initialized default Runtime. Counters are sampled
|
|
592
|
+
independently and do not constitute an atomic cross-resource snapshot.
|
|
580
593
|
Use these to distinguish worker saturation from EventLoop backlog/latency.
|
|
581
594
|
|
|
582
595
|
## Shutdown
|
|
583
596
|
|
|
597
|
+
Pool creation and registration close atomically before the shutdown resource set
|
|
598
|
+
is captured. Accepted continuations can still acquire resources during Runtime's
|
|
599
|
+
draining phase. Once resource shutdown begins, all pools close admission before
|
|
600
|
+
any pool waits for its workers.
|
|
601
|
+
|
|
602
|
+
The stop-phase monotonic deadline is shared by EventLoop and every pool join.
|
|
603
|
+
A join timeout does not stop a synchronous worker. Runtime reports incomplete
|
|
604
|
+
cleanup while any worker remains, even if that worker's TaskResult has already
|
|
605
|
+
been cancelled or timed out. It does not forcefully interrupt worker threads.
|
|
606
|
+
See [R8 unit19](architecture/r8-unit19.md) and its
|
|
607
|
+
[migration notes](migrations/r8-unit19.md) for the shutdown budget and result rules.
|
|
608
|
+
|
|
584
609
|
`Runtime#shutdown` is terminal for that Runtime. It drains/terminates the
|
|
585
610
|
Runtime-owned EventLoop, then closes pools and timers according to the Runtime
|
|
586
611
|
shutdown contract.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# A bounded Workflow completion recipe
|
|
2
|
+
|
|
3
|
+
This is a **legacy manual-signal recipe**, retained for compatibility and its
|
|
4
|
+
bounded-lifetime tests. New code should use
|
|
5
|
+
[`task_state`](architecture/dx05-workflow-task-state.md), which owns completion
|
|
6
|
+
correlation and failure propagation for operations started by a Workflow state.
|
|
7
|
+
The helper below is application code and is not a Phronomy integration API.
|
|
8
|
+
Its limitations do not describe the new task-state API.
|
|
9
|
+
|
|
10
|
+
Use the application-local function below only for **one live waiting stage, with
|
|
11
|
+
no restart, reentry or replacement while its operation is pending**. It separates
|
|
12
|
+
result mapping (application logic) from the repetitive notification plumbing.
|
|
13
|
+
For repeated/resumed stages, the app must put an operation ID in every success
|
|
14
|
+
AND error payload and check it with a receiver-side transition `guard:` before
|
|
15
|
+
applying the payload. Signal admission alone is not that check. The example
|
|
16
|
+
below intentionally does not claim that wider guarantee.
|
|
17
|
+
|
|
18
|
+
```ruby runnable
|
|
19
|
+
def forward_completion(task, workflow:, workflow_instance_id:, event:, on_delivery_error:)
|
|
20
|
+
task.on_complete do |payload, operation_error|
|
|
21
|
+
delivery_error = nil
|
|
22
|
+
begin
|
|
23
|
+
accepted = workflow.signal(
|
|
24
|
+
workflow_instance_id: workflow_instance_id, event: event,
|
|
25
|
+
payload: operation_error ? {error: operation_error} : payload
|
|
26
|
+
)
|
|
27
|
+
delivery_error = Phronomy::Error.new("Workflow did not accept #{event}") unless accepted
|
|
28
|
+
rescue => error
|
|
29
|
+
delivery_error = error
|
|
30
|
+
end
|
|
31
|
+
on_delivery_error.call(delivery_error) if delivery_error
|
|
32
|
+
end
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class RecipeAnswerAgent < Phronomy::Agent::Base
|
|
37
|
+
agent_definition id: "bounded-completion-recipe", version: 1
|
|
38
|
+
model "gpt-4o-mini"
|
|
39
|
+
instructions "Answer briefly."
|
|
40
|
+
end
|
|
41
|
+
class CompletionState
|
|
42
|
+
include Phronomy::WorkflowContext
|
|
43
|
+
field :question, type: :replace
|
|
44
|
+
field :answer, type: :replace
|
|
45
|
+
field :error_message, type: :replace
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
agent = RecipeAnswerAgent.new
|
|
49
|
+
errors = Queue.new
|
|
50
|
+
workflow = nil
|
|
51
|
+
workflow = Phronomy::Workflow.define(CompletionState) do
|
|
52
|
+
initial :asking
|
|
53
|
+
state :asking
|
|
54
|
+
state :done
|
|
55
|
+
entry :asking, ->(state) {
|
|
56
|
+
task = agent.invoke_async(state.question).map { |result| {answer: result.fetch(:output)} }
|
|
57
|
+
forward_completion(task, workflow: workflow,
|
|
58
|
+
workflow_instance_id: state.workflow_instance_id, event: :answer_ready,
|
|
59
|
+
on_delivery_error: ->(error) { errors << error })
|
|
60
|
+
state
|
|
61
|
+
}
|
|
62
|
+
transition from: :asking, on: :answer_ready, to: :done,
|
|
63
|
+
action: ->(state, event) {
|
|
64
|
+
error = event.payload[:error]
|
|
65
|
+
error ? state.merge(error_message: error.message) : state.merge(answer: event.payload.fetch(:answer))
|
|
66
|
+
}
|
|
67
|
+
transition from: :done, to: :__finish__
|
|
68
|
+
end
|
|
69
|
+
final = workflow.invoke({question: "Explain completion handles"},
|
|
70
|
+
config: {workflow_instance_id: "bounded-recipe"})
|
|
71
|
+
puts final.error_message || final.answer
|
|
72
|
+
raise errors.pop unless errors.empty?
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`forward_completion` returns nil after registering one callback. Already-completed
|
|
76
|
+
results are notified immediately. Two registrations are two subscriptions; the
|
|
77
|
+
application avoids duplicates. No task state is changed, no retry is attempted,
|
|
78
|
+
and no callback waits for execution, storage or network delivery.
|
|
79
|
+
|
|
80
|
+
Operation failures and cancellation notify the Workflow with `{error: error}`.
|
|
81
|
+
A false signal result and a raised signal exception each notify the required
|
|
82
|
+
`on_delivery_error` callback once. A failure of that callback follows TaskResult's
|
|
83
|
+
existing logger policy; it is not retried and does not rewrite the task result.
|
|
84
|
+
The application observes delivery errors and chooses cancellation/recovery.
|
|
85
|
+
|
|
86
|
+
Signal acceptance does not guarantee transition completion or a durable save.
|
|
87
|
+
Incomplete tasks retain their subscriptions until settlement under the existing
|
|
88
|
+
TaskResult lifecycle. The caller still owns operation cancellation and lifetime;
|
|
89
|
+
this recipe installs no Runtime shutdown hook or global subscription registry.
|
|
90
|
+
|
|
91
|
+
See [the full application recipe](application-recipes.md#workflow-completion-and-failures)
|
|
92
|
+
for the same failure behavior without extracting this application-local function.
|