omnibase_infra 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl

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.
Files changed (72) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/enums/__init__.py +3 -0
  3. omnibase_infra/enums/enum_consumer_group_purpose.py +9 -0
  4. omnibase_infra/enums/enum_postgres_error_code.py +188 -0
  5. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +29 -20
  6. omnibase_infra/mixins/__init__.py +14 -0
  7. omnibase_infra/mixins/mixin_postgres_error_response.py +314 -0
  8. omnibase_infra/mixins/mixin_postgres_op_executor.py +298 -0
  9. omnibase_infra/models/__init__.py +3 -0
  10. omnibase_infra/{nodes/effects/models → models}/model_backend_result.py +22 -6
  11. omnibase_infra/models/projection/__init__.py +11 -0
  12. omnibase_infra/models/projection/model_contract_projection.py +170 -0
  13. omnibase_infra/models/projection/model_topic_projection.py +148 -0
  14. omnibase_infra/nodes/contract_registry_reducer/__init__.py +5 -0
  15. omnibase_infra/nodes/contract_registry_reducer/contract_registration_event_router.py +689 -0
  16. omnibase_infra/nodes/effects/__init__.py +1 -1
  17. omnibase_infra/nodes/effects/models/__init__.py +6 -4
  18. omnibase_infra/nodes/effects/models/model_registry_response.py +1 -1
  19. omnibase_infra/nodes/effects/protocol_consul_client.py +1 -1
  20. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +1 -1
  21. omnibase_infra/nodes/effects/registry_effect.py +1 -1
  22. omnibase_infra/nodes/node_contract_persistence_effect/__init__.py +101 -0
  23. omnibase_infra/nodes/node_contract_persistence_effect/contract.yaml +490 -0
  24. omnibase_infra/nodes/node_contract_persistence_effect/handlers/__init__.py +74 -0
  25. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_cleanup_topics.py +217 -0
  26. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_contract_upsert.py +242 -0
  27. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_deactivate.py +194 -0
  28. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_heartbeat.py +243 -0
  29. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_mark_stale.py +208 -0
  30. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_topic_update.py +298 -0
  31. omnibase_infra/nodes/node_contract_persistence_effect/models/__init__.py +15 -0
  32. omnibase_infra/nodes/node_contract_persistence_effect/models/model_persistence_result.py +52 -0
  33. omnibase_infra/nodes/node_contract_persistence_effect/node.py +114 -0
  34. omnibase_infra/nodes/node_contract_persistence_effect/registry/__init__.py +27 -0
  35. omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +220 -0
  36. omnibase_infra/nodes/node_registry_effect/models/__init__.py +2 -2
  37. omnibase_infra/projectors/__init__.py +6 -0
  38. omnibase_infra/projectors/projection_reader_contract.py +1301 -0
  39. omnibase_infra/runtime/__init__.py +5 -0
  40. omnibase_infra/runtime/contract_registration_event_router.py +500 -0
  41. omnibase_infra/runtime/db/__init__.py +4 -0
  42. omnibase_infra/runtime/db/models/__init__.py +15 -10
  43. omnibase_infra/runtime/db/models/model_db_operation.py +40 -0
  44. omnibase_infra/runtime/db/models/model_db_param.py +24 -0
  45. omnibase_infra/runtime/db/models/model_db_repository_contract.py +40 -0
  46. omnibase_infra/runtime/db/models/model_db_return.py +26 -0
  47. omnibase_infra/runtime/db/models/model_db_safety_policy.py +32 -0
  48. omnibase_infra/runtime/intent_execution_router.py +430 -0
  49. omnibase_infra/runtime/models/__init__.py +6 -0
  50. omnibase_infra/runtime/models/model_contract_registry_config.py +41 -0
  51. omnibase_infra/runtime/models/model_intent_execution_summary.py +79 -0
  52. omnibase_infra/runtime/models/model_runtime_config.py +8 -0
  53. omnibase_infra/runtime/protocols/__init__.py +16 -0
  54. omnibase_infra/runtime/protocols/protocol_intent_executor.py +107 -0
  55. omnibase_infra/runtime/request_response_wiring.py +785 -0
  56. omnibase_infra/runtime/service_kernel.py +295 -8
  57. omnibase_infra/services/registry_api/models/__init__.py +25 -0
  58. omnibase_infra/services/registry_api/models/model_contract_ref.py +44 -0
  59. omnibase_infra/services/registry_api/models/model_contract_view.py +81 -0
  60. omnibase_infra/services/registry_api/models/model_response_contracts.py +50 -0
  61. omnibase_infra/services/registry_api/models/model_response_topics.py +50 -0
  62. omnibase_infra/services/registry_api/models/model_topic_summary.py +57 -0
  63. omnibase_infra/services/registry_api/models/model_topic_view.py +63 -0
  64. omnibase_infra/services/registry_api/routes.py +205 -6
  65. omnibase_infra/services/registry_api/service.py +528 -1
  66. omnibase_infra/validation/infra_validators.py +3 -1
  67. omnibase_infra/validation/validation_exemptions.yaml +54 -0
  68. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/METADATA +3 -3
  69. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/RECORD +72 -34
  70. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/WHEEL +0 -0
  71. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/entry_points.txt +0 -0
  72. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -5,15 +5,17 @@
5
5
  This module exports models used by Effect layer nodes for external I/O operations.
6
6
 
7
7
  Available Models:
8
- - ModelBackendResult: Individual backend operation result
8
+ - ModelBackendResult: Individual backend operation result (re-exported)
9
9
  - ModelEffectIdempotencyConfig: Configuration for effect idempotency store
10
10
  - ModelRegistryRequest: Registry effect input request
11
11
  - ModelRegistryResponse: Dual-backend registry operation response
12
+
13
+ Note:
14
+ ModelBackendResult canonical location is omnibase_infra.models.model_backend_result.
15
+ Re-exported here for backward compatibility.
12
16
  """
13
17
 
14
- from omnibase_infra.nodes.effects.models.model_backend_result import (
15
- ModelBackendResult,
16
- )
18
+ from omnibase_infra.models.model_backend_result import ModelBackendResult
17
19
  from omnibase_infra.nodes.effects.models.model_effect_idempotency_config import (
18
20
  ModelEffectIdempotencyConfig,
19
21
  )
@@ -41,7 +41,7 @@ from uuid import UUID
41
41
  from pydantic import BaseModel, ConfigDict, Field
42
42
 
43
43
  from omnibase_infra.enums import EnumBackendType, EnumRegistryResponseStatus
44
- from omnibase_infra.nodes.effects.models.model_backend_result import (
44
+ from omnibase_infra.models.model_backend_result import (
45
45
  ModelBackendResult,
46
46
  )
47
47
 
@@ -21,7 +21,7 @@ from __future__ import annotations
21
21
 
22
22
  from typing import Protocol, runtime_checkable
23
23
 
24
- from omnibase_infra.nodes.effects.models import ModelBackendResult
24
+ from omnibase_infra.models import ModelBackendResult
25
25
 
26
26
 
27
27
  @runtime_checkable
@@ -24,7 +24,7 @@ from uuid import UUID
24
24
 
25
25
  from omnibase_core.enums import EnumNodeKind
26
26
  from omnibase_core.models.primitives import ModelSemVer
27
- from omnibase_infra.nodes.effects.models import ModelBackendResult
27
+ from omnibase_infra.models import ModelBackendResult
28
28
 
29
29
 
30
30
  @runtime_checkable
@@ -100,7 +100,7 @@ from omnibase_infra.errors import (
100
100
  InfraConnectionError,
101
101
  InfraTimeoutError,
102
102
  )
103
- from omnibase_infra.nodes.effects.models.model_backend_result import (
103
+ from omnibase_infra.models.model_backend_result import (
104
104
  ModelBackendResult,
105
105
  )
106
106
  from omnibase_infra.nodes.effects.models.model_effect_idempotency_config import (
@@ -0,0 +1,101 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Node Contract Persistence Effect package - Declarative effect node for contract persistence.
4
+
5
+ This package provides NodeContractPersistenceEffect, a declarative effect node that
6
+ routes intents from ContractRegistryReducer to PostgreSQL handlers for contract
7
+ and topic persistence operations.
8
+
9
+ Architecture (OMN-1845):
10
+ This package follows the ONEX declarative node pattern:
11
+ - node.py: Declarative node shell extending NodeEffect
12
+ - handlers/: PostgreSQL operation handlers
13
+ - registry/: Infrastructure registry for dependency injection
14
+ - contract.yaml: Intent routing and I/O definitions
15
+
16
+ The node is 100% contract-driven with zero custom business logic in node.py.
17
+ All intent routing is defined in contract.yaml and handlers are resolved
18
+ via container dependency injection.
19
+
20
+ Node Type: EFFECT_GENERIC
21
+ Purpose: Execute PostgreSQL I/O operations based on intents from ContractRegistryReducer.
22
+
23
+ Implementation Details:
24
+ - Routes 6 intent types to specialized handlers
25
+ - Circuit breaker protection for PostgreSQL
26
+ - Error sanitization for security
27
+ - Retry policies for transient failures
28
+
29
+ Supported Intent Types:
30
+ - postgres.upsert_contract: Insert/update contract record
31
+ - postgres.update_topic: Update topic routing table
32
+ - postgres.mark_stale: Batch mark stale contracts
33
+ - postgres.update_heartbeat: Update heartbeat timestamp
34
+ - postgres.deactivate_contract: Soft delete contract
35
+ - postgres.cleanup_topic_references: Remove contract from topics
36
+
37
+ Handlers:
38
+ - HandlerPostgresContractUpsert: Contract upsert operations
39
+ - HandlerPostgresTopicUpdate: Topic routing updates
40
+ - HandlerPostgresMarkStale: Batch staleness marking
41
+ - HandlerPostgresHeartbeat: Heartbeat timestamp updates
42
+ - HandlerPostgresDeactivate: Contract deactivation
43
+ - HandlerPostgresCleanupTopics: Topic reference cleanup
44
+
45
+ Usage:
46
+ ```python
47
+ from omnibase_core.models.container import ModelONEXContainer
48
+ from omnibase_infra.nodes.node_contract_persistence_effect import (
49
+ NodeContractPersistenceEffect,
50
+ )
51
+
52
+ # Create via container injection
53
+ container = ModelONEXContainer()
54
+ effect = NodeContractPersistenceEffect(container)
55
+ ```
56
+
57
+ Related:
58
+ - contract.yaml: Intent routing definition
59
+ - node.py: Declarative node implementation
60
+ - handlers/: PostgreSQL operation handlers
61
+ - registry/: Infrastructure registry
62
+ - contract_registry_reducer/: Source of intents
63
+ - OMN-1845: Implementation ticket
64
+ - OMN-1653: ContractRegistryReducer ticket
65
+ """
66
+
67
+ from __future__ import annotations
68
+
69
+ # Export handlers
70
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers import (
71
+ HandlerPostgresCleanupTopics,
72
+ HandlerPostgresContractUpsert,
73
+ HandlerPostgresDeactivate,
74
+ HandlerPostgresHeartbeat,
75
+ HandlerPostgresMarkStale,
76
+ HandlerPostgresTopicUpdate,
77
+ )
78
+
79
+ # Export the declarative node
80
+ from omnibase_infra.nodes.node_contract_persistence_effect.node import (
81
+ NodeContractPersistenceEffect,
82
+ )
83
+
84
+ # Export registry
85
+ from omnibase_infra.nodes.node_contract_persistence_effect.registry import (
86
+ RegistryInfraContractPersistenceEffect,
87
+ )
88
+
89
+ __all__: list[str] = [
90
+ # Node
91
+ "NodeContractPersistenceEffect",
92
+ # Registry
93
+ "RegistryInfraContractPersistenceEffect",
94
+ # Handlers
95
+ "HandlerPostgresCleanupTopics",
96
+ "HandlerPostgresContractUpsert",
97
+ "HandlerPostgresDeactivate",
98
+ "HandlerPostgresHeartbeat",
99
+ "HandlerPostgresMarkStale",
100
+ "HandlerPostgresTopicUpdate",
101
+ ]
@@ -0,0 +1,490 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ #
4
+ # ONEX Node Contract
5
+ # Node: NodeContractPersistenceEffect
6
+ #
7
+ # This contract defines the interface for the Contract Persistence Effect node,
8
+ # which handles PostgreSQL persistence for contract registry intents emitted by
9
+ # ContractRegistryReducer (OMN-1653).
10
+ #
11
+ # Related Tickets:
12
+ # - OMN-1845: NodeContractPersistenceEffect implementation
13
+ # - OMN-1653: ContractRegistryReducer (source of intents)
14
+ # Contract identifiers
15
+ name: "node_contract_persistence_effect"
16
+ contract_name: "node_contract_persistence_effect"
17
+ node_name: "node_contract_persistence_effect"
18
+ contract_version:
19
+ major: 1
20
+ minor: 0
21
+ patch: 0
22
+ node_version:
23
+ major: 1
24
+ minor: 0
25
+ patch: 0
26
+ # Node type
27
+ node_type: "EFFECT_GENERIC"
28
+ # Description
29
+ description: >
30
+ Effect node for contract registry persistence. Routes intents from ContractRegistryReducer to PostgreSQL handlers for contract and topic management. Supports upsert, update, deactivate, and cleanup operations with circuit breaker protection and retry policies.
31
+
32
+ # Strongly typed I/O models
33
+ # Note: Input is ModelIntent with typed payloads from ContractRegistryReducer
34
+ input_model:
35
+ name: "ModelIntent"
36
+ module: "omnibase_core.models.reducer.model_intent"
37
+ description: "Intent model from ContractRegistryReducer with typed payload."
38
+ output_model:
39
+ name: "ModelPersistenceResult"
40
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.models"
41
+ description: "Output model containing PostgreSQL operation result."
42
+ # =============================================================================
43
+ # HANDLER ROUTING (Declarative Handler Dispatch)
44
+ # =============================================================================
45
+ # This section defines how intents are routed to PostgreSQL handlers.
46
+ # The routing strategy determines handler selection based on payload.intent_type
47
+ # (per ONEX handler routing standards).
48
+ #
49
+ # Design Rationale:
50
+ # - Single backend (PostgreSQL) with multiple operation types
51
+ # - Each handler is responsible for a specific PostgreSQL operation
52
+ # - Handlers are isolated for clear error handling and retry logic
53
+ #
54
+ # Execution Flow:
55
+ # 1. Receive ModelIntent with typed payload from ContractRegistryReducer
56
+ # 2. Extract payload.intent_type for routing
57
+ # 3. Route to matching handler based on intent_type
58
+ # 4. Execute PostgreSQL operation via handler
59
+ # 5. Return ModelPersistenceResult with operation status
60
+ #
61
+ # ┌─────────────────────────────────────────────────────────────────────────┐
62
+ # │ HANDLER ROUTING ARCHITECTURE │
63
+ # ├─────────────────────────────────────────────────────────────────────────┤
64
+ # │ │
65
+ # │ ┌────────────────────────┐ │
66
+ # │ │ ModelIntent │ │
67
+ # │ │ payload.intent_type │ │
68
+ # │ └───────────┬────────────┘ │
69
+ # │ │ │
70
+ # │ ▼ │
71
+ # │ ┌───────────────────────┐ │
72
+ # │ │ Handler Router │ routing_strategy: "payload_type_match" │
73
+ # │ │ (Contract-Driven) │ Matches payload.intent_type to handlers │
74
+ # │ └───────────┬───────────┘ │
75
+ # │ │ │
76
+ # │ ┌────────┼────────┬────────┬────────┬────────┐ │
77
+ # │ │ │ │ │ │ │ │
78
+ # │ ▼ ▼ ▼ ▼ ▼ ▼ │
79
+ # │ ┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ │
80
+ # │ │Upsert ││Topic ││Mark ││Heartbeat││Deact. ││Cleanup │ │
81
+ # │ │Contract││Update ││Stale ││Update ││Contract││Topics │ │
82
+ # │ │Handler ││Handler ││Handler ││Handler ││Handler ││Handler │ │
83
+ # │ └────┬───┘└────┬───┘└────┬───┘└────┬───┘└────┬───┘└────┬───┘ │
84
+ # │ │ │ │ │ │ │ │
85
+ # │ └─────────┴─────────┴────┬────┴─────────┴─────────┘ │
86
+ # │ │ │
87
+ # │ ▼ │
88
+ # │ ┌─────────────────────────────────────────────────────────────┐ │
89
+ # │ │ PostgreSQL Database │ │
90
+ # │ │ ┌────────────────┐ ┌────────────────┐ │ │
91
+ # │ │ │ contracts │ │ topics │ │ │
92
+ # │ │ └────────────────┘ └────────────────┘ │ │
93
+ # │ └─────────────────────────────────────────────────────────────┘ │
94
+ # │ │
95
+ # └────────────────────────────────────────────────────────────────────────┘
96
+ #
97
+ # Handler Responsibility:
98
+ # Each handler manages its own retry logic using the retry_policy defined
99
+ # in error_handling. The effect layer dispatches once per handler and
100
+ # delegates retry behavior to the handlers themselves.
101
+ # =============================================================================
102
+ handler_routing:
103
+ routing_strategy: "payload_type_match"
104
+ handlers:
105
+ # Upsert Contract - Insert or update contract record
106
+ # Triggered by contract-registered events from ContractRegistryReducer.
107
+ # Uses upsert semantics (ON CONFLICT DO UPDATE) for idempotency.
108
+ - payload_type: "postgres.upsert_contract"
109
+ handler:
110
+ name: "HandlerPostgresContractUpsert"
111
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_contract_upsert"
112
+ backend: "postgres"
113
+ description: "Insert or update contract record in contracts table"
114
+ input_model:
115
+ name: "ModelPayloadUpsertContract"
116
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_upsert_contract"
117
+ output_fields:
118
+ - success
119
+ - contract_id
120
+ - operation
121
+ - rows_affected
122
+ # Update Topic - Update topic routing table
123
+ # Triggered by contract-registered events with publish/subscribe declarations.
124
+ # Maintains topic-to-contract mapping for event routing.
125
+ - payload_type: "postgres.update_topic"
126
+ handler:
127
+ name: "HandlerPostgresTopicUpdate"
128
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_topic_update"
129
+ backend: "postgres"
130
+ description: "Update topic routing table with producer/consumer info"
131
+ input_model:
132
+ name: "ModelPayloadUpdateTopic"
133
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_update_topic"
134
+ output_fields:
135
+ - success
136
+ - topic_suffix
137
+ - direction
138
+ - rows_affected
139
+ # Mark Stale - Batch deactivate contracts that stopped heartbeating
140
+ # Triggered by runtime-tick events to detect unresponsive contracts.
141
+ # Deactivates (is_active=FALSE) contracts not seen within threshold.
142
+ - payload_type: "postgres.mark_stale"
143
+ handler:
144
+ name: "HandlerPostgresMarkStale"
145
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_mark_stale"
146
+ backend: "postgres"
147
+ description: "Batch mark contracts as stale based on last_seen_at threshold"
148
+ input_model:
149
+ name: "ModelPayloadMarkStale"
150
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_mark_stale"
151
+ output_fields:
152
+ - success
153
+ - contracts_marked
154
+ - stale_cutoff
155
+ # Update Heartbeat - Update last_seen_at timestamp
156
+ # Triggered by node-heartbeat events to track contract liveness.
157
+ # Lightweight update operation for high-frequency heartbeats.
158
+ - payload_type: "postgres.update_heartbeat"
159
+ handler:
160
+ name: "HandlerPostgresHeartbeat"
161
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_heartbeat"
162
+ backend: "postgres"
163
+ description: "Update contract heartbeat timestamp"
164
+ input_model:
165
+ name: "ModelPayloadUpdateHeartbeat"
166
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_update_heartbeat"
167
+ output_fields:
168
+ - success
169
+ - contract_id
170
+ - last_seen_at
171
+ # Deactivate Contract - Soft delete contract
172
+ # Triggered by contract-deregistered events for graceful shutdown.
173
+ # Marks contract as inactive without deleting the record.
174
+ - payload_type: "postgres.deactivate_contract"
175
+ handler:
176
+ name: "HandlerPostgresDeactivate"
177
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_deactivate"
178
+ backend: "postgres"
179
+ description: "Mark contract as inactive (soft delete)"
180
+ input_model:
181
+ name: "ModelPayloadDeactivateContract"
182
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_deactivate_contract"
183
+ output_fields:
184
+ - success
185
+ - contract_id
186
+ - deactivated_at
187
+ # Cleanup Topic References - Remove contract from topic arrays
188
+ # Triggered by contract-deregistered events to clean topic mappings.
189
+ # Removes contract_id from topics.contract_ids JSONB arrays.
190
+ - payload_type: "postgres.cleanup_topic_references"
191
+ handler:
192
+ name: "HandlerPostgresCleanupTopics"
193
+ module: "omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_cleanup_topics"
194
+ backend: "postgres"
195
+ description: "Remove contract from topic references"
196
+ input_model:
197
+ name: "ModelPayloadCleanupTopicReferences"
198
+ module: "omnibase_infra.nodes.contract_registry_reducer.models.model_payload_cleanup_topic_references"
199
+ output_fields:
200
+ - success
201
+ - contract_id
202
+ - topics_updated
203
+ # Execution configuration
204
+ execution_mode: "sequential"
205
+ partial_failure_handling: false
206
+ aggregation_strategy: "first_match"
207
+ # =============================================================================
208
+ # ERROR HANDLING (Circuit Breaker + Retry + Sanitization)
209
+ # =============================================================================
210
+ # Error handling configuration following ONEX infrastructure patterns.
211
+ # Uses circuit breakers for PostgreSQL protection and retry policies for
212
+ # transient failure recovery.
213
+ #
214
+ # Error Flow:
215
+ # 1. Operation fails -> Check if error is retriable
216
+ # 2. Retry with exponential backoff if retriable
217
+ # 3. After max retries, check circuit breaker state
218
+ # 4. Open circuit if failure threshold exceeded
219
+ # 5. Sanitize error before logging/returning
220
+ # =============================================================================
221
+ error_handling:
222
+ # Circuit breaker configuration
223
+ # Prevents cascading failures by stopping requests to failing PostgreSQL.
224
+ circuit_breaker:
225
+ enabled: true
226
+ failure_threshold: 5
227
+ reset_timeout_ms: 60000
228
+ half_open_max_requests: 3
229
+ per_backend: false
230
+ backends:
231
+ - name: "postgres"
232
+ failure_threshold: 5
233
+ reset_timeout_ms: 60000
234
+ # Retry policy with exponential backoff
235
+ # Handles transient PostgreSQL failures.
236
+ #
237
+ # ==========================================================================
238
+ # IMPORTANT: Effect Layer vs Handler Layer Retry Behavior
239
+ # ==========================================================================
240
+ #
241
+ # Effect Layer (NodeContractPersistenceEffect):
242
+ # - Dispatches to handlers EXACTLY ONCE per intent
243
+ # - Does NOT implement retry loops
244
+ # - Returns immediately after handler execution
245
+ # - Effective retry count at this layer: ZERO (0)
246
+ #
247
+ # Handler Layer (HandlerPostgres*):
248
+ # - OWNS retry logic using this retry_policy configuration
249
+ # - Implements exponential backoff for retriable errors
250
+ # - Returns final result after all retries exhausted
251
+ # - Effective retry count: 0 to max_retries
252
+ #
253
+ # ==========================================================================
254
+ retry_policy:
255
+ max_retries: 3
256
+ initial_delay_ms: 100
257
+ max_delay_ms: 5000
258
+ exponential_base: 2
259
+ retry_on:
260
+ - "InfraConnectionError"
261
+ - "InfraTimeoutError"
262
+ - "InfraUnavailableError"
263
+ - "RepositoryExecutionError"
264
+ # Error sanitization
265
+ # Removes sensitive information from error messages before logging.
266
+ error_sanitization:
267
+ enabled: true
268
+ utility: "omnibase_infra.utils.util_error_sanitization"
269
+ sanitize_patterns:
270
+ - "password"
271
+ - "secret"
272
+ - "token"
273
+ - "api_key"
274
+ - "connection_string"
275
+ # Error type definitions
276
+ error_types:
277
+ - name: "InfraConnectionError"
278
+ description: "Failed to connect to PostgreSQL"
279
+ recoverable: true
280
+ retry_strategy: "exponential_backoff"
281
+ - name: "InfraTimeoutError"
282
+ description: "PostgreSQL operation timed out"
283
+ recoverable: true
284
+ retry_strategy: "exponential_backoff"
285
+ - name: "InfraAuthenticationError"
286
+ description: "Authentication failed with PostgreSQL"
287
+ recoverable: false
288
+ retry_strategy: "none"
289
+ - name: "InfraUnavailableError"
290
+ description: "PostgreSQL is unavailable (circuit open)"
291
+ recoverable: true
292
+ retry_strategy: "exponential_backoff"
293
+ - name: "RepositoryExecutionError"
294
+ description: "PostgreSQL query execution failed"
295
+ recoverable: true
296
+ retry_strategy: "exponential_backoff"
297
+ # Error codes used in result models
298
+ error_codes:
299
+ # Connection errors
300
+ - code: "POSTGRES_CONNECTION_ERROR"
301
+ backend: "postgres"
302
+ operation: "all"
303
+ description: "Connection to PostgreSQL server failed"
304
+ retriable: true
305
+ - code: "POSTGRES_TIMEOUT_ERROR"
306
+ backend: "postgres"
307
+ operation: "all"
308
+ description: "PostgreSQL operation exceeded timeout"
309
+ retriable: true
310
+ - code: "POSTGRES_AUTH_ERROR"
311
+ backend: "postgres"
312
+ operation: "all"
313
+ description: "Authentication with PostgreSQL server failed"
314
+ retriable: false
315
+ # Operation-specific errors
316
+ - code: "POSTGRES_UPSERT_ERROR"
317
+ backend: "postgres"
318
+ operation: "upsert_contract"
319
+ description: "PostgreSQL upsert operation failed"
320
+ retriable: false
321
+ - code: "POSTGRES_TOPIC_UPDATE_ERROR"
322
+ backend: "postgres"
323
+ operation: "update_topic"
324
+ description: "PostgreSQL topic update failed"
325
+ retriable: false
326
+ - code: "POSTGRES_MARK_STALE_ERROR"
327
+ backend: "postgres"
328
+ operation: "mark_stale"
329
+ description: "PostgreSQL mark stale operation failed"
330
+ retriable: false
331
+ - code: "POSTGRES_HEARTBEAT_ERROR"
332
+ backend: "postgres"
333
+ operation: "update_heartbeat"
334
+ description: "PostgreSQL heartbeat update failed"
335
+ retriable: false
336
+ - code: "POSTGRES_DEACTIVATE_ERROR"
337
+ backend: "postgres"
338
+ operation: "deactivate_contract"
339
+ description: "PostgreSQL deactivation failed"
340
+ retriable: false
341
+ - code: "POSTGRES_CLEANUP_ERROR"
342
+ backend: "postgres"
343
+ operation: "cleanup_topic_references"
344
+ description: "PostgreSQL topic cleanup failed"
345
+ retriable: false
346
+ - code: "POSTGRES_UNKNOWN_ERROR"
347
+ backend: "postgres"
348
+ operation: "all"
349
+ description: "Unknown error during PostgreSQL operation"
350
+ retriable: false
351
+ # IO operations (EFFECT node specific)
352
+ io_operations:
353
+ - operation: "upsert_contract"
354
+ description: "Insert or update a contract record"
355
+ input_fields:
356
+ - contract_id
357
+ - node_name
358
+ - version_major
359
+ - version_minor
360
+ - version_patch
361
+ - contract_hash
362
+ - contract_yaml
363
+ - source_node_id
364
+ - is_active
365
+ - registered_at
366
+ - last_seen_at
367
+ output_fields:
368
+ - success
369
+ - contract_id
370
+ - operation
371
+ - rows_affected
372
+ - error
373
+ - error_code
374
+ - operation: "update_topic"
375
+ description: "Update topic routing information"
376
+ input_fields:
377
+ - topic_suffix
378
+ - direction
379
+ - contract_id
380
+ - node_name
381
+ - event_type
382
+ - last_seen_at
383
+ output_fields:
384
+ - success
385
+ - topic_suffix
386
+ - direction
387
+ - rows_affected
388
+ - error
389
+ - error_code
390
+ - operation: "mark_stale"
391
+ description: "Batch mark contracts as stale"
392
+ input_fields:
393
+ - stale_cutoff
394
+ - checked_at
395
+ output_fields:
396
+ - success
397
+ - contracts_marked
398
+ - stale_cutoff
399
+ - error
400
+ - error_code
401
+ - operation: "update_heartbeat"
402
+ description: "Update contract heartbeat timestamp"
403
+ input_fields:
404
+ - contract_id
405
+ - node_name
406
+ - source_node_id
407
+ - last_seen_at
408
+ - uptime_seconds
409
+ - sequence_number
410
+ output_fields:
411
+ - success
412
+ - contract_id
413
+ - last_seen_at
414
+ - error
415
+ - error_code
416
+ - operation: "deactivate_contract"
417
+ description: "Mark contract as inactive"
418
+ input_fields:
419
+ - contract_id
420
+ - node_name
421
+ - reason
422
+ - deactivated_at
423
+ output_fields:
424
+ - success
425
+ - contract_id
426
+ - deactivated_at
427
+ - error
428
+ - error_code
429
+ - operation: "cleanup_topic_references"
430
+ description: "Remove contract from topic references"
431
+ input_fields:
432
+ - contract_id
433
+ - node_name
434
+ - cleaned_at
435
+ output_fields:
436
+ - success
437
+ - contract_id
438
+ - topics_updated
439
+ - error
440
+ - error_code
441
+ # Dependencies (protocols this node requires)
442
+ dependencies:
443
+ - name: "protocol_postgres_adapter"
444
+ type: "protocol"
445
+ class_name: "ProtocolPostgresAdapter"
446
+ module: "omnibase_infra.adapters.protocol_postgres_adapter"
447
+ description: "PostgreSQL database operations"
448
+ - name: "protocol_circuit_breaker_aware"
449
+ type: "protocol"
450
+ class_name: "ProtocolCircuitBreakerAware"
451
+ module: "omnibase_infra.mixins.protocol_circuit_breaker_aware"
452
+ description: "Circuit breaker awareness for backend protection"
453
+ # Capabilities provided by this node
454
+ capabilities:
455
+ - name: "contract_persistence"
456
+ description: "Persist contract records to PostgreSQL"
457
+ - name: "topic_routing"
458
+ description: "Manage topic-to-contract routing mappings"
459
+ - name: "staleness_detection"
460
+ description: "Batch mark stale contracts based on heartbeat"
461
+ - name: "heartbeat_tracking"
462
+ description: "Track contract liveness via heartbeat updates"
463
+ - name: "soft_delete"
464
+ description: "Deactivate contracts without data loss"
465
+ - name: "circuit_breaker_protection"
466
+ description: "Protect PostgreSQL with circuit breaker pattern"
467
+ # Health check configuration
468
+ health_check:
469
+ enabled: true
470
+ endpoint: "/health"
471
+ interval_seconds: 30
472
+ backends:
473
+ - name: "postgres"
474
+ check_type: "connection"
475
+ timeout_ms: 5000
476
+ # Metadata
477
+ metadata:
478
+ author: "OmniNode Team"
479
+ license: "MIT"
480
+ created: "2025-02-02"
481
+ updated: "2025-02-02"
482
+ tags:
483
+ - effect
484
+ - contract-registry
485
+ - postgresql
486
+ - persistence
487
+ - circuit-breaker
488
+ related_tickets:
489
+ - "OMN-1845"
490
+ - "OMN-1653"