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,251 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Registry for NodeContractPersistenceEffect infrastructure dependencies.
4
+
5
+ This registry provides factory methods for creating NodeContractPersistenceEffect
6
+ instances with their required dependencies resolved from the container.
7
+
8
+ Following ONEX naming conventions:
9
+ - File: registry_infra_<node_name>.py
10
+ - Class: RegistryInfra<NodeName>
11
+
12
+ The registry serves as the entry point for creating properly configured
13
+ effect node instances, documenting required protocols, and providing
14
+ node metadata for introspection.
15
+
16
+ Related:
17
+ - contract.yaml: Node contract defining operations and dependencies
18
+ - node.py: Declarative node implementation
19
+ - handlers/: PostgreSQL operation handlers
20
+ - OMN-1845: Implementation ticket
21
+
22
+ .. versionadded:: 0.5.0
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import warnings
28
+ from typing import TYPE_CHECKING
29
+
30
+ if TYPE_CHECKING:
31
+ from omnibase_core.models.container.model_onex_container import ModelONEXContainer
32
+ from omnibase_infra.models.runtime.model_resolved_dependencies import (
33
+ ModelResolvedDependencies,
34
+ )
35
+ from omnibase_infra.nodes.node_contract_persistence_effect.node import (
36
+ NodeContractPersistenceEffect,
37
+ )
38
+
39
+
40
+ class RegistryInfraContractPersistenceEffect:
41
+ """Infrastructure registry for NodeContractPersistenceEffect.
42
+
43
+ Provides dependency resolution and factory methods for creating
44
+ properly configured NodeContractPersistenceEffect instances.
45
+
46
+ This registry follows the ONEX infrastructure registry pattern:
47
+ - Factory method for node creation with container injection
48
+ - Protocol requirements documentation for container validation
49
+ - Node type classification for routing decisions
50
+ - Capability listing for service discovery
51
+
52
+ Example:
53
+ >>> from omnibase_core.models.container import ModelONEXContainer
54
+ >>> from omnibase_infra.nodes.node_contract_persistence_effect.registry import (
55
+ ... RegistryInfraContractPersistenceEffect,
56
+ ... )
57
+ >>>
58
+ >>> # Create container with required protocols registered
59
+ >>> container = ModelONEXContainer()
60
+ >>> # ... register protocols ...
61
+ >>>
62
+ >>> # Create node instance via registry
63
+ >>> effect = RegistryInfraContractPersistenceEffect.create(container)
64
+
65
+ .. versionadded:: 0.5.0
66
+ """
67
+
68
+ @staticmethod
69
+ def create(
70
+ container: ModelONEXContainer,
71
+ dependencies: ModelResolvedDependencies | None = None,
72
+ ) -> NodeContractPersistenceEffect:
73
+ """Create a NodeContractPersistenceEffect instance with resolved dependencies.
74
+
75
+ Factory method that creates a fully configured NodeContractPersistenceEffect
76
+ using the provided ONEX container for dependency injection.
77
+
78
+ Args:
79
+ container: ONEX dependency injection container. Must have the
80
+ following protocols registered:
81
+ - ProtocolPostgresAdapter: PostgreSQL database operations
82
+ - ProtocolCircuitBreakerAware: Backend circuit breaker protection
83
+ dependencies: Optional pre-resolved protocol dependencies from
84
+ ContractDependencyResolver. If provided, the node uses these
85
+ instead of resolving from container. Part of OMN-1732 runtime
86
+ dependency injection.
87
+
88
+ Returns:
89
+ Configured NodeContractPersistenceEffect instance ready for operation.
90
+
91
+ Raises:
92
+ OnexError: If required protocols are not registered in container.
93
+
94
+ Example:
95
+ >>> container = ModelONEXContainer()
96
+ >>> container.register(ProtocolPostgresAdapter, postgres_adapter)
97
+ >>> effect = RegistryInfraContractPersistenceEffect.create(container)
98
+ >>>
99
+ >>> # With pre-resolved dependencies (OMN-1732)
100
+ >>> resolved = resolver.resolve(contract)
101
+ >>> effect = RegistryInfraContractPersistenceEffect.create(
102
+ ... container, dependencies=resolved
103
+ ... )
104
+
105
+ .. versionadded:: 0.5.0
106
+ .. versionchanged:: 0.6.0
107
+ Added optional ``dependencies`` parameter for constructor injection (OMN-1732).
108
+ """
109
+ from omnibase_infra.nodes.node_contract_persistence_effect.node import (
110
+ NodeContractPersistenceEffect,
111
+ )
112
+
113
+ return NodeContractPersistenceEffect(container, dependencies=dependencies)
114
+
115
+ @staticmethod
116
+ def get_required_protocols() -> list[str]:
117
+ """Get list of protocols required by this node.
118
+
119
+ Returns the protocol class names that must be registered in the
120
+ container before creating a NodeContractPersistenceEffect instance.
121
+
122
+ .. deprecated:: 0.6.0
123
+ Use contract.yaml dependencies field instead. This method will be
124
+ removed in a future version. The contract is now the single source
125
+ of truth for protocol requirements (OMN-1732).
126
+
127
+ Returns:
128
+ List of protocol class names required for node operation.
129
+
130
+ Example:
131
+ >>> protocols = RegistryInfraContractPersistenceEffect.get_required_protocols()
132
+ >>> for proto in protocols:
133
+ ... if not container.has(proto):
134
+ ... raise ConfigurationError(f"Missing: {proto}")
135
+
136
+ .. versionadded:: 0.5.0
137
+ """
138
+ warnings.warn(
139
+ "get_required_protocols() is deprecated. Use contract.yaml dependencies "
140
+ "field instead. The contract is the single source of truth for protocol "
141
+ "requirements (OMN-1732).",
142
+ DeprecationWarning,
143
+ stacklevel=2,
144
+ )
145
+ return [
146
+ "ProtocolPostgresAdapter",
147
+ "ProtocolCircuitBreakerAware",
148
+ ]
149
+
150
+ @staticmethod
151
+ def get_node_type() -> str:
152
+ """Get the node type classification.
153
+
154
+ Returns the ONEX node archetype for this node, used for
155
+ routing decisions and execution context selection.
156
+
157
+ Returns:
158
+ Node type string ("EFFECT").
159
+
160
+ Note:
161
+ EFFECT nodes perform external I/O operations and should
162
+ be treated as side-effecting by the runtime.
163
+
164
+ .. versionadded:: 0.5.0
165
+ """
166
+ return "EFFECT"
167
+
168
+ @staticmethod
169
+ def get_node_name() -> str:
170
+ """Get the canonical node name.
171
+
172
+ Returns:
173
+ The node name as defined in contract.yaml.
174
+
175
+ .. versionadded:: 0.5.0
176
+ """
177
+ return "node_contract_persistence_effect"
178
+
179
+ @staticmethod
180
+ def get_capabilities() -> list[str]:
181
+ """Get list of capabilities provided by this node.
182
+
183
+ Returns capability identifiers that can be used for service
184
+ discovery and feature detection.
185
+
186
+ Returns:
187
+ List of capability identifiers.
188
+
189
+ .. versionadded:: 0.5.0
190
+ """
191
+ return [
192
+ "contract_persistence",
193
+ "topic_routing",
194
+ "staleness_detection",
195
+ "heartbeat_tracking",
196
+ "soft_delete",
197
+ "circuit_breaker_protection",
198
+ ]
199
+
200
+ @staticmethod
201
+ def get_supported_operations() -> list[str]:
202
+ """Get list of operations supported by this node.
203
+
204
+ Returns:
205
+ List of operation identifiers as defined in contract.yaml.
206
+
207
+ .. versionadded:: 0.5.0
208
+ """
209
+ return [
210
+ "upsert_contract",
211
+ "update_topic",
212
+ "mark_stale",
213
+ "update_heartbeat",
214
+ "deactivate_contract",
215
+ "cleanup_topic_references",
216
+ ]
217
+
218
+ @staticmethod
219
+ def get_supported_intent_types() -> list[str]:
220
+ """Get list of intent types routed by this node.
221
+
222
+ Returns the payload.intent_type values that this effect node
223
+ can handle, matching ContractRegistryReducer output.
224
+
225
+ Returns:
226
+ List of intent type strings.
227
+
228
+ .. versionadded:: 0.5.0
229
+ """
230
+ return [
231
+ "postgres.upsert_contract",
232
+ "postgres.update_topic",
233
+ "postgres.mark_stale",
234
+ "postgres.update_heartbeat",
235
+ "postgres.deactivate_contract",
236
+ "postgres.cleanup_topic_references",
237
+ ]
238
+
239
+ @staticmethod
240
+ def get_backends() -> list[str]:
241
+ """Get list of backend types this node interacts with.
242
+
243
+ Returns:
244
+ List of backend identifiers.
245
+
246
+ .. versionadded:: 0.5.0
247
+ """
248
+ return ["postgres"]
249
+
250
+
251
+ __all__ = ["RegistryInfraContractPersistenceEffect"]
@@ -26,7 +26,7 @@ Thread Safety:
26
26
  Edge Case Behavior:
27
27
  The ``endpoints`` field validator explicitly handles the following cases:
28
28
  - ``None``: Raises ValueError (invalid input, not silently ignored)
29
- - Empty Mapping ``{}``: Logs warning and converts to empty tuple
29
+ - Empty Mapping ``{}``: Raises ValueError (use default=() for no endpoints)
30
30
  - Invalid types (int, str, list, etc.): Raises ValueError
31
31
  - Tuple: Passed through as-is
32
32
  - Non-empty Mapping: Converted to tuple of (key, value) pairs
@@ -36,7 +36,6 @@ Edge Case Behavior:
36
36
  from __future__ import annotations
37
37
 
38
38
  import logging
39
- import warnings
40
39
  from collections.abc import Mapping
41
40
  from types import MappingProxyType
42
41
  from uuid import UUID
@@ -153,7 +152,7 @@ class ModelPostgresIntentPayload(BaseModel):
153
152
 
154
153
  Edge Cases:
155
154
  - ``None``: Raises ValueError (explicit rejection)
156
- - Empty Mapping ``{}``: Logs warning, returns empty tuple
155
+ - Empty Mapping ``{}``: Raises ValueError (use default=() for no endpoints)
157
156
  - Empty tuple ``()``: Passed through (same as default)
158
157
  - Invalid types (list, int, str): Raises ValueError
159
158
  - Non-string keys/values: Raises ValueError (strict mode)
@@ -192,16 +191,13 @@ class ModelPostgresIntentPayload(BaseModel):
192
191
  return v # type: ignore[return-value] # NOTE: runtime type validated by Pydantic
193
192
  if isinstance(v, Mapping):
194
193
  if len(v) == 0:
195
- # Log warning for empty Mapping to help detect potentially missing data.
196
- # This is different from the default empty tuple - it's an explicit
197
- # empty Mapping input that gets coerced.
198
- warning_msg = (
199
- "Empty Mapping provided for endpoints, coercing to empty tuple. "
200
- "If this is intentional, consider using default=() instead."
194
+ # Explicit empty Mapping is rejected - use default=() instead.
195
+ # This prevents silent coercion that could mask invalid input.
196
+ raise ValueError(
197
+ "Empty Mapping provided for endpoints. "
198
+ "If no endpoints are needed, omit the field to use default=() "
199
+ "rather than passing an explicit empty Mapping."
201
200
  )
202
- logger.warning(warning_msg)
203
- warnings.warn(warning_msg, UserWarning, stacklevel=2)
204
- return ()
205
201
  # Validate and convert to tuple - strict mode requires string keys/values
206
202
  result: list[tuple[str, str]] = []
207
203
  for key, val in v.items():
@@ -21,9 +21,9 @@ Related:
21
21
 
22
22
  from __future__ import annotations
23
23
 
24
- # Re-export shared effect models for convenience
24
+ # Re-export shared models for convenience
25
+ from omnibase_infra.models import ModelBackendResult
25
26
  from omnibase_infra.nodes.effects.models import (
26
- ModelBackendResult,
27
27
  ModelEffectIdempotencyConfig,
28
28
  ModelRegistryRequest,
29
29
  ModelRegistryResponse,
@@ -0,0 +1,33 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Slack Alerter Effect Node - Declarative Slack webhook alerting.
4
+
5
+ This module exports the declarative NodeSlackAlerterEffect for sending
6
+ infrastructure alerts to Slack via webhooks.
7
+
8
+ Architecture:
9
+ This node follows the ONEX declarative pattern:
10
+ - DECLARATIVE effect driven by contract.yaml
11
+ - Zero custom routing logic - all behavior from handler_routing
12
+ - Lightweight shell that delegates to HandlerSlackWebhook
13
+ - Pattern: "Contract-driven, handlers wired externally"
14
+
15
+ Example:
16
+ >>> from omnibase_core.models.container import ModelONEXContainer
17
+ >>> from omnibase_infra.nodes.node_slack_alerter_effect import NodeSlackAlerterEffect
18
+ >>> from omnibase_infra.handlers import HandlerSlackWebhook
19
+ >>>
20
+ >>> container = ModelONEXContainer()
21
+ >>> node = NodeSlackAlerterEffect(container)
22
+ >>>
23
+ >>> # Handler receives dependencies via constructor
24
+ >>> handler = HandlerSlackWebhook()
25
+ >>> # result = await handler.handle(alert)
26
+
27
+ Related Tickets:
28
+ - OMN-1905: Add declarative Slack webhook handler to omnibase_infra
29
+ """
30
+
31
+ from omnibase_infra.nodes.node_slack_alerter_effect.node import NodeSlackAlerterEffect
32
+
33
+ __all__ = ["NodeSlackAlerterEffect"]
@@ -0,0 +1,291 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ #
4
+ # ONEX Node Contract
5
+ # Node: NodeSlackAlerterEffect
6
+ #
7
+ # This contract defines the interface for the Slack Alerter Effect node,
8
+ # which sends infrastructure alerts to Slack via webhooks. The node uses
9
+ # declarative operation routing to dispatch operations to the handler.
10
+ #
11
+ # Related Tickets:
12
+ # - OMN-1905: Add declarative Slack webhook handler to omnibase_infra
13
+ # Contract identifiers
14
+ name: "node_slack_alerter_effect"
15
+ contract_name: "node_slack_alerter_effect"
16
+ node_name: "node_slack_alerter_effect"
17
+ contract_version:
18
+ major: 1
19
+ minor: 0
20
+ patch: 0
21
+ node_version:
22
+ major: 1
23
+ minor: 0
24
+ patch: 0
25
+ # Node type
26
+ node_type: "EFFECT_GENERIC"
27
+ # Description
28
+ description: >
29
+ Effect node for Slack webhook alerting. Sends infrastructure alerts to Slack channels using incoming webhooks with Block Kit formatting, retry with exponential backoff, and rate limit handling. Uses declarative operation routing to dispatch to the HandlerSlackWebhook.
30
+
31
+ # Strongly typed I/O models
32
+ input_model:
33
+ name: "ModelSlackAlert"
34
+ module: "omnibase_infra.handlers.models.model_slack_alert"
35
+ description: "Input model containing alert severity, message, and optional details."
36
+ output_model:
37
+ name: "ModelSlackAlertResult"
38
+ module: "omnibase_infra.handlers.models.model_slack_alert"
39
+ description: "Output model containing delivery status, timing, and error information."
40
+ # =============================================================================
41
+ # HANDLER ROUTING (Declarative Handler Dispatch)
42
+ # =============================================================================
43
+ # This section defines how operations are routed to the Slack webhook handler.
44
+ # The routing strategy determines handler selection based on operation type.
45
+ #
46
+ # Design Rationale:
47
+ # - Single handler for all Slack operations
48
+ # - Block Kit formatting for rich messages
49
+ # - Retry with exponential backoff for resilience
50
+ # - Rate limit handling for graceful degradation
51
+ #
52
+ # Execution Flow:
53
+ # 1. Receive ModelSlackAlert with operation type
54
+ # 2. Route to HandlerSlackWebhook based on operation
55
+ # 3. Execute webhook delivery with retry logic
56
+ # 4. Return ModelSlackAlertResult with delivery status
57
+ # =============================================================================
58
+ handler_routing:
59
+ routing_strategy: "operation_match"
60
+ handlers:
61
+ # Send Alert - Formatted Block Kit message
62
+ # Sends a rich formatted alert with severity, message, and details.
63
+ - operation: "send_alert"
64
+ handler:
65
+ name: "HandlerSlackWebhook"
66
+ module: "omnibase_infra.handlers.handler_slack_webhook"
67
+ description: "Send formatted alert to Slack with Block Kit formatting"
68
+ output_fields:
69
+ - success
70
+ - duration_ms
71
+ - retry_count
72
+ # Send Message - Plain text message
73
+ # Sends a simple text message (uses same handler, alert formatting applied)
74
+ - operation: "send_message"
75
+ handler:
76
+ name: "HandlerSlackWebhook"
77
+ module: "omnibase_infra.handlers.handler_slack_webhook"
78
+ description: "Send message to Slack channel"
79
+ output_fields:
80
+ - success
81
+ - duration_ms
82
+ - retry_count
83
+ # =============================================================================
84
+ # ERROR HANDLING (Retry + Rate Limiting)
85
+ # =============================================================================
86
+ # Error handling configuration for Slack webhook operations.
87
+ # Uses retry with exponential backoff and handles rate limiting gracefully.
88
+ #
89
+ # Note: The handler implements its own retry logic internally rather than
90
+ # relying on external retry orchestration. This ensures consistent behavior
91
+ # and proper rate limit handling.
92
+ # =============================================================================
93
+ error_handling:
94
+ # Retry policy with exponential backoff
95
+ retry_policy:
96
+ max_retries: 3
97
+ initial_delay_ms: 1000
98
+ max_delay_ms: 4000
99
+ exponential_base: 2
100
+ retry_on:
101
+ - "SLACK_RATE_LIMITED"
102
+ - "SLACK_TIMEOUT"
103
+ - "SLACK_CONNECTION_ERROR"
104
+ - "SLACK_CLIENT_ERROR"
105
+ # Error type definitions
106
+ error_types:
107
+ - name: "SLACK_NOT_CONFIGURED"
108
+ description: "SLACK_WEBHOOK_URL environment variable not set"
109
+ recoverable: false
110
+ retry_strategy: "none"
111
+ - name: "SLACK_RATE_LIMITED"
112
+ description: "Slack returned HTTP 429 rate limit"
113
+ recoverable: true
114
+ retry_strategy: "exponential_backoff"
115
+ - name: "SLACK_TIMEOUT"
116
+ description: "Webhook request timed out"
117
+ recoverable: true
118
+ retry_strategy: "exponential_backoff"
119
+ - name: "SLACK_CONNECTION_ERROR"
120
+ description: "Failed to connect to Slack webhook"
121
+ recoverable: true
122
+ retry_strategy: "exponential_backoff"
123
+ - name: "SLACK_CLIENT_ERROR"
124
+ description: "HTTP client error during request"
125
+ recoverable: true
126
+ retry_strategy: "exponential_backoff"
127
+ - name: "SLACK_HTTP_4XX"
128
+ description: "Slack returned HTTP 4xx error (non-429)"
129
+ recoverable: false
130
+ retry_strategy: "none"
131
+ - name: "SLACK_HTTP_5XX"
132
+ description: "Slack returned HTTP 5xx error"
133
+ recoverable: true
134
+ retry_strategy: "exponential_backoff"
135
+ # IO operations (EFFECT node specific)
136
+ io_operations:
137
+ - operation: "send_alert"
138
+ description: "Send a formatted alert to Slack with Block Kit formatting"
139
+ input_fields:
140
+ - severity
141
+ - message
142
+ - title
143
+ - details
144
+ - channel
145
+ - correlation_id
146
+ output_fields:
147
+ - success
148
+ - duration_ms
149
+ - correlation_id
150
+ - error
151
+ - error_code
152
+ - retry_count
153
+ - operation: "send_message"
154
+ description: "Send a plain text message to Slack"
155
+ input_fields:
156
+ - message
157
+ - channel
158
+ - correlation_id
159
+ output_fields:
160
+ - success
161
+ - duration_ms
162
+ - correlation_id
163
+ - error
164
+ - error_code
165
+ - retry_count
166
+ # Dependencies (protocols this node requires)
167
+ dependencies:
168
+ - name: "http_client"
169
+ type: "library"
170
+ library: "aiohttp"
171
+ description: "Async HTTP client for webhook requests"
172
+ - name: "slack_webhook_url"
173
+ type: "environment"
174
+ env_var: "SLACK_WEBHOOK_URL"
175
+ description: "Slack incoming webhook URL"
176
+ required: true
177
+ # Capabilities provided by this node
178
+ capabilities:
179
+ - name: "slack_alerting"
180
+ description: "Send infrastructure alerts to Slack channels"
181
+ - name: "block_kit_formatting"
182
+ description: "Format messages using Slack Block Kit for rich display"
183
+ - name: "retry_with_backoff"
184
+ description: "Retry failed requests with exponential backoff"
185
+ - name: "rate_limit_handling"
186
+ description: "Handle Slack rate limiting gracefully"
187
+ # Model definitions
188
+ definitions:
189
+ ModelSlackAlert:
190
+ type: object
191
+ description: "Input payload for Slack alert operations"
192
+ frozen: true
193
+ extra: "forbid"
194
+ properties:
195
+ severity:
196
+ type: enum
197
+ enum_class: "EnumAlertSeverity"
198
+ module: "omnibase_infra.handlers.models.model_slack_alert"
199
+ description: "Alert severity level for visual formatting"
200
+ default: "info"
201
+ enum:
202
+ - "critical"
203
+ - "error"
204
+ - "warning"
205
+ - "info"
206
+ message:
207
+ type: string
208
+ description: "Main alert message content"
209
+ min_length: 1
210
+ max_length: 3000
211
+ title:
212
+ type: string
213
+ nullable: true
214
+ default: null
215
+ max_length: 150
216
+ description: "Optional alert title"
217
+ details:
218
+ type: object
219
+ key_type: string
220
+ value_type: any
221
+ description: "Additional key-value details"
222
+ default_factory: "dict"
223
+ channel:
224
+ type: string
225
+ nullable: true
226
+ default: null
227
+ description: "Optional channel override"
228
+ correlation_id:
229
+ type: uuid
230
+ description: "UUID for distributed tracing"
231
+ default_factory: "uuid4"
232
+ required:
233
+ - message
234
+ ModelSlackAlertResult:
235
+ type: object
236
+ description: "Response from Slack webhook operations"
237
+ frozen: true
238
+ extra: "forbid"
239
+ properties:
240
+ success:
241
+ type: boolean
242
+ description: "Whether the alert was delivered successfully"
243
+ duration_ms:
244
+ type: float
245
+ description: "Time taken for the operation in milliseconds"
246
+ default: 0.0
247
+ ge: 0.0
248
+ correlation_id:
249
+ type: uuid
250
+ description: "UUID from the original request"
251
+ error:
252
+ type: string
253
+ nullable: true
254
+ default: null
255
+ description: "Sanitized error message if success is False"
256
+ error_code:
257
+ type: string
258
+ nullable: true
259
+ default: null
260
+ description: "Error code for programmatic handling"
261
+ retry_count:
262
+ type: integer
263
+ description: "Number of retry attempts made"
264
+ default: 0
265
+ ge: 0
266
+ required:
267
+ - success
268
+ - correlation_id
269
+ # Health check configuration
270
+ health_check:
271
+ enabled: true
272
+ endpoint: "/health"
273
+ interval_seconds: 30
274
+ checks:
275
+ - name: "webhook_configured"
276
+ check_type: "environment"
277
+ env_var: "SLACK_WEBHOOK_URL"
278
+ description: "Verify SLACK_WEBHOOK_URL is configured"
279
+ # Metadata
280
+ metadata:
281
+ author: "OmniNode Team"
282
+ license: "MIT"
283
+ created: "2026-02-04"
284
+ updated: "2026-02-04"
285
+ tags:
286
+ - effect
287
+ - slack
288
+ - alerting
289
+ - webhook
290
+ - infrastructure
291
+ - block-kit