From 1984879f4152b48ec9cf4c2a6ec926a7712f5bd2 Mon Sep 17 00:00:00 2001 From: jonahgabriel Date: Thu, 27 Aug 2026 21:38:17 -0400 Subject: [PATCH 1/2] chore(OMN-16815): repin omnibase-infra 0.38.11 and retire the OMN-16767 chain-gate xfail omnibase_infra v0.38.11 is the first release carrying the delegation-arm fixes (#2937, #2943, #2949, #2951). Resolving the lock to it makes the delegation-routing-reducer chain-gate row XPASS under its strict xfail, which is exactly the signal the marker was written to produce, so the marker goes with the bump. The arm-selection assertion in test_projection_arm_constructs.py is INVERTED, not deleted: post-#2937 the correct arm for a handler with a concrete BaseModel input is the typed def-B arm, and the projection arm is now named as the OMN-16767 regression signature. Because wiring no longer reaches _resolve_projection_database_target for this contract, the OMN-16794 constructibility claim is re-asserted directly against that resolver under all seven topology profiles rather than left to evaporate as a side effect of the arm change. --- tests/chains/test_event_chain_gate.py | 18 +-- .../chains/test_projection_arm_constructs.py | 107 ++++++++++++++---- uv.lock | 6 +- 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/tests/chains/test_event_chain_gate.py b/tests/chains/test_event_chain_gate.py index 3e5f7e8574..ab56443b6f 100644 --- a/tests/chains/test_event_chain_gate.py +++ b/tests/chains/test_event_chain_gate.py @@ -168,13 +168,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. ), ) diff --git a/tests/chains/test_projection_arm_constructs.py b/tests/chains/test_projection_arm_constructs.py index 8bc2389638..885b49572a 100644 --- a/tests/chains/test_projection_arm_constructs.py +++ b/tests/chains/test_projection_arm_constructs.py @@ -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 @@ -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, ) @@ -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.._callback" +_TYPED_DEF_B_CALLBACK_QUALNAME = "_make_dispatch_callback.._callback" # Every topology profile checked into omnibase_infra. The original report was # "unconstructible under ALL SEVEN", so all seven are asserted, not a sample. @@ -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 @@ -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}" ) diff --git a/uv.lock b/uv.lock index 166f133f54..f74fa92e9a 100644 --- a/uv.lock +++ b/uv.lock @@ -1635,7 +1635,7 @@ wheels = [ [[package]] name = "omnibase-infra" -version = "0.38.10" +version = "0.38.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "a2a-sdk" }, @@ -1690,9 +1690,9 @@ dependencies = [ { name = "uvicorn" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/ea/35107c3afc238835f6feb75d759e652893add85950f5905a084c957c766c/omnibase_infra-0.38.10.tar.gz", hash = "sha256:ee5d1d22b1abe02a7e3fc377ec7311aaf28e7b5b2ebbd4bf855766106f92c41e", size = 12381538, upload-time = "2026-08-26T01:42:55.343Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/91/0c2657164ac78a3297c219d9eecf22dc8c2a32d3330b1b4294cfa0163d8c/omnibase_infra-0.38.11.tar.gz", hash = "sha256:79861097a8b5911a00cfca8ccb8625c71f1255b2238c0af800acc3dbaab9513d", size = 12765552, upload-time = "2026-08-28T01:21:09.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/63/ab38f06d00c08df3b27df3d2d5ec871c4f0cb6fcbfe441e38b826a197d54/omnibase_infra-0.38.10-py3-none-any.whl", hash = "sha256:3b59634504a551669622f10852e31aaf78fe4d786a086969a96403cc07782a46", size = 5992569, upload-time = "2026-08-26T01:42:49.078Z" }, + { url = "https://files.pythonhosted.org/packages/95/de/d15b07ebc5de624061330d40eb378fd4fc9b9a7a6b1348bdf710e1c29f86/omnibase_infra-0.38.11-py3-none-any.whl", hash = "sha256:447795bc47528e1104a40ed34d1c35c1fb08176a493c632984a42122e1f91527", size = 6068083, upload-time = "2026-08-28T01:21:06.918Z" }, ] [[package]] From 60df58a8282dab0e2a1647973da0bd7b8c9196c8 Mon Sep 17 00:00:00 2001 From: jonahgabriel Date: Thu, 27 Aug 2026 21:53:57 -0400 Subject: [PATCH 2/2] fix(OMN-16815): make the chain gate's tier routability hermetic, not ambient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate passed on a laptop and failed on the CI runner at this pin: ProtocolConfigurationError: [ONEX_CORE_041_INVALID_CONFIGURATION] No tier has a configured endpoint for task_type='summarization'. Nothing about the chain differed. The laptop shell carried live cloud credentials (GEMINI_API_KEY / LLM_GLM_API_KEY / OPEN_ROUTER_API_KEY), so the cheap_cloud rung was routable there; the runner has none, and the local rung was unroutable on both because local-reasoner's endpoint_url is null in the repo default (site-specific, overlay-supplied per OMN-12815/OMN-15807). The gate's verdict was therefore a function of the ambient environment — the OMN-16796 defect class, inside the suite written to catch chains dying silently. A module-level autouse fixture now writes a minimal BIFROST overlay giving local-reasoner a complete but deliberately unreachable chat-completions URL (127.0.0.1:9, the discard port). The node under gate is a routing REDUCER: it emits ModelRoutingDecision and never opens a connection, so an unreachable address costs nothing and keeps the gate server-free. tests/conftest.py clears BIFROST_OVERLAY_PATH unconditionally; a module-level autouse fixture runs after it and wins, which is that fixture's own documented override path. Verified both ways: green with the developer environment intact, and green under `env -u` of every cloud key and every BIFROST_* binding (the CI shape, which reproduced the failure before this change). --- tests/chains/test_event_chain_gate.py | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/chains/test_event_chain_gate.py b/tests/chains/test_event_chain_gate.py index ab56443b6f..8073de803c 100644 --- a/tests/chains/test_event_chain_gate.py +++ b/tests/chains/test_event_chain_gate.py @@ -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: