Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "omnibase_infra"
version = "0.36.1"
version = "0.37.0"
description = "ONEX Infrastructure - Service integration and database infrastructure tools"
authors = [{ name = "OmniNode.ai", email = "contact@omninode.ai" }]
license = {text = "MIT"}
Expand All @@ -29,8 +29,8 @@ dependencies = [
# omnibase-core = { git = "https://github.com/org/omnibase_core.git", rev = "SHA" }
# Remember to switch back to PyPI versions before merging to main.
#
"omnibase-core>=0.41.0,<0.42.0",
"omnibase-spi>=0.21.0,<0.22.0",
"omnibase-core>=0.42.0,<0.43.0",
"omnibase-spi>=0.22.0,<0.23.0",
# ============================================================================
# External Dependency Security Guidance
# ============================================================================
Expand Down Expand Up @@ -163,19 +163,14 @@ dev = [

[tool.uv]
override-dependencies = [
"omnibase-core>=0.41.0,<0.42.0",
# to resolve from the release/v0.21.0 branch (swap to tag once v0.21.0 lands).
"omnibase-spi @ git+https://github.com/OmniNode-ai/omnibase_spi.git@v0.21.0",
"omnibase-core>=0.42.0,<0.43.0",
"omnibase-spi>=0.22.0,<0.23.0",
]

[tool.uv.sources]
# OMN-11142: pin to omnibase_core main HEAD for the omnibase_core.gate module
# (receipt_canonical, OmniGate config) used by the OmniGate signer/executor.
# Also carries EnumDataProvenance (OMN-11210). Swap to a tagged release once cut.
omnibase-core = { git = "https://github.com/OmniNode-ai/omnibase_core.git", rev = "7631ef3321e1a6561f4b2c70019ee57d24160620" }
# v0.36.0: spi v0.21.0 tag does not exist yet (release in flight); pin to the
# release branch and swap to `tag = "v0.21.0"` in a follow-up once tagged.
omnibase-spi = { git = "https://github.com/OmniNode-ai/omnibase_spi.git", tag = "v0.21.0" }
# Release v0.37.0: pin to release tags for core v0.42.0 and spi v0.22.0
omnibase-core = { git = "https://github.com/OmniNode-ai/omnibase_core.git", tag = "v0.42.0" }
omnibase-spi = { git = "https://github.com/OmniNode-ai/omnibase_spi.git", tag = "v0.22.0" }
onex-change-control = { git = "https://github.com/OmniNode-ai/onex_change_control.git", branch = "main" }
[tool.ruff]
target-version = "py312"
Expand Down
4 changes: 3 additions & 1 deletion scripts/validation/topic_literal_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Pre-existing violations grandfathered in on 2026-03-02 (updated 2026-03-10 for SPDX line shifts).
# Format: <repo-relative-path>:<lineno>
# DO NOT add new entries here — fix violations instead.
# Total entries: 121
# Total entries: 123
src/omnibase_infra/cli/git_hook_relay.py:72
src/omnibase_infra/cli/git_hook_relay.py:74
src/omnibase_infra/cli/infra_test/introspect.py:25
Expand Down Expand Up @@ -120,4 +120,6 @@ src/omnibase_infra/tui/consumers/consumer_status.py:39
src/omnibase_infra/tui/consumers/consumer_status.py:40
src/omnibase_infra/tui/consumers/consumer_status.py:41
src/omnibase_infra/tui/consumers/consumer_status.py:42
src/omnibase_infra/runtime/service_kernel.py:2015
src/omnibase_infra/runtime/service_kernel.py:2016
src/omnibase_infra/validation/demo_loop_gate.py:76
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ node_type: "EFFECT_GENERIC"
contract_version:
major: 1
minor: 0
patch: 0
node_version: "1.0.0"
patch: 1
node_version: "1.0.1"
description: >
Effect node that sends chat completion requests to OpenAI-compatible LLM endpoints (vLLM, LiteLLM, Ollama). Supports model routing by token count and async streaming.
Effect node that sends chat completion requests to OpenAI-compatible LLM endpoints (vLLM, LiteLLM, Ollama). Supports model routing by token count and async streaming. Endpoint resolution is contract/config driven and requires LLM_CODER_URL when no provider endpoint is supplied.

input_model:
name: "ModelLLMCompletionRequest"
Expand Down Expand Up @@ -43,7 +43,7 @@ handler_routing:
dependencies:
- type: "environment"
name: "LLM_CODER_URL"
description: "Base URL for the primary LLM endpoint"
description: "Required base URL for the primary LLM endpoint when no contract/provider endpoint is supplied; no localhost fallback is permitted"
- type: "environment"
name: "LLM_CODER_FAST_URL"
description: "Base URL for the fast/mid-tier LLM endpoint"
Expand Down
39 changes: 39 additions & 0 deletions src/omnibase_infra/runtime/runtime_contract_config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,17 @@

import logging
from pathlib import Path
from typing import TYPE_CHECKING
from uuid import UUID, uuid4

import yaml

from omnibase_core.models.contracts.model_contract_base import ModelContractBase

if TYPE_CHECKING:
from omnibase_core.models.contracts.subcontracts.model_hook_activation import (
ModelHookActivation,
)
from omnibase_infra.enums import EnumInfraTransportType
from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
from omnibase_infra.models.bindings import ModelOperationBindingsSubcontract
Expand Down Expand Up @@ -485,6 +491,39 @@ def _load_single_contract(
correlation_id=correlation_id,
)

def load_hook_activations_from_path(
self,
contracts_dir: Path,
) -> list[ModelHookActivation]:
"""Load hook_activations.yaml from a package contracts directory.

Returns empty list if file absent, malformed, or contains unknown bits (lenient policy).
"""
from omnibase_core.models.contracts.subcontracts.model_hook_activation import (
ModelHookActivation,
)

yaml_path = contracts_dir / "hook_activations.yaml"
if not yaml_path.exists():
return []
try:
raw = yaml.safe_load(yaml_path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
logger.warning(
"hook_activations.yaml at %s is not a mapping — skipping",
yaml_path,
)
return []
raw_activations = raw.get("hook_activations", [])
return [ModelHookActivation.model_validate(a) for a in raw_activations]
except Exception as exc: # noqa: BLE001 — boundary: log warning and degrade
logger.warning(
"Skipping malformed hook_activations.yaml at %s: %s",
yaml_path,
exc,
)
return []

def load_from_directory(self, search_path: Path) -> list[ModelContractBase]:
"""Scan a directory tree for contract.yaml files and return typed contract models.

Expand Down
92 changes: 92 additions & 0 deletions tests/unit/runtime/test_hook_activations_startup_wiring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SPDX-FileCopyrightText: 2025 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT

import logging
from pathlib import Path

import pytest

from omnibase_infra.runtime.runtime_contract_config_loader import (
RuntimeContractConfigLoader,
)


@pytest.mark.unit
def test_loader_collects_hook_activations_from_packages(tmp_path: Path) -> None:
contracts_dir = tmp_path / "pkg" / "contracts"
contracts_dir.mkdir(parents=True)
yaml_content = """
hook_activations:
- hook_bit: WORKTREE_GUARD
enabled_by_default: true
"""
(contracts_dir / "hook_activations.yaml").write_text(yaml_content)

loader = RuntimeContractConfigLoader()
activations = loader.load_hook_activations_from_path(contracts_dir)
assert len(activations) == 1
assert activations[0].hook_bit.name == "WORKTREE_GUARD"


@pytest.mark.unit
def test_loader_returns_empty_when_file_absent(tmp_path: Path) -> None:
contracts_dir = tmp_path / "pkg" / "contracts"
contracts_dir.mkdir(parents=True)

loader = RuntimeContractConfigLoader()
activations = loader.load_hook_activations_from_path(contracts_dir)
assert activations == []


@pytest.mark.unit
def test_loader_skips_malformed_hook_activations_yaml(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
contracts_dir = tmp_path / "pkg" / "contracts"
contracts_dir.mkdir(parents=True)
(contracts_dir / "hook_activations.yaml").write_text("not: valid: yaml: [")

loader = RuntimeContractConfigLoader()
with caplog.at_level(logging.WARNING):
activations = loader.load_hook_activations_from_path(contracts_dir)
assert activations == []


@pytest.mark.unit
def test_loader_skips_unknown_hook_bit(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
contracts_dir = tmp_path / "pkg" / "contracts"
contracts_dir.mkdir(parents=True)
yaml_content = """
hook_activations:
- hook_bit: DOES_NOT_EXIST
enabled_by_default: true
"""
(contracts_dir / "hook_activations.yaml").write_text(yaml_content)

loader = RuntimeContractConfigLoader()
with caplog.at_level(logging.WARNING):
activations = loader.load_hook_activations_from_path(contracts_dir)
assert activations == []


@pytest.mark.unit
def test_loader_parses_multiple_activations(tmp_path: Path) -> None:
contracts_dir = tmp_path / "pkg" / "contracts"
contracts_dir.mkdir(parents=True)
yaml_content = """
hook_activations:
- hook_bit: WORKTREE_GUARD
enabled_by_default: true
description: "Enforces worktree discipline"
- hook_bit: RUFF_FIX
enabled_by_default: false
description: "Auto-runs ruff after file writes"
"""
(contracts_dir / "hook_activations.yaml").write_text(yaml_content)

loader = RuntimeContractConfigLoader()
activations = loader.load_hook_activations_from_path(contracts_dir)
assert len(activations) == 2
assert activations[1].enabled_by_default is False
Loading