omnibase_infra 0.3.1__py3-none-any.whl → 0.4.0__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 (117) 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/errors/__init__.py +4 -0
  6. omnibase_infra/errors/error_infra.py +60 -0
  7. omnibase_infra/handlers/__init__.py +3 -0
  8. omnibase_infra/handlers/handler_slack_webhook.py +426 -0
  9. omnibase_infra/handlers/models/__init__.py +14 -0
  10. omnibase_infra/handlers/models/enum_alert_severity.py +36 -0
  11. omnibase_infra/handlers/models/model_slack_alert.py +24 -0
  12. omnibase_infra/handlers/models/model_slack_alert_payload.py +77 -0
  13. omnibase_infra/handlers/models/model_slack_alert_result.py +73 -0
  14. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +29 -20
  15. omnibase_infra/mixins/__init__.py +14 -0
  16. omnibase_infra/mixins/mixin_node_introspection.py +42 -20
  17. omnibase_infra/mixins/mixin_postgres_error_response.py +314 -0
  18. omnibase_infra/mixins/mixin_postgres_op_executor.py +298 -0
  19. omnibase_infra/models/__init__.py +3 -0
  20. omnibase_infra/models/discovery/model_dependency_spec.py +1 -0
  21. omnibase_infra/models/discovery/model_discovered_capabilities.py +1 -1
  22. omnibase_infra/models/discovery/model_introspection_config.py +28 -1
  23. omnibase_infra/models/discovery/model_introspection_performance_metrics.py +1 -0
  24. omnibase_infra/models/discovery/model_introspection_task_config.py +1 -0
  25. omnibase_infra/{nodes/effects/models → models}/model_backend_result.py +22 -6
  26. omnibase_infra/models/projection/__init__.py +11 -0
  27. omnibase_infra/models/projection/model_contract_projection.py +170 -0
  28. omnibase_infra/models/projection/model_topic_projection.py +148 -0
  29. omnibase_infra/models/runtime/__init__.py +4 -0
  30. omnibase_infra/models/runtime/model_resolved_dependencies.py +116 -0
  31. omnibase_infra/nodes/contract_registry_reducer/__init__.py +5 -0
  32. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +6 -5
  33. omnibase_infra/nodes/contract_registry_reducer/contract_registration_event_router.py +689 -0
  34. omnibase_infra/nodes/contract_registry_reducer/reducer.py +9 -26
  35. omnibase_infra/nodes/effects/__init__.py +1 -1
  36. omnibase_infra/nodes/effects/models/__init__.py +6 -4
  37. omnibase_infra/nodes/effects/models/model_registry_response.py +1 -1
  38. omnibase_infra/nodes/effects/protocol_consul_client.py +1 -1
  39. omnibase_infra/nodes/effects/protocol_postgres_adapter.py +1 -1
  40. omnibase_infra/nodes/effects/registry_effect.py +1 -1
  41. omnibase_infra/nodes/node_contract_persistence_effect/__init__.py +101 -0
  42. omnibase_infra/nodes/node_contract_persistence_effect/contract.yaml +490 -0
  43. omnibase_infra/nodes/node_contract_persistence_effect/handlers/__init__.py +74 -0
  44. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_cleanup_topics.py +217 -0
  45. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_contract_upsert.py +242 -0
  46. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_deactivate.py +194 -0
  47. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_heartbeat.py +243 -0
  48. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_mark_stale.py +208 -0
  49. omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_topic_update.py +298 -0
  50. omnibase_infra/nodes/node_contract_persistence_effect/models/__init__.py +15 -0
  51. omnibase_infra/nodes/node_contract_persistence_effect/models/model_persistence_result.py +52 -0
  52. omnibase_infra/nodes/node_contract_persistence_effect/node.py +131 -0
  53. omnibase_infra/nodes/node_contract_persistence_effect/registry/__init__.py +27 -0
  54. omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +251 -0
  55. omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +8 -12
  56. omnibase_infra/nodes/node_registry_effect/models/__init__.py +2 -2
  57. omnibase_infra/nodes/node_slack_alerter_effect/__init__.py +33 -0
  58. omnibase_infra/nodes/node_slack_alerter_effect/contract.yaml +291 -0
  59. omnibase_infra/nodes/node_slack_alerter_effect/node.py +106 -0
  60. omnibase_infra/projectors/__init__.py +6 -0
  61. omnibase_infra/projectors/projection_reader_contract.py +1301 -0
  62. omnibase_infra/runtime/__init__.py +12 -0
  63. omnibase_infra/runtime/baseline_subscriptions.py +13 -6
  64. omnibase_infra/runtime/contract_dependency_resolver.py +455 -0
  65. omnibase_infra/runtime/contract_registration_event_router.py +500 -0
  66. omnibase_infra/runtime/db/__init__.py +4 -0
  67. omnibase_infra/runtime/db/models/__init__.py +15 -10
  68. omnibase_infra/runtime/db/models/model_db_operation.py +40 -0
  69. omnibase_infra/runtime/db/models/model_db_param.py +24 -0
  70. omnibase_infra/runtime/db/models/model_db_repository_contract.py +40 -0
  71. omnibase_infra/runtime/db/models/model_db_return.py +26 -0
  72. omnibase_infra/runtime/db/models/model_db_safety_policy.py +32 -0
  73. omnibase_infra/runtime/emit_daemon/event_registry.py +34 -22
  74. omnibase_infra/runtime/event_bus_subcontract_wiring.py +63 -23
  75. omnibase_infra/runtime/intent_execution_router.py +430 -0
  76. omnibase_infra/runtime/models/__init__.py +6 -0
  77. omnibase_infra/runtime/models/model_contract_registry_config.py +41 -0
  78. omnibase_infra/runtime/models/model_intent_execution_summary.py +79 -0
  79. omnibase_infra/runtime/models/model_runtime_config.py +8 -0
  80. omnibase_infra/runtime/protocols/__init__.py +16 -0
  81. omnibase_infra/runtime/protocols/protocol_intent_executor.py +107 -0
  82. omnibase_infra/runtime/publisher_topic_scoped.py +16 -11
  83. omnibase_infra/runtime/registry_policy.py +29 -15
  84. omnibase_infra/runtime/request_response_wiring.py +793 -0
  85. omnibase_infra/runtime/service_kernel.py +295 -8
  86. omnibase_infra/runtime/service_runtime_host_process.py +149 -5
  87. omnibase_infra/runtime/util_version.py +5 -1
  88. omnibase_infra/schemas/schema_latency_baseline.sql +135 -0
  89. omnibase_infra/services/contract_publisher/config.py +4 -4
  90. omnibase_infra/services/contract_publisher/service.py +8 -5
  91. omnibase_infra/services/observability/injection_effectiveness/__init__.py +67 -0
  92. omnibase_infra/services/observability/injection_effectiveness/config.py +295 -0
  93. omnibase_infra/services/observability/injection_effectiveness/consumer.py +1461 -0
  94. omnibase_infra/services/observability/injection_effectiveness/models/__init__.py +32 -0
  95. omnibase_infra/services/observability/injection_effectiveness/models/model_agent_match.py +79 -0
  96. omnibase_infra/services/observability/injection_effectiveness/models/model_context_utilization.py +118 -0
  97. omnibase_infra/services/observability/injection_effectiveness/models/model_latency_breakdown.py +107 -0
  98. omnibase_infra/services/observability/injection_effectiveness/models/model_pattern_utilization.py +46 -0
  99. omnibase_infra/services/observability/injection_effectiveness/writer_postgres.py +596 -0
  100. omnibase_infra/services/registry_api/models/__init__.py +25 -0
  101. omnibase_infra/services/registry_api/models/model_contract_ref.py +44 -0
  102. omnibase_infra/services/registry_api/models/model_contract_view.py +81 -0
  103. omnibase_infra/services/registry_api/models/model_response_contracts.py +50 -0
  104. omnibase_infra/services/registry_api/models/model_response_topics.py +50 -0
  105. omnibase_infra/services/registry_api/models/model_topic_summary.py +57 -0
  106. omnibase_infra/services/registry_api/models/model_topic_view.py +63 -0
  107. omnibase_infra/services/registry_api/routes.py +205 -6
  108. omnibase_infra/services/registry_api/service.py +528 -1
  109. omnibase_infra/utils/__init__.py +7 -0
  110. omnibase_infra/utils/util_db_error_context.py +292 -0
  111. omnibase_infra/validation/infra_validators.py +3 -1
  112. omnibase_infra/validation/validation_exemptions.yaml +65 -0
  113. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/METADATA +3 -3
  114. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/RECORD +117 -58
  115. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/WHEEL +0 -0
  116. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/entry_points.txt +0 -0
  117. {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -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"
@@ -0,0 +1,74 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Handlers for NodeContractPersistenceEffect operations.
4
+
5
+ This package contains the handlers for the NodeContractPersistenceEffect node,
6
+ following the declarative node pattern where PostgreSQL operations are
7
+ encapsulated in dedicated handler classes.
8
+
9
+ Available Handlers:
10
+ HandlerPostgresContractUpsert: Upsert contract record handler.
11
+ HandlerPostgresTopicUpdate: Update topic routing handler.
12
+ HandlerPostgresMarkStale: Batch mark stale contracts handler.
13
+ HandlerPostgresHeartbeat: Update heartbeat timestamp handler.
14
+ HandlerPostgresDeactivate: Deactivate contract handler.
15
+ HandlerPostgresCleanupTopics: Cleanup topic references handler.
16
+
17
+ Architecture:
18
+ These handlers are used by NodeContractPersistenceEffect to execute
19
+ PostgreSQL operations based on intents from ContractRegistryReducer.
20
+ Each handler is responsible for:
21
+ - Operation timing and observability
22
+ - Error sanitization for security
23
+ - Structured result construction
24
+ - Retry logic per retry_policy configuration
25
+
26
+ Shared Patterns:
27
+ All handlers share a common error handling pattern:
28
+ - TimeoutError/InfraTimeoutError: Returns *_TIMEOUT_ERROR code
29
+ - InfraAuthenticationError: Returns *_AUTH_ERROR code (non-retriable)
30
+ - InfraConnectionError: Returns *_CONNECTION_ERROR code (retriable)
31
+ - RepositoryExecutionError: Returns operation-specific error code
32
+ - Exception: Returns *_UNKNOWN_ERROR code
33
+
34
+ Each handler sanitizes errors via sanitize_error_message() to prevent
35
+ credential exposure in logs and error responses.
36
+
37
+ Related:
38
+ - NodeContractPersistenceEffect: Parent effect node coordinating handlers
39
+ - ContractRegistryReducer: Source of intents
40
+ - OMN-1845: Implementation ticket
41
+ - OMN-1653: ContractRegistryReducer ticket
42
+ """
43
+
44
+ from __future__ import annotations
45
+
46
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_cleanup_topics import (
47
+ HandlerPostgresCleanupTopics,
48
+ )
49
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_contract_upsert import (
50
+ HandlerPostgresContractUpsert,
51
+ )
52
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_deactivate import (
53
+ HandlerPostgresDeactivate,
54
+ )
55
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_heartbeat import (
56
+ HandlerPostgresHeartbeat,
57
+ )
58
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_mark_stale import (
59
+ HandlerPostgresMarkStale,
60
+ )
61
+ from omnibase_infra.nodes.node_contract_persistence_effect.handlers.handler_postgres_topic_update import (
62
+ HandlerPostgresTopicUpdate,
63
+ normalize_topic_for_storage,
64
+ )
65
+
66
+ __all__: list[str] = [
67
+ "HandlerPostgresCleanupTopics",
68
+ "HandlerPostgresContractUpsert",
69
+ "HandlerPostgresDeactivate",
70
+ "HandlerPostgresHeartbeat",
71
+ "HandlerPostgresMarkStale",
72
+ "HandlerPostgresTopicUpdate",
73
+ "normalize_topic_for_storage",
74
+ ]