phronomy 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +4 -0
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +89 -0
- data/docs/architecture/before-llm-input.md +6 -0
- data/docs/architecture/context-management.md +30 -0
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-metadata-and-values.md +80 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +8 -2
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +27 -4
- data/docs/architecture/refactoring-closure.md +334 -0
- data/docs/architecture/remaining-refactoring-plan.md +374 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/tool-schema-recording-gap.md +50 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
- data/docs/decisions/032-storage-backend-composition.md +80 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
- data/docs/decisions/040-configuration-default-composition.md +94 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +78 -0
- data/docs/decisions/README.md +35 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/features.md +9 -3
- data/docs/getting-started.md +4 -4
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
- data/docs/migrations/handoff-runner-multi-agent.md +35 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/persistence-backends.md +162 -204
- data/docs/runtime-and-concurrency.md +117 -32
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/agent/async_event_api.rb +2 -2
- data/lib/phronomy/agent/base.rb +51 -241
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context/capability/base.rb +67 -26
- data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
- data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
- data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
- data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
- data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
- data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
- data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
- data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
- data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
- data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
- data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
- data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
- data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
- data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
- data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
- data/lib/phronomy/agent/persistence/codec.rb +358 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
- data/lib/phronomy/agent/persistence/queries.rb +61 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
- data/lib/phronomy/agent/persistence/watermark.rb +27 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
- data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
- data/lib/phronomy/agent/selection/candidate.rb +1 -1
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
- data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
- data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
- data/lib/phronomy/configuration/global_configuration.rb +26 -0
- data/lib/phronomy/content_store/storage_schema.rb +11 -0
- data/lib/phronomy/content_store/stored_contents.rb +43 -0
- data/lib/phronomy/engine/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
- data/lib/phronomy/engine/cancellation_error.rb +7 -0
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
- data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +158 -624
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
- data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +35 -28
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
- data/lib/phronomy/engine/runtime.rb +80 -84
- data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/engine/task_result.rb +4 -0
- data/lib/phronomy/engine/timeout_error.rb +7 -0
- data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
- data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
- data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
- data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
- data/lib/phronomy/llm_contract/transport_error.rb +7 -0
- data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
- data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
- data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
- data/lib/phronomy/persistence/api/persistence.rb +140 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
- data/lib/phronomy/persistence_composition/repositories.rb +77 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/storage/backend.rb +101 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/contract/tool_error.rb +7 -0
- data/lib/phronomy/tools/agent.rb +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
- data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
- data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +1 -1
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +0 -1
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +18 -0
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +10 -1
- metadata +230 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_executor.rb +0 -55
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/workflow_recovery.rb +0 -123
- /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
- /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
- /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
- /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -60,13 +60,6 @@ module Phronomy
|
|
|
60
60
|
end
|
|
61
61
|
private_class_method :__run_async
|
|
62
62
|
|
|
63
|
-
# Common admission binding for Agent and synchronous-work adapters.
|
|
64
|
-
# @api private
|
|
65
|
-
def self.__operation_binding(invocation_context:, cancellation_token:)
|
|
66
|
-
Concurrency::OperationBinding.new(invocation_context: invocation_context,
|
|
67
|
-
cancellation_token: cancellation_token)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
63
|
# A scoped observation copy. The source, its controls and its physical work
|
|
71
64
|
# keep their original ownership. Bind before adding this run's map/flat_map.
|
|
72
65
|
# @param source_result [TaskResult]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Internal lifecycle/delivery contract for feature-owned execution state.
|
|
5
|
+
# Receivers are retained by one EventLoop, not an application callback SPI.
|
|
6
|
+
# Normal mutation and delivery run only on that loop. idle? runs with its
|
|
7
|
+
# lifecycle lock held; it must not call Runtime or acquire that lock again.
|
|
8
|
+
# shutdown runs on a failing loop, or exclusively after a clean loop joins.
|
|
9
|
+
# @api private
|
|
10
|
+
class ExecutionReceiver
|
|
11
|
+
def self.for(event_loop)
|
|
12
|
+
event_loop.__execution_receiver(key: self) ||
|
|
13
|
+
event_loop.__register_execution_receiver(key: self, receiver: new(event_loop: event_loop))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Lookup only: neither a loop nor a receiver is created during inspection.
|
|
17
|
+
def self.existing_for(runtime)
|
|
18
|
+
runtime.__event_loop_if_initialized&.__execution_receiver(key: self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(event_loop:)
|
|
22
|
+
@event_loop = event_loop
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def __bound_to?(event_loop)
|
|
26
|
+
@event_loop.equal?(event_loop)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def idle?
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def deliver(message)
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def shutdown(error:)
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def session_retired(fsm_session_id, reason:)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def synchronize(&block)
|
|
47
|
+
@event_loop.__synchronize_execution_state(&block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def admit(&block)
|
|
51
|
+
@event_loop.__admit_execution(self, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def post_message(message, admission: false, completion: nil)
|
|
55
|
+
@event_loop.__post_execution(self, message, admission: admission, completion: completion)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def assert_event_loop_thread!
|
|
59
|
+
return if @event_loop.current?
|
|
60
|
+
|
|
61
|
+
raise Phronomy::Error,
|
|
62
|
+
"Phronomy-managed live execution state may only be mutated on EventLoop"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Shared terminal-state vocabulary and domain decisions for FSM execution.
|
|
5
|
+
# This protocol does not depend on a particular runner or session instance.
|
|
6
|
+
# @api private
|
|
7
|
+
module FSMProtocol
|
|
8
|
+
FINISH = :__end__
|
|
9
|
+
|
|
10
|
+
# A domain policy permits completion, reports failure, or retires a session
|
|
11
|
+
# without settling its caller. It never carries live session authority.
|
|
12
|
+
TerminalDecision = Data.define(:action, :error)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
require "securerandom"
|
|
4
6
|
|
|
5
7
|
module Phronomy
|
|
@@ -9,6 +11,8 @@ module Phronomy
|
|
|
9
11
|
# FSMSession owns FSM execution only; it does not own external TaskResult handles,
|
|
10
12
|
# activity tokens, callback correlation, or domain-specific stale-event policy.
|
|
11
13
|
class FSMSession
|
|
14
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
15
|
+
|
|
12
16
|
class IdentityReservation
|
|
13
17
|
attr_reader :fsm_session_id
|
|
14
18
|
|
|
@@ -30,6 +34,8 @@ module Phronomy
|
|
|
30
34
|
private_constant :IdentityReservation
|
|
31
35
|
|
|
32
36
|
class EventSink
|
|
37
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
38
|
+
|
|
33
39
|
attr_reader :fsm_session_id
|
|
34
40
|
|
|
35
41
|
def initialize(event_loop:)
|
|
@@ -60,8 +66,6 @@ module Phronomy
|
|
|
60
66
|
end
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
FINISH = WorkflowRunner::FINISH
|
|
64
|
-
|
|
65
69
|
attr_reader :id, :context, :event_sink
|
|
66
70
|
|
|
67
71
|
# Returns the live current Workflow phase. During an active FSM transition
|
|
@@ -95,7 +99,7 @@ module Phronomy
|
|
|
95
99
|
context_metadata: {},
|
|
96
100
|
event_sink: nil,
|
|
97
101
|
identity_reservation: nil,
|
|
98
|
-
|
|
102
|
+
terminal_policy: nil
|
|
99
103
|
)
|
|
100
104
|
@id = if identity_reservation
|
|
101
105
|
unless identity_reservation.is_a?(IdentityReservation)
|
|
@@ -123,7 +127,7 @@ module Phronomy
|
|
|
123
127
|
@resume_event = resume_event
|
|
124
128
|
@resume_phase = resume_phase
|
|
125
129
|
@stable_observer = stable_observer
|
|
126
|
-
@
|
|
130
|
+
@terminal_policy = terminal_policy
|
|
127
131
|
@terminal_lifecycle_state = :running
|
|
128
132
|
@pending_terminal_type = nil
|
|
129
133
|
@pending_terminal_notify_stable = false
|
|
@@ -160,14 +164,13 @@ module Phronomy
|
|
|
160
164
|
def handle(event)
|
|
161
165
|
return if @done
|
|
162
166
|
|
|
163
|
-
if event
|
|
164
|
-
|
|
167
|
+
if @terminal_policy&.handles?(event)
|
|
168
|
+
handle_terminal_event(event)
|
|
165
169
|
return
|
|
166
170
|
end
|
|
167
171
|
|
|
168
|
-
# Once terminal
|
|
169
|
-
#
|
|
170
|
-
# FSMSession can advance its terminal lifecycle.
|
|
172
|
+
# Once a terminal decision is pending, ordinary events cannot advance
|
|
173
|
+
# the state machine. Only the injected policy can permit terminalization.
|
|
171
174
|
return unless @terminal_lifecycle_state == :running
|
|
172
175
|
|
|
173
176
|
context_disposition = apply_context_event(event)
|
|
@@ -261,7 +264,7 @@ module Phronomy
|
|
|
261
264
|
end
|
|
262
265
|
|
|
263
266
|
def advance_or_halt
|
|
264
|
-
return finish! if @current_state == FINISH
|
|
267
|
+
return finish! if @current_state == FSMProtocol::FINISH
|
|
265
268
|
|
|
266
269
|
# A wait state is the logical end of this Workflow execution segment. Its
|
|
267
270
|
# public stable-state notification is therefore part of terminalization
|
|
@@ -324,7 +327,7 @@ module Phronomy
|
|
|
324
327
|
def finish!(notify_stable: false)
|
|
325
328
|
request_terminal!(
|
|
326
329
|
:finished,
|
|
327
|
-
phase:
|
|
330
|
+
phase: FSMProtocol::FINISH,
|
|
328
331
|
notify_stable: notify_stable
|
|
329
332
|
)
|
|
330
333
|
end
|
|
@@ -339,13 +342,13 @@ module Phronomy
|
|
|
339
342
|
apply_context_metadata!(phase: phase)
|
|
340
343
|
@pending_terminal_type = terminal_type
|
|
341
344
|
@pending_terminal_notify_stable = notify_stable
|
|
342
|
-
unless @
|
|
345
|
+
unless @terminal_policy
|
|
343
346
|
complete_terminal!(terminal_type)
|
|
344
347
|
return
|
|
345
348
|
end
|
|
346
349
|
|
|
347
|
-
@terminal_lifecycle_state = :
|
|
348
|
-
@
|
|
350
|
+
@terminal_lifecycle_state = :awaiting_terminal
|
|
351
|
+
@terminal_policy.start(
|
|
349
352
|
terminal_type: terminal_type,
|
|
350
353
|
context: @ctx,
|
|
351
354
|
event_sink: @event_sink
|
|
@@ -354,31 +357,35 @@ module Phronomy
|
|
|
354
357
|
finish_with_error(error)
|
|
355
358
|
end
|
|
356
359
|
|
|
357
|
-
def
|
|
358
|
-
return unless @terminal_lifecycle_state == :
|
|
360
|
+
def handle_terminal_event(event)
|
|
361
|
+
return unless @terminal_lifecycle_state == :awaiting_terminal
|
|
359
362
|
|
|
360
|
-
|
|
361
|
-
|
|
363
|
+
decision = @terminal_policy.decision_for(event)
|
|
364
|
+
case decision.action
|
|
365
|
+
when :complete
|
|
362
366
|
complete_terminal!(@pending_terminal_type)
|
|
363
|
-
when :
|
|
364
|
-
finish_with_error(
|
|
365
|
-
|
|
366
|
-
)
|
|
367
|
-
when :outcome_unknown
|
|
368
|
-
@done = true
|
|
369
|
-
@terminal_lifecycle_state = :recovery_required
|
|
370
|
-
post_recovery_required_event(result.error)
|
|
367
|
+
when :fail
|
|
368
|
+
finish_with_error(decision.error)
|
|
369
|
+
when :retire
|
|
370
|
+
retire_without_result!(decision.error)
|
|
371
371
|
else
|
|
372
372
|
raise Phronomy::Error,
|
|
373
|
-
"unknown
|
|
373
|
+
"unknown FSM terminal decision: #{decision.action.inspect}"
|
|
374
374
|
end
|
|
375
375
|
end
|
|
376
376
|
|
|
377
|
-
def
|
|
377
|
+
def retire_without_result!(error)
|
|
378
378
|
@done = true
|
|
379
|
+
@terminal_lifecycle_state = :retired
|
|
380
|
+
post_recovery_required_event(error)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def complete_terminal!(terminal_type)
|
|
379
384
|
@terminal_lifecycle_state =
|
|
380
385
|
(terminal_type == :halted) ? :halted : :completed
|
|
386
|
+
# Keep the error path open until the observer returns successfully.
|
|
381
387
|
notify_stable_state! if @pending_terminal_notify_stable
|
|
388
|
+
@done = true
|
|
382
389
|
post_terminal_event(terminal_type, @ctx)
|
|
383
390
|
end
|
|
384
391
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
require_relative "runtime/timer_queue"
|
|
4
6
|
require_relative "runtime/shutdown_result"
|
|
5
7
|
require_relative "runtime/timer_service"
|
|
6
|
-
require_relative "runtime/agent_ownership_registry"
|
|
7
|
-
require_relative "runtime/team_ownership_registry"
|
|
8
8
|
|
|
9
9
|
module Phronomy
|
|
10
10
|
class Runtime
|
|
11
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
12
|
+
|
|
11
13
|
@instance_mutex = Mutex.new
|
|
12
14
|
|
|
13
15
|
class << self
|
|
@@ -64,9 +66,8 @@ module Phronomy
|
|
|
64
66
|
@pool_registry = Phronomy::Concurrency::PoolRegistry.new(
|
|
65
67
|
timer_queue_provider: -> { timer_queue }
|
|
66
68
|
)
|
|
67
|
-
@
|
|
68
|
-
@
|
|
69
|
-
@team_ownership_registry = TeamOwnershipRegistry.new
|
|
69
|
+
@shutdown_participants = {}
|
|
70
|
+
@shutdown_participant_error = nil
|
|
70
71
|
@lifecycle_mutex = Mutex.new
|
|
71
72
|
@shutdown_mutex = Mutex.new
|
|
72
73
|
@state = :running
|
|
@@ -103,83 +104,40 @@ module Phronomy
|
|
|
103
104
|
@timer_service.timer_queue
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
#
|
|
107
|
-
# execution state remains inside EventLoop and is never exposed to callers.
|
|
108
|
-
# @api private
|
|
109
|
-
def __agent_execution_owner(execution_id)
|
|
110
|
-
loop_instance = @lifecycle_mutex.synchronize { @event_loop }
|
|
111
|
-
loop_instance&.agent_execution_owner(execution_id)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# @api private
|
|
115
|
-
def __team_owner(id, klass:, create:, persistence:, &block)
|
|
116
|
-
@team_ownership_registry.fetch(id, klass: klass, create: create, persistence: persistence, &block)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# @api private
|
|
120
|
-
def __get_team(id, klass:)
|
|
121
|
-
@team_ownership_registry.get(id.to_s, klass: klass)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# @api private
|
|
125
|
-
def __create_agent(agent_id, expected_class:, &block)
|
|
126
|
-
@agent_ownership_registry.create(agent_id, expected_class: expected_class, &block)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# @api private
|
|
130
|
-
def __load_agent(agent_id, expected_class:, &block)
|
|
131
|
-
@agent_ownership_registry.load(agent_id, expected_class: expected_class, &block)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# @api private
|
|
135
|
-
def __get_agent(agent_id, expected_class:)
|
|
136
|
-
@agent_ownership_registry.get(agent_id, expected_class: expected_class)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# @api private
|
|
140
|
-
def __agent_owned?(agent)
|
|
141
|
-
@agent_ownership_registry.owned?(agent)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# @api private
|
|
145
|
-
def __begin_agent_purge(agent)
|
|
146
|
-
@agent_ownership_registry.begin_purge(agent)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# @api private
|
|
150
|
-
def __complete_agent_purge(agent, token)
|
|
151
|
-
@agent_ownership_registry.complete_purge(agent, token)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# @api private
|
|
155
|
-
def __abort_agent_purge(agent, token)
|
|
156
|
-
@agent_ownership_registry.abort_purge(agent, token)
|
|
157
|
-
end
|
|
158
|
-
|
|
107
|
+
# Lookup only; inspection must not create an EventLoop during shutdown.
|
|
159
108
|
# @api private
|
|
160
|
-
def
|
|
161
|
-
@
|
|
109
|
+
def __event_loop_if_initialized
|
|
110
|
+
@lifecycle_mutex.synchronize { @event_loop }
|
|
162
111
|
end
|
|
163
112
|
|
|
113
|
+
# Shares one internal participant per key for this Runtime's lifetime.
|
|
114
|
+
# begin_draining must be idempotent, nonblocking and must not call Runtime:
|
|
115
|
+
# admission closes under the same lifecycle lock as registration.
|
|
116
|
+
# wait_until_idle receives an absolute monotonic deadline and returns a Boolean.
|
|
117
|
+
# Optional after_runtime_shutdown runs outside the lifecycle lock only after
|
|
118
|
+
# all waits succeed, EventLoop stops, and pools/timer finish shutdown. It must
|
|
119
|
+
# be idempotent, short, and perform no I/O. Exceptions mark cleanup incomplete.
|
|
120
|
+
# This is a shutdown contract, not an application extension point.
|
|
164
121
|
# @api private
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
122
|
+
def __register_shutdown_participant(key:, participant:)
|
|
123
|
+
unless participant.respond_to?(:begin_draining) && participant.respond_to?(:wait_until_idle)
|
|
124
|
+
raise ArgumentError, "shutdown participant must implement begin_draining and wait_until_idle"
|
|
125
|
+
end
|
|
169
126
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
127
|
+
@lifecycle_mutex.synchronize do
|
|
128
|
+
unless @state == :running
|
|
129
|
+
raise Phronomy::RuntimeShutdownError,
|
|
130
|
+
"Runtime is #{@state}; shutdown participants cannot be registered"
|
|
131
|
+
end
|
|
132
|
+
@shutdown_participants[key] ||= participant
|
|
176
133
|
end
|
|
177
|
-
@multi_agent_admissions.admit!(coordinator)
|
|
178
134
|
end
|
|
179
135
|
|
|
136
|
+
# Lookup only: never registers a participant or reopens its admission gate.
|
|
137
|
+
# Retained participants remain available during and after shutdown.
|
|
180
138
|
# @api private
|
|
181
|
-
def
|
|
182
|
-
@
|
|
139
|
+
def __shutdown_participant(key:)
|
|
140
|
+
@lifecycle_mutex.synchronize { @shutdown_participants[key] }
|
|
183
141
|
end
|
|
184
142
|
|
|
185
143
|
def event_loop
|
|
@@ -214,6 +172,7 @@ module Phronomy
|
|
|
214
172
|
|
|
215
173
|
@failure ||= error
|
|
216
174
|
@state = :failed
|
|
175
|
+
begin_draining_participants
|
|
217
176
|
end
|
|
218
177
|
end
|
|
219
178
|
|
|
@@ -232,17 +191,14 @@ module Phronomy
|
|
|
232
191
|
return @shutdown_result if @shutdown_result
|
|
233
192
|
|
|
234
193
|
drain_deadline = monotonic_now + timeout
|
|
235
|
-
loop_instance = @lifecycle_mutex.synchronize do
|
|
194
|
+
loop_instance, participants = @lifecycle_mutex.synchronize do
|
|
236
195
|
@state = :draining unless @state == :failed
|
|
237
|
-
|
|
196
|
+
begin_draining_participants
|
|
197
|
+
[@event_loop, @shutdown_participants.values]
|
|
238
198
|
end
|
|
239
199
|
loop_instance&.begin_draining
|
|
240
|
-
@agent_ownership_registry.begin_draining
|
|
241
|
-
@team_ownership_registry.begin_draining
|
|
242
200
|
|
|
243
|
-
|
|
244
|
-
agent_ownership_stable = @agent_ownership_registry.wait_until_stable(drain_deadline)
|
|
245
|
-
team_ownership_stable = @team_ownership_registry.wait_until_stable(drain_deadline)
|
|
201
|
+
participants_idle = wait_for_participants(participants, drain_deadline)
|
|
246
202
|
loop_idle = !loop_instance || loop_instance.wait_until_idle(drain_deadline)
|
|
247
203
|
|
|
248
204
|
@lifecycle_mutex.synchronize do
|
|
@@ -257,11 +213,15 @@ module Phronomy
|
|
|
257
213
|
end
|
|
258
214
|
|
|
259
215
|
subsystem_error = shutdown_pools_and_timer
|
|
260
|
-
|
|
216
|
+
participant_error = @lifecycle_mutex.synchronize { @shutdown_participant_error }
|
|
217
|
+
cleanup_complete = participants_idle && participant_error.nil? &&
|
|
218
|
+
loop_idle &&
|
|
261
219
|
(!loop_instance || !loop_instance.thread_alive?) &&
|
|
262
220
|
event_loop_status != :cancel_timeout &&
|
|
221
|
+
(!loop_instance || loop_instance.__receiver_cleanup_complete?) &&
|
|
263
222
|
subsystem_error.nil?
|
|
264
223
|
|
|
224
|
+
cleanup_complete = finalize_participants(participants) if cleanup_complete
|
|
265
225
|
failure = @lifecycle_mutex.synchronize { @failure } || subsystem_error
|
|
266
226
|
runtime_outcome = if failure || event_loop_status == :failed
|
|
267
227
|
:failed
|
|
@@ -269,9 +229,6 @@ module Phronomy
|
|
|
269
229
|
:terminated
|
|
270
230
|
end
|
|
271
231
|
|
|
272
|
-
@agent_ownership_registry.shutdown! if cleanup_complete
|
|
273
|
-
@team_ownership_registry.shutdown! if cleanup_complete
|
|
274
|
-
|
|
275
232
|
result = ShutdownResult.new(
|
|
276
233
|
runtime_outcome: runtime_outcome,
|
|
277
234
|
cleanup_status: cleanup_complete ? :complete : :incomplete,
|
|
@@ -290,6 +247,45 @@ module Phronomy
|
|
|
290
247
|
|
|
291
248
|
private
|
|
292
249
|
|
|
250
|
+
# Called with the lifecycle lock held; participants only close their gates.
|
|
251
|
+
def begin_draining_participants
|
|
252
|
+
@shutdown_participants.each_value do |participant|
|
|
253
|
+
participant.begin_draining
|
|
254
|
+
rescue => error
|
|
255
|
+
record_participant_failure(error)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# All gates are already closed. Wait without holding the lifecycle lock and
|
|
260
|
+
# give every participant the same deadline, even if an earlier wait fails.
|
|
261
|
+
def wait_for_participants(participants, deadline)
|
|
262
|
+
participants.map do |participant|
|
|
263
|
+
participant.wait_until_idle(deadline) == true
|
|
264
|
+
rescue => error
|
|
265
|
+
@lifecycle_mutex.synchronize { record_participant_failure(error) }
|
|
266
|
+
false
|
|
267
|
+
end.all?
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Finalize every participant even if one fails. Earlier releases are not
|
|
271
|
+
# rolled back, and a failure prevents replacement of the default Runtime.
|
|
272
|
+
def finalize_participants(participants)
|
|
273
|
+
participants.map do |participant|
|
|
274
|
+
participant.after_runtime_shutdown if participant.respond_to?(:after_runtime_shutdown)
|
|
275
|
+
true
|
|
276
|
+
rescue => error
|
|
277
|
+
@lifecycle_mutex.synchronize { record_participant_failure(error) }
|
|
278
|
+
false
|
|
279
|
+
end.all?
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Called with the lifecycle lock held. Failed hooks make cleanup uncertain.
|
|
283
|
+
def record_participant_failure(error)
|
|
284
|
+
@shutdown_participant_error ||= error
|
|
285
|
+
@failure ||= error
|
|
286
|
+
@state = :failed
|
|
287
|
+
end
|
|
288
|
+
|
|
293
289
|
def ensure_accepting_work!
|
|
294
290
|
current_state = @lifecycle_mutex.synchronize { @state }
|
|
295
291
|
return if %i[running draining].include?(current_state)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "event_loop_reentrancy_error"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
# Backward-compatible error class name for callers that still rescue the old
|
|
7
|
+
# scheduler-oriented exception. New code should use EventLoopReentrancyError.
|
|
8
|
+
class SchedulerReentrancyError < EventLoopReentrancyError; end
|
|
9
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
module Phronomy
|
|
4
6
|
# A thread-free asynchronous completion handle.
|
|
5
7
|
#
|
|
@@ -13,6 +15,8 @@ module Phronomy
|
|
|
13
15
|
# is supplied through the CancellationToken accepted by the API that created
|
|
14
16
|
# the TaskResult.
|
|
15
17
|
class TaskResult
|
|
18
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
19
|
+
|
|
16
20
|
STATES = %i[pending completed failed cancelled].freeze
|
|
17
21
|
TERMINAL_STATES = %i[completed failed cancelled].freeze
|
|
18
22
|
private_constant :TERMINAL_STATES
|