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
72 changes: 65 additions & 7 deletions tests/chains/test_event_chain_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,60 @@

_SRC_ROOT = Path(__file__).resolve().parents[2] / "src" / "omnimarket" / "nodes"

# The tier `summarization` routes to first: `local-reasoner`
# (src/omnimarket/configs/routing_tiers.yaml, model Qwen3.6-27B-MTP, OMN-15630).
# Its repo-default `endpoint_url` is null — a site-specific local address the
# overlay is required to supply (OMN-12815/OMN-15807) — so with no overlay the
# routing authority reports NO configured endpoint for this task type and the
# reducer raises before it can emit a decision.
_LOCAL_TIER_BACKEND_ID = "local-reasoner"

# The discard port. Syntactically a COMPLETE chat-completions URL (a bare base is
# rejected, OMN-12815) and deliberately unreachable: the node under gate is a
# routing REDUCER — it decides which backend to use and emits ModelRoutingDecision.
# It never opens a connection, so a live address here would buy nothing and would
# make this gate depend on a running server.
_UNREACHABLE_ENDPOINT_URL = "http://127.0.0.1:9/v1/chat/completions"


@pytest.fixture(autouse=True)
def _hermetic_routing_config(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""Make tier routability a fact about the repo, not about the machine.

This gate ran GREEN on a developer laptop and RED on the CI runner at the
omnibase-infra 0.38.11 pin (OMN-16815), with::

ProtocolConfigurationError: [ONEX_CORE_041_INVALID_CONFIGURATION]
No tier has a configured endpoint for task_type='summarization'.

Nothing about the chain differed. The laptop's shell carried live cloud
credentials (``GEMINI_API_KEY``, ``LLM_GLM_API_KEY``, ``OPEN_ROUTER_API_KEY``),
which made the cheap_cloud rung routable and let routing succeed there; the
runner has none, and the local rung was unroutable on both because its
``endpoint_url`` is null in the repo default. So the gate's verdict was a
function of the ambient environment — the OMN-16796 defect class, in the
suite written to catch chains dying silently.

Binding a minimal overlay makes the LOCAL rung routable everywhere, with no
credential and no server. ``tests/conftest.py::_ensure_bifrost_contract_path``
unconditionally clears ``BIFROST_OVERLAY_PATH``; this module-level autouse
fixture runs after it and therefore wins, which is the documented override
path.
"""
overlay = tmp_path / "bifrost_overlay_chain_gate.yaml"
overlay.write_text(
"config_version: '2.4.0'\n"
"schema_version: 'bifrost_delegation.v1'\n"
"backends:\n"
f" - backend_id: {_LOCAL_TIER_BACKEND_ID}\n"
f" endpoint_url: '{_UNREACHABLE_ENDPOINT_URL}'\n",
encoding="utf-8",
)
monkeypatch.setenv("BIFROST_OVERLAY_PATH", str(overlay))


@dataclass(frozen=True)
class ChainCase:
Expand Down Expand Up @@ -168,13 +222,17 @@ def as_param(self) -> object:
"excluded_backend_refs": [],
},
terminal_type_name="ModelRoutingDecision",
# Dead today. This node's contract declares db_io.db_tables (the
# OMN-15631 tenant-overlay table) while its handler is canonical def-B
# (`handle(self, intent: ModelRoutingIntent) -> ModelRoutingDecision`),
# so the installed omnibase-infra wires it to the PROJECTION arm and
# hands it a raw dict. Fix in flight: omnibase_infra#2937. This row
# turns RED the moment the pin picks that up.
broken_by="OMN-16767",
# LIVE as of the omnibase-infra 0.38.11 pin (OMN-16815). This row was
# xfail(strict=True) on OMN-16767: the node's contract declares
# db_io.db_tables (the OMN-15631 tenant-overlay table) while its handler
# is canonical def-B (`handle(self, intent: ModelRoutingIntent) ->
# ModelRoutingDecision`), and pre-0.38.11 wiring selected the PROJECTION
# arm on db_tables alone, handing the typed handler a raw dict. The fix
# (omnibase_infra#2937, plus #2943/#2949/#2951) first shipped in
# v0.38.11; the row XPASSed the moment the lock resolved it, which is
# exactly the signal the marker was written to produce, so the marker is
# gone. It is now an ordinary passing row: if this chain dies again, this
# goes RED with no marker to hide behind.
),
)

Expand Down
107 changes: 84 additions & 23 deletions tests/chains/test_projection_arm_constructs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,29 @@
contract. This test pins the outcome, so a future resolution below 0.38.10 fails
here with a named cause instead of resurfacing as a dead chain.

Deliberately NOT asserted here: that the chain terminalizes. It does not yet —
the constructed arm is the PROJECTION arm, which hands the typed def-B handler a
raw dict (OMN-16767). That fix is omnibase_infra#2937, merged 2026-08-27 but cut
AFTER v0.38.10, so it is not in any release this repo can pin. The chain-gate row
in ``test_event_chain_gate.py`` stays ``xfail(strict=True)`` on OMN-16767 until a
release carries it, at which point that row XPASSes and forces the marker's
removal. This suite covers the strictly earlier question: can the arm be built.
What changed at the 0.38.11 pin (OMN-16815) — read before editing an assertion.

This suite originally asserted that wiring SELECTS the projection arm, because
under 0.38.10 it did: ``db_io.db_tables`` alone chose the arm, which is precisely
the OMN-16767 defect (a typed def-B handler handed a raw ``input_data`` dict, and
the delegation chain died on the first attribute access). omnibase_infra#2937,
first released in **v0.38.11**, clears ``db_tables`` for a handler that declares a
concrete BaseModel input, so the correct arm for this contract is now the TYPED
DEF-B arm. Asserting the projection arm here after that fix would be asserting the
defect, so the selection assertion is INVERTED, not deleted — arm IDENTITY is
still pinned, so a silent regression back to the projection arm (the OMN-16767
signature) fails here by name.

That inversion means wiring no longer reaches ``_resolve_projection_database_target``
for this contract, so it can no longer carry the OMN-16794 constructibility claim.
That claim did not move to a comment: it is asserted DIRECTLY against the resolver
in ``test_projection_binding_resolves_under_every_topology_profile`` below, which
is the exact call that raised under 0.38.9, plus the resolver-level pin in
``test_overlay_table_resolves_to_its_physical_grant_schema``. Both survive a
future arm-selection change, because neither goes through wiring at all.

Terminalization is asserted by the chain-gate row in ``test_event_chain_gate.py``,
which stopped being ``xfail(strict=True)`` at this same pin.
"""

from __future__ import annotations
Expand All @@ -56,7 +72,10 @@
)
from omnibase_infra.event_bus.event_bus_inmemory import EventBusInmemory
from omnibase_infra.runtime.auto_wiring.discovery import discover_contracts_from_paths
from omnibase_infra.runtime.auto_wiring.handler_wiring import _prepare_handler_wiring
from omnibase_infra.runtime.auto_wiring.handler_wiring import (
_prepare_handler_wiring,
_resolve_projection_database_target,
)
from omnibase_infra.runtime.auto_wiring.models.model_discovered_contract import (
ModelDiscoveredContract,
)
Expand All @@ -76,12 +95,14 @@
# whole subject of this suite.
_OVERLAY_TABLE = "delegation_routing_tenant_overlay"

# The projection arm's callback is a closure minted by
# _make_projection_dispatch_callback, so its __qualname__ is how the SELECTED
# Both arms mint their callback as a closure, so __qualname__ is how the SELECTED
# ARM is identified. Asserting only that some dispatcher_id exists would be
# satisfied by the typed def-B fallback arm too — which is exactly the
# wrong-arm outcome OMN-16767 shipped and this suite exists to catch.
# satisfied by EITHER arm, and which one is the whole question: the projection arm
# is the wrong-arm outcome OMN-16767 shipped, and the typed def-B arm is what
# omnibase_infra#2937 (v0.38.11) restored for a handler with a concrete BaseModel
# input.
_PROJECTION_CALLBACK_QUALNAME = "_make_projection_dispatch_callback.<locals>._callback"
_TYPED_DEF_B_CALLBACK_QUALNAME = "_make_dispatch_callback.<locals>._callback"

# Every topology profile checked into omnibase_infra. The original report was
# "unconstructible under ALL SEVEN", so all seven are asserted, not a sample.
Expand Down Expand Up @@ -163,11 +184,46 @@ def test_contract_still_declares_the_db_io_block() -> None:


@pytest.mark.parametrize("profile_name", _PROFILES)
def test_projection_arm_constructs_under_every_topology_profile(
def test_projection_binding_resolves_under_every_topology_profile(
profile_name: str,
) -> None:
"""The OMN-16794 claim, asserted against the resolver that actually raised.

``_resolve_projection_database_target`` is the exact call that produced::

Projection binding 'tenant_projection' principal 'tenant_projection_writer'
lacks declared read privileges: SELECT on table
tenant.delegation_routing_tenant_overlay

under all seven profiles on omnibase-infra 0.38.9. Since 0.38.11 wiring no
longer routes this contract through that resolver (omnibase_infra#2937 clears
``db_tables`` for a typed def-B handler), the constructibility claim is made
here directly rather than as a side effect of wiring — otherwise the OMN-16794
coverage would have silently evaporated with the arm change.
"""
contract = _load_contract()
assert contract.db_io is not None

# No assertion on the return value: the claim under test is that resolution
# SUCCEEDS. Under 0.38.9 this raised for every profile; a privilege or
# physical-schema regression makes it raise again, by name, here.
_resolve_projection_database_target(
tuple(contract.db_io.db_tables),
load_topology_profile(profile_name),
)


@pytest.mark.parametrize("profile_name", _PROFILES)
def test_wiring_selects_the_typed_def_b_arm_under_every_topology_profile(
profile_name: str,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""All seven profiles must build the arm. Under 0.38.9 all seven raised."""
"""All seven profiles must wire, and all seven must pick the TYPED arm.

Under 0.38.9 all seven raised (unconstructible). Under 0.38.10 all seven
built the PROJECTION arm, which is the OMN-16767 defect. Under 0.38.11 all
seven must build the typed def-B arm.
"""
contract = _load_contract()

# Pin the DSN rather than inheriting it, so this asserts the same thing on a
Expand All @@ -192,14 +248,19 @@ def test_projection_arm_constructs_under_every_topology_profile(
f"declares db_io"
)

# THE ARM, not just "a dispatcher". A contract whose db_tables came back
# empty (stale core, unpopulated db_io) still yields a perfectly valid
# dispatcher_id — it is just wired to the typed def-B arm instead. That
# silent fallback IS the OMN-16796 defect class, so identity is asserted.
# THE ARM, not just "a dispatcher". Both arms yield a valid dispatcher_id,
# so which one was selected is the entire assertion. The projection arm is
# called out by name because that specific value is the OMN-16767 outage
# signature, not merely "some other arm".
selected = getattr(prepared.dispatcher, "__qualname__", "")
assert selected == _PROJECTION_CALLBACK_QUALNAME, (
f"[{profile_name}] expected the PROJECTION arm "
f"({_PROJECTION_CALLBACK_QUALNAME}), got {selected!r} — the contract "
f"declares db_io, so a different arm means db_tables resolved empty or "
f"the binding fell through"
assert selected != _PROJECTION_CALLBACK_QUALNAME, (
f"[{profile_name}] wiring selected the PROJECTION arm for a handler whose "
f"input is a concrete BaseModel — this is the OMN-16767 regression: the "
f"handler receives a raw input_data dict and dies on its first attribute "
f"access. The installed omnibase-infra has regressed below v0.38.11 "
f"(omnibase_infra#2937)."
)
assert selected == _TYPED_DEF_B_CALLBACK_QUALNAME, (
f"[{profile_name}] expected the TYPED def-B arm "
f"({_TYPED_DEF_B_CALLBACK_QUALNAME}), got {selected!r}"
)
6 changes: 3 additions & 3 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading