omnibase_infra 0.2.5__py3-none-any.whl → 0.2.7__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 (139) hide show
  1. omnibase_infra/constants_topic_patterns.py +26 -0
  2. omnibase_infra/enums/__init__.py +3 -0
  3. omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
  4. omnibase_infra/enums/enum_handler_source_mode.py +16 -2
  5. omnibase_infra/errors/__init__.py +4 -0
  6. omnibase_infra/errors/error_binding_resolution.py +128 -0
  7. omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +0 -2
  8. omnibase_infra/event_bus/event_bus_inmemory.py +64 -10
  9. omnibase_infra/event_bus/event_bus_kafka.py +105 -47
  10. omnibase_infra/event_bus/mixin_kafka_broadcast.py +3 -7
  11. omnibase_infra/event_bus/mixin_kafka_dlq.py +12 -6
  12. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +0 -81
  13. omnibase_infra/event_bus/testing/__init__.py +26 -0
  14. omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
  15. omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
  16. omnibase_infra/handlers/handler_consul.py +2 -0
  17. omnibase_infra/handlers/mixins/__init__.py +5 -0
  18. omnibase_infra/handlers/mixins/mixin_consul_service.py +274 -10
  19. omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
  20. omnibase_infra/handlers/models/model_filesystem_config.py +4 -4
  21. omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
  22. omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
  23. omnibase_infra/mixins/mixin_node_introspection.py +189 -19
  24. omnibase_infra/models/__init__.py +8 -0
  25. omnibase_infra/models/bindings/__init__.py +59 -0
  26. omnibase_infra/models/bindings/constants.py +144 -0
  27. omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
  28. omnibase_infra/models/bindings/model_operation_binding.py +44 -0
  29. omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
  30. omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
  31. omnibase_infra/models/discovery/model_introspection_config.py +25 -17
  32. omnibase_infra/models/dispatch/__init__.py +8 -0
  33. omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
  34. omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
  35. omnibase_infra/models/handlers/model_handler_source_config.py +1 -1
  36. omnibase_infra/models/model_node_identity.py +126 -0
  37. omnibase_infra/models/projection/model_snapshot_topic_config.py +3 -2
  38. omnibase_infra/models/registration/__init__.py +9 -0
  39. omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
  40. omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
  41. omnibase_infra/models/registration/model_node_introspection_event.py +11 -0
  42. omnibase_infra/models/runtime/__init__.py +9 -0
  43. omnibase_infra/models/validation/model_coverage_metrics.py +2 -2
  44. omnibase_infra/nodes/__init__.py +9 -0
  45. omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
  46. omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
  47. omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
  48. omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
  49. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
  50. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
  51. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
  52. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
  53. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
  54. omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
  55. omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
  56. omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
  57. omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
  58. omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
  59. omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
  60. omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
  61. omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
  62. omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
  63. omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
  64. omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
  65. omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
  66. omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
  67. omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
  68. omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
  69. omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
  70. omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
  71. omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
  72. omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
  73. omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
  74. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
  75. omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
  76. omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
  77. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
  78. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
  79. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
  80. omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
  81. omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
  82. omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
  83. omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
  84. omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
  85. omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
  86. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -5
  87. omnibase_infra/nodes/reducers/models/__init__.py +7 -2
  88. omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +11 -0
  89. omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
  90. omnibase_infra/nodes/reducers/registration_reducer.py +1 -0
  91. omnibase_infra/protocols/__init__.py +3 -0
  92. omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
  93. omnibase_infra/runtime/__init__.py +60 -0
  94. omnibase_infra/runtime/binding_resolver.py +753 -0
  95. omnibase_infra/runtime/constants_security.py +70 -0
  96. omnibase_infra/runtime/contract_loaders/__init__.py +9 -0
  97. omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
  98. omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
  99. omnibase_infra/runtime/emit_daemon/cli.py +844 -0
  100. omnibase_infra/runtime/emit_daemon/client.py +811 -0
  101. omnibase_infra/runtime/emit_daemon/config.py +535 -0
  102. omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
  103. omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
  104. omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
  105. omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
  106. omnibase_infra/runtime/emit_daemon/queue.py +618 -0
  107. omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
  108. omnibase_infra/runtime/handler_source_resolver.py +43 -2
  109. omnibase_infra/runtime/kafka_contract_source.py +984 -0
  110. omnibase_infra/runtime/models/__init__.py +13 -0
  111. omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
  112. omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
  113. omnibase_infra/runtime/models/model_runtime_scheduler_config.py +4 -3
  114. omnibase_infra/runtime/models/model_security_config.py +109 -0
  115. omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
  116. omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
  117. omnibase_infra/runtime/service_kernel.py +76 -6
  118. omnibase_infra/runtime/service_message_dispatch_engine.py +558 -15
  119. omnibase_infra/runtime/service_runtime_host_process.py +770 -20
  120. omnibase_infra/runtime/transition_notification_publisher.py +3 -2
  121. omnibase_infra/runtime/util_wiring.py +206 -62
  122. omnibase_infra/services/mcp/service_mcp_tool_sync.py +27 -9
  123. omnibase_infra/services/session/config_consumer.py +25 -8
  124. omnibase_infra/services/session/config_store.py +2 -2
  125. omnibase_infra/services/session/consumer.py +1 -1
  126. omnibase_infra/topics/__init__.py +45 -0
  127. omnibase_infra/topics/platform_topic_suffixes.py +140 -0
  128. omnibase_infra/topics/util_topic_composition.py +95 -0
  129. omnibase_infra/types/typed_dict/__init__.py +9 -1
  130. omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
  131. omnibase_infra/utils/__init__.py +9 -0
  132. omnibase_infra/utils/util_consumer_group.py +232 -0
  133. omnibase_infra/validation/infra_validators.py +18 -1
  134. omnibase_infra/validation/validation_exemptions.yaml +192 -0
  135. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/METADATA +3 -3
  136. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/RECORD +139 -52
  137. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/entry_points.txt +1 -0
  138. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/WHEEL +0 -0
  139. {omnibase_infra-0.2.5.dist-info → omnibase_infra-0.2.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,58 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Deactivate contract intent payload model.
4
+
5
+ Related:
6
+ - OMN-1653: Contract Registry Reducer implementation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+
18
+ class ModelPayloadDeactivateContract(BaseModel):
19
+ """Payload for PostgreSQL deactivate contract intents.
20
+
21
+ Used when a contract-deregistered event is processed to mark the
22
+ contract as inactive (soft delete).
23
+
24
+ Note: contract_id is a derived natural key (node_name:major.minor.patch),
25
+ not a UUID. This is intentional per the contract registry design.
26
+
27
+ Attributes:
28
+ intent_type: Routing discriminator. Always "postgres.deactivate_contract".
29
+ correlation_id: Correlation ID for distributed tracing.
30
+ contract_id: Contract to deactivate.
31
+ node_name: Contract node name.
32
+ reason: Deregistration reason (shutdown, upgrade, manual).
33
+ deactivated_at: Deactivation timestamp.
34
+ """
35
+
36
+ model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
37
+
38
+ intent_type: Literal["postgres.deactivate_contract"] = Field(
39
+ default="postgres.deactivate_contract",
40
+ description="Routing discriminator for intent dispatch.",
41
+ )
42
+
43
+ correlation_id: UUID = Field(
44
+ ..., description="Correlation ID for distributed tracing."
45
+ )
46
+
47
+ # ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
48
+ contract_id: str = Field(..., description="Contract to deactivate.")
49
+
50
+ # ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
51
+ node_name: str = Field(..., description="Contract node name.")
52
+
53
+ reason: str = Field(..., description="Deregistration reason.")
54
+
55
+ deactivated_at: datetime = Field(..., description="Deactivation timestamp.")
56
+
57
+
58
+ __all__ = ["ModelPayloadDeactivateContract"]
@@ -0,0 +1,49 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Mark stale intent payload model.
4
+
5
+ Related:
6
+ - OMN-1653: Contract Registry Reducer implementation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+
18
+ class ModelPayloadMarkStale(BaseModel):
19
+ """Payload for PostgreSQL mark stale intents.
20
+
21
+ Used when a runtime-tick event is processed to batch mark
22
+ contracts as stale if they haven't been seen within the threshold.
23
+
24
+ Attributes:
25
+ intent_type: Routing discriminator. Always "postgres.mark_stale".
26
+ correlation_id: Correlation ID for distributed tracing.
27
+ stale_cutoff: Contracts with last_seen_at before this are stale.
28
+ checked_at: When staleness check was performed.
29
+ """
30
+
31
+ model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
32
+
33
+ intent_type: Literal["postgres.mark_stale"] = Field(
34
+ default="postgres.mark_stale",
35
+ description="Routing discriminator for intent dispatch.",
36
+ )
37
+
38
+ correlation_id: UUID = Field(
39
+ ..., description="Correlation ID for distributed tracing."
40
+ )
41
+
42
+ stale_cutoff: datetime = Field(
43
+ ..., description="Contracts older than this are marked stale."
44
+ )
45
+
46
+ checked_at: datetime = Field(..., description="When check was performed.")
47
+
48
+
49
+ __all__ = ["ModelPayloadMarkStale"]
@@ -0,0 +1,71 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Update heartbeat intent payload model.
4
+
5
+ Related:
6
+ - OMN-1653: Contract Registry Reducer implementation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+
18
+ class ModelPayloadUpdateHeartbeat(BaseModel):
19
+ """Payload for PostgreSQL update heartbeat intents.
20
+
21
+ Used when a node-heartbeat event is processed to update the
22
+ last_seen_at timestamp for a contract.
23
+
24
+ Note: contract_id is a derived natural key (node_name:major.minor.patch),
25
+ not a UUID. This is intentional per the contract registry design.
26
+
27
+ Attributes:
28
+ intent_type: Routing discriminator. Always "postgres.update_heartbeat".
29
+ correlation_id: Correlation ID for distributed tracing.
30
+ contract_id: Contract to update.
31
+ node_name: Contract node name.
32
+ source_node_id: String representation of heartbeating node's UUID (optional).
33
+ last_seen_at: New heartbeat timestamp.
34
+ uptime_seconds: Node uptime in seconds (optional).
35
+ sequence_number: Heartbeat sequence number (optional).
36
+ """
37
+
38
+ model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
39
+
40
+ intent_type: Literal["postgres.update_heartbeat"] = Field(
41
+ default="postgres.update_heartbeat",
42
+ description="Routing discriminator for intent dispatch.",
43
+ )
44
+
45
+ correlation_id: UUID = Field(
46
+ ..., description="Correlation ID for distributed tracing."
47
+ )
48
+
49
+ # ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
50
+ contract_id: str = Field(..., description="Contract to update.")
51
+
52
+ # ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
53
+ node_name: str = Field(..., description="Contract node name.")
54
+
55
+ source_node_id: str | None = Field(
56
+ default=None,
57
+ description="Source node ID as string (UUID string representation, optional).",
58
+ )
59
+
60
+ last_seen_at: datetime = Field(..., description="Heartbeat timestamp.")
61
+
62
+ uptime_seconds: float | None = Field(
63
+ default=None, ge=0, description="Node uptime in seconds."
64
+ )
65
+
66
+ sequence_number: int | None = Field(
67
+ default=None, ge=0, description="Heartbeat sequence number."
68
+ )
69
+
70
+
71
+ __all__ = ["ModelPayloadUpdateHeartbeat"]
@@ -0,0 +1,66 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Update topic intent payload model.
4
+
5
+ Related:
6
+ - OMN-1653: Contract Registry Reducer implementation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+
18
+ class ModelPayloadUpdateTopic(BaseModel):
19
+ """Payload for PostgreSQL update topic intents.
20
+
21
+ Used when a contract-registered event is processed to update
22
+ the topic routing table with producer/consumer information.
23
+
24
+ Note: contract_id is a derived natural key (node_name:major.minor.patch),
25
+ not a UUID. This is intentional per the contract registry design.
26
+
27
+ Attributes:
28
+ intent_type: Routing discriminator. Always "postgres.update_topic".
29
+ correlation_id: Correlation ID for distributed tracing.
30
+ topic_suffix: Topic suffix (e.g., onex.evt.platform.contract-registered.v1).
31
+ direction: Whether this contract publishes or subscribes to the topic.
32
+ contract_id: Contract that uses this topic.
33
+ node_name: Contract node name.
34
+ event_type: Event type name (optional).
35
+ last_seen_at: When topic usage was last seen.
36
+ """
37
+
38
+ model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
39
+
40
+ intent_type: Literal["postgres.update_topic"] = Field(
41
+ default="postgres.update_topic",
42
+ description="Routing discriminator for intent dispatch.",
43
+ )
44
+
45
+ correlation_id: UUID = Field(
46
+ ..., description="Correlation ID for distributed tracing."
47
+ )
48
+
49
+ topic_suffix: str = Field(..., description="Topic suffix.")
50
+
51
+ direction: Literal["publish", "subscribe"] = Field(
52
+ ..., description="Topic direction."
53
+ )
54
+
55
+ # ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
56
+ contract_id: str = Field(..., description="Contract using this topic.")
57
+
58
+ # ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
59
+ node_name: str = Field(..., description="Contract node name.")
60
+
61
+ event_type: str | None = Field(default=None, description="Event type name.")
62
+
63
+ last_seen_at: datetime = Field(..., description="Last seen timestamp.")
64
+
65
+
66
+ __all__ = ["ModelPayloadUpdateTopic"]
@@ -0,0 +1,92 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Upsert contract intent payload model.
4
+
5
+ Related:
6
+ - OMN-1653: Contract Registry Reducer implementation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ from uuid import UUID
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+ from omnibase_core.types import JsonType
18
+
19
+
20
+ class ModelPayloadUpsertContract(BaseModel):
21
+ """Payload for PostgreSQL upsert contract intents.
22
+
23
+ Used when a contract-registered event is processed to insert or update
24
+ the contract record in the contracts table.
25
+
26
+ Note: contract_id is a derived natural key (node_name:major.minor.patch),
27
+ not a UUID. This is intentional per the contract registry design.
28
+
29
+ Serialization Note:
30
+ The contract_yaml field accepts both dict (parsed) and str (raw YAML).
31
+ The Effect layer (PostgresAdapter) is responsible for serializing dict
32
+ to YAML string before INSERT, as the PostgreSQL column is TEXT type.
33
+
34
+ Attributes:
35
+ intent_type: Routing discriminator. Always "postgres.upsert_contract".
36
+ correlation_id: Correlation ID for distributed tracing.
37
+ contract_id: Derived contract identity (node_name:major.minor.patch).
38
+ node_name: Contract node name from event.
39
+ version_major: Semantic version major component.
40
+ version_minor: Semantic version minor component.
41
+ version_patch: Semantic version patch component.
42
+ contract_hash: Hash of the contract YAML for change detection.
43
+ contract_yaml: Full contract YAML for storage and replay. Accepts dict
44
+ (parsed) or str (raw). Effect layer serializes dict to YAML string
45
+ before INSERT.
46
+ source_node_id: UUID of the node that emitted the event (optional).
47
+ is_active: Whether the contract is currently active.
48
+ registered_at: Timestamp when contract was registered.
49
+ last_seen_at: Last heartbeat/registration timestamp.
50
+ """
51
+
52
+ model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
53
+
54
+ intent_type: Literal["postgres.upsert_contract"] = Field(
55
+ default="postgres.upsert_contract",
56
+ description="Routing discriminator for intent dispatch.",
57
+ )
58
+
59
+ correlation_id: UUID = Field(
60
+ ..., description="Correlation ID for distributed tracing."
61
+ )
62
+
63
+ # ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
64
+ contract_id: str = Field(
65
+ ..., description="Contract identity: node_name:major.minor.patch"
66
+ )
67
+
68
+ # ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
69
+ node_name: str = Field(..., description="Contract node name.")
70
+
71
+ version_major: int = Field(..., ge=0, description="Semantic version major.")
72
+ version_minor: int = Field(..., ge=0, description="Semantic version minor.")
73
+ version_patch: int = Field(..., ge=0, description="Semantic version patch.")
74
+
75
+ contract_hash: str = Field(..., description="Hash of contract YAML.")
76
+
77
+ contract_yaml: JsonType = Field(
78
+ ..., description="Full contract YAML (dict or raw string)."
79
+ )
80
+
81
+ source_node_id: str | None = Field(
82
+ default=None, description="Source node UUID (optional)."
83
+ )
84
+
85
+ is_active: bool = Field(default=True, description="Whether contract is active.")
86
+
87
+ registered_at: datetime = Field(..., description="Registration timestamp.")
88
+
89
+ last_seen_at: datetime = Field(..., description="Last heartbeat timestamp.")
90
+
91
+
92
+ __all__ = ["ModelPayloadUpsertContract"]
@@ -0,0 +1,121 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Contract Registry Reducer - FSM-driven declarative reducer.
4
+
5
+ This reducer follows the ONEX declarative pattern:
6
+ - DECLARATIVE reducer driven by contract.yaml
7
+ - Zero custom routing logic - all behavior from FSM state_machine
8
+ - Lightweight shell that delegates to NodeReducer base class
9
+ - Used for ONEX-compliant runtime execution via RuntimeHostProcess
10
+ - Pattern: "Contract-driven, FSM state transitions"
11
+
12
+ Extends NodeReducer from omnibase_core for FSM-driven state management.
13
+ All state transition logic is 100% driven by contract.yaml, not Python code.
14
+
15
+ Purpose:
16
+ This reducer projects contract registration events to PostgreSQL:
17
+ 1. Receives contract registration events from Kafka
18
+ 2. Validates and deduplicates events using Kafka offset tracking
19
+ 3. Emits PostgreSQL upsert intents for the contract_registry table
20
+ 4. Processes heartbeat events to update last_seen timestamps
21
+ 5. Handles deregistration events to mark contracts as inactive
22
+
23
+ FSM Pattern:
24
+ 1. Receive contract event (trigger: contract_registered, heartbeat, deregistered)
25
+ 2. Check idempotency via Kafka offset (skip duplicates)
26
+ 3. Emit PostgreSQL intent for persistence
27
+ 4. Update state with processed event tracking
28
+
29
+ Design Decisions:
30
+ - 100% Contract-Driven: All FSM logic in YAML, not Python
31
+ - Zero Custom Methods: Base class handles everything
32
+ - Declarative Execution: State transitions defined in state_machine
33
+ - Pure Function Pattern: (state, event) -> (new_state, intents)
34
+
35
+ Related Modules:
36
+ - contract.yaml: FSM state machine and intent emission configuration
37
+ - models/: State model for idempotency and statistics tracking
38
+ - OMN-1653: Contract registry reducer implementation
39
+
40
+ Testing Notes:
41
+ - Unit tests: tests/unit/nodes/contract_registry_reducer/
42
+ - Integration tests: Required before production
43
+ * Intent emission through RuntimeHostProcess
44
+ * End-to-end contract workflow with PostgreSQL mocks
45
+ * Kafka offset-based idempotency verification
46
+ """
47
+
48
+ from __future__ import annotations
49
+
50
+ from typing import TYPE_CHECKING
51
+
52
+ from omnibase_core.nodes.node_reducer import NodeReducer
53
+
54
+ if TYPE_CHECKING:
55
+ # Import needed for string annotation in class definition
56
+ from omnibase_infra.nodes.contract_registry_reducer.models.model_contract_registry_state import (
57
+ ModelContractRegistryState,
58
+ )
59
+
60
+
61
+ class NodeContractRegistryReducer(
62
+ NodeReducer["ModelContractRegistryState", "ModelContractRegistryState"]
63
+ ):
64
+ """Contract registry reducer - FSM state transitions driven by contract.yaml.
65
+
66
+ This is a purely declarative reducer. All behavior is defined in contract.yaml.
67
+ No custom Python logic is required - the base NodeReducer class handles all
68
+ FSM-driven state transitions via the contract configuration.
69
+
70
+ This reducer processes contract registration workflows by:
71
+ 1. Receiving contract events (registration, heartbeat, deregistration)
72
+ 2. Executing FSM transitions defined in contract.yaml
73
+ 3. Emitting PostgreSQL upsert intents for the contract_registry table
74
+ 4. Tracking processed events for idempotency via Kafka offsets
75
+
76
+ All state transition logic, intent emission, and validation are driven
77
+ entirely by the contract.yaml FSM configuration.
78
+
79
+ Example YAML Contract (state_machine section):
80
+ ```yaml
81
+ state_machine:
82
+ state_machine_name: contract_registry_fsm
83
+ initial_state: idle
84
+ states:
85
+ - state_name: idle
86
+ description: "Waiting for contract events"
87
+ - state_name: processing
88
+ description: "Processing contract event"
89
+ entry_actions:
90
+ - emit_postgres_upsert_intent
91
+ transitions:
92
+ - from_state: idle
93
+ to_state: processing
94
+ trigger: contract_received
95
+ conditions:
96
+ - expression: "node_id is_present"
97
+ required: true
98
+ ```
99
+
100
+ Usage:
101
+ ```python
102
+ from omnibase_core.models.container import ModelONEXContainer
103
+ from omnibase_core.models.reducer import ModelReducerInput
104
+ from omnibase_core.enums import EnumReductionType
105
+
106
+ # Create and initialize
107
+ container = ModelONEXContainer()
108
+ reducer = NodeContractRegistryReducer(container)
109
+
110
+ # Process input via FSM
111
+ input_data = ModelReducerInput(
112
+ data=contract_event,
113
+ reduction_type=EnumReductionType.MERGE,
114
+ metadata=ModelReducerMetadata(trigger="contract_registered"),
115
+ )
116
+ result = await reducer.process(input_data)
117
+ ```
118
+ """
119
+
120
+
121
+ __all__ = ["NodeContractRegistryReducer"]