From f01890d00eecd7911aebdfe3f7bb4a6d21aeac09 Mon Sep 17 00:00:00 2001 From: jonahgabriel Date: Wed, 7 Oct 2026 19:27:14 -0400 Subject: [PATCH] fix(OMN-19081): remove fallback immutable grain projections PR #3943 (b1981c929) introduced reading key_grain from contracts for the projection_delta_dropped health dimension, retaining a four-entry literal fallback, FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS, for unresolved grains. This fallback was originally required because a pre-PR proof on dogfood-101 measured omnimarket 0.4.178, which predates key_grain (introduced in 0.4.181), causing content-addressed exposures to read DEGRADED. A read-only check on the .201 dev lane on 2026-09-21 confirmed omnimarket 0.4.185 in the runtime container, where 45 of 409 contracts declare key_grain. The omnimarket workspace is now 0.4.303. This change, tracked under OMN-19093, deletes the literal, the fallback_immutable_projections parameter of evaluate_projection_apply_flow, the fallback_exempted_projections verdict field, and the fallback note in the dimension detail. The monitor no longer passes this parameter. The contract declaration is now the sole source of an exemption. Behavior updates: unresolved grains are graded and reported under apply_flow_key_grain_unresolved, never exempted. Declared mutable grains are graded. Declared immutable grains (HandlerProjectionSessionReplay, HandlerProjectionWorkEvents) are excluded and read HEALTHY on a rising gauge. Tests in test_key_grain_from_contract_omn19081.py were updated to cover declared and undeclared cases and verify the removal of the literal and parameter; the seam test no longer passes the fallback. 26 tests pass in the two key-grain files. The constant-literal-coordinate refusal gate remains in omnimarket's validator, not in this repo. --- .../model_projection_apply_flow_verdict.py | 11 --- .../runtime/health/projection_apply_flow.py | 88 +++-------------- .../service_runtime_health_monitor.py | 7 -- .../test_key_grain_contract_seam_omn19081.py | 22 ++--- .../test_key_grain_from_contract_omn19081.py | 96 +++++++------------ 5 files changed, 60 insertions(+), 164 deletions(-) diff --git a/src/omnibase_infra/models/health/model_projection_apply_flow_verdict.py b/src/omnibase_infra/models/health/model_projection_apply_flow_verdict.py index aa6f3f9573..3dce7c70b9 100644 --- a/src/omnibase_infra/models/health/model_projection_apply_flow_verdict.py +++ b/src/omnibase_infra/models/health/model_projection_apply_flow_verdict.py @@ -99,17 +99,6 @@ class ModelProjectionApplyFlowVerdict(BaseModel): "would hide that the declaration is the thing to fix." ), ) - fallback_exempted_projections: tuple[str, ...] = Field( - default=(), - description=( - "Projections exempted by the INTERIM fallback list rather than by " - "a contract declaration (OMN-19081). Rendered rather than hidden: " - "an exemption resting on a literal in this repository is weaker " - "evidence than one the contract states, and a reader deciding " - "whether the literal can be deleted needs to see which exposures " - "still depend on it." - ), - ) total_consumed: int = Field( default=0, ge=0, description="Envelopes dispatched across all windows in scope" ) diff --git a/src/omnibase_infra/runtime/health/projection_apply_flow.py b/src/omnibase_infra/runtime/health/projection_apply_flow.py index 95db750176..7dea78076f 100644 --- a/src/omnibase_infra/runtime/health/projection_apply_flow.py +++ b/src/omnibase_infra/runtime/health/projection_apply_flow.py @@ -38,9 +38,10 @@ fixed source coordinate and are CORRECT to do so. Both key on a content-addressed, immutable grain, so the serving cache only ever compares a key against a delta derived from the same source event, and the drop is the -intended idempotence rather than lost data. They are enumerated and excluded by -name rather than thresholded over, because a threshold wide enough to cover -them is wide enough to hide the defect. +intended idempotence rather than lost data. The exposure's contract declares +that grain (``key_grain: immutable``) and the declaration is the only thing +that excludes it; it is excluded rather than thresholded over, because a +threshold wide enough to cover them is wide enough to hide the defect. Shape ----- @@ -109,44 +110,6 @@ #: healthy projection degraded. DELTA_DROP_MIN_ACCUMULATION: int = 10 -#: The interim fallback exemption set, consulted ONLY for an exposure whose -#: contract resolves no key grain (OMN-19081, operator ruling 2026-09-21). -#: -#: The contract declaration is the source of truth and WINS wherever it -#: answers; this list is not a second opinion, it is what covers the window in -#: which a deployed runtime carries an omnimarket older than the one that -#: introduced ``key_grain``. -#: -#: THIS EXISTS BECAUSE A PRE-PR PROOF FAILED, and the measurement is worth -#: keeping next to the constant. Deleting the literal outright and reading the -#: contract alone was measured on dogfood-101 against a runtime carrying -#: omnimarket 0.4.178: the declaration landed in 0.4.181, none of that image's -#: 409 contracts carried the field, so both content-addressed exposures became -#: unresolved, their intended idempotence graded as loss, and -#: ``projection_delta_dropped`` went DEGRADED on healthy behaviour. On a lane -#: whose container probe runs with ``--degraded-policy fail`` that is not a -#: false alarm, it is an outage. -#: -#: The two alternatives were refused for stated reasons: refusing to grade -#: below a version floor is a gate blinding itself, and exempting every -#: unresolved exposure exempts ALL of them on a pre-0.4.181 runtime, so the -#: dimension would grade nothing for the whole window. -#: -#: Both identity forms are listed because the projection identity recorded at -#: dispatch is the handler class name while the contract and every ticket name -#: the node. Matching is EXACT: a substring rule would exempt the next handler -#: whose name happens to contain one of these. -#: -#: DELETION IS TRIGGERED BY A READING, not by a date: the deployed dev-lane -#: omnimarket at or above 0.4.181, read from inside the runtime container -#: rather than from a pin. Tracked in the follow-up on OMN-19081. -FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS: tuple[str, ...] = ( - "HandlerProjectionSessionReplay", - "HandlerProjectionWorkEvents", - "node_projection_session_replay", - "node_projection_work_events", -) - #: Cap on names rendered into a dimension detail. The detail rides every #: ``/health`` response and every health event; a fleet-wide breakage must not #: turn it into a log dump. Mirrors ``projection_liveness.MAX_NAMED_PROJECTIONS``. @@ -164,9 +127,8 @@ #: The outcome token for a registered projection whose contract-declared key #: grain could not be resolved (OMN-19081). Such a projection is never #: exempted on the strength of the missing declaration itself -- that would -#: hide a real accumulation behind a missing field. It is graded unless the -#: interim FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS list covers it, and it is -#: named under this token either way so a reader can tell it from +#: hide a real accumulation behind a missing field. It is graded, and it is +#: named under this token so a reader can tell it from #: one that genuinely declares a mutable grain. The remedies differ: one is a #: contract edit, the other is a real investigation. OUTCOME_KEY_GRAIN_UNRESOLVED: str = "apply_flow_key_grain_unresolved" @@ -186,7 +148,6 @@ def evaluate_projection_apply_flow( registered_projections: Iterable[str], immutable_grain_projections: Iterable[str] = (), grain_unresolved_projections: Iterable[str] = (), - fallback_immutable_projections: Iterable[str] = (), ) -> ModelProjectionApplyFlowVerdict: """Grade consume-versus-write and drop accumulation over the closed windows. @@ -218,19 +179,10 @@ def evaluate_projection_apply_flow( registered = tuple(sorted({p for p in registered_projections if p})) declared_immutable = frozenset(p for p in immutable_grain_projections if p) unresolved = frozenset(p for p in grain_unresolved_projections if p) - fallback = frozenset(p for p in fallback_immutable_projections if p) - # RESOLUTION ORDER, and the order is the whole design. The contract wins - # wherever it answers -- a projection the contract declares MUTABLE is - # graded even if it appears in the fallback, so the literal can never - # override a live declaration. The fallback is reached only where the - # contract resolved nothing, which is the deployed-version window. - fallback_exempt = frozenset(p for p in unresolved if p in fallback) - # .union() rather than the | operator: the repository non-optional-union - # ratchet counts this line as a TYPE union and trips on it, which is a - # miscount rather than a finding. Written the explicit way instead of - # raising the ratchet, because raising a gate to fit a false positive - # is how the gate stops meaning anything. - exempt = declared_immutable.union(fallback_exempt) + # The contract declaration is the only source of an exemption. A + # projection whose grain did not resolve is graded and named, never + # exempted on the strength of the missing declaration itself. + exempt = declared_immutable consumed_by: dict[str, int] = dict.fromkeys(registered, 0) upserted_by: dict[str, int] = dict.fromkeys(registered, 0) @@ -296,7 +248,6 @@ def evaluate_projection_apply_flow( indeterminate_drop_projections=tuple(indeterminate), excluded_immutable_grain=tuple(sorted(exempt & set(in_scope))), grain_unresolved_projections=tuple(sorted(unresolved & set(in_scope))), - fallback_exempted_projections=tuple(sorted(fallback_exempt & set(in_scope))), total_consumed=sum(consumed_by.values()), total_upserted=sum(upserted_by.values()), total_refused_by_guard=sum(refused_by.values()), @@ -427,20 +378,10 @@ def describe_projection_delta_dropped(verdict: ModelProjectionApplyFlowVerdict) unresolved = ( f" ({OUTCOME_KEY_GRAIN_UNRESOLVED}: " f"{len(verdict.grain_unresolved_projections)} projection(s) " - "declare no resolvable key grain. Each is graded unless the " - "interim fallback below covers it, and none is exempted on the " - "strength of the missing declaration alone: " + "declare no resolvable key grain. Each is graded, and none is " + "exempted on the strength of the missing declaration alone: " f"{_name_list(verdict.grain_unresolved_projections)})" ) - fallback_note = "" - if verdict.fallback_exempted_projections: - fallback_note = ( - f" [{len(verdict.fallback_exempted_projections)} of those " - "exemptions came from the INTERIM fallback list rather than from " - "a contract declaration, because this runtime carries an " - "omnimarket predating key_grain: " - f"{_name_list(verdict.fallback_exempted_projections)}]" - ) indeterminate = "" if verdict.indeterminate_drop_projections: indeterminate = ( @@ -454,7 +395,7 @@ def describe_projection_delta_dropped(verdict: ModelProjectionApplyFlowVerdict) return ( f"No projection's discarded-delta count rose across " f"{verdict.observed_window_count} apply window(s)" - f"{indeterminate}{unresolved}{exempted}{fallback_note}" + f"{indeterminate}{unresolved}{exempted}" ) return ( f"{len(verdict.drop_accumulating_projections)} projection(s) discarded " @@ -462,13 +403,12 @@ def describe_projection_delta_dropped(verdict: ModelProjectionApplyFlowVerdict) f"window(s) — the serving cache consumed them and kept nothing, which " f"reads as fresh at zero lag: " f"{_name_list(verdict.drop_accumulating_projections)}" - f"{indeterminate}{unresolved}{exempted}{fallback_note}" + f"{indeterminate}{unresolved}{exempted}" ) __all__: list[str] = [ "APPLY_DIVERGENCE_MIN_CONSUMED", - "FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS", "DELTA_DROP_MIN_ACCUMULATION", "DELTA_DROP_MIN_RISING_WINDOWS", "MAX_NAMED_PROJECTIONS", diff --git a/src/omnibase_infra/services/service_runtime_health_monitor.py b/src/omnibase_infra/services/service_runtime_health_monitor.py index 242fbf463a..f4ce70a202 100644 --- a/src/omnibase_infra/services/service_runtime_health_monitor.py +++ b/src/omnibase_infra/services/service_runtime_health_monitor.py @@ -58,7 +58,6 @@ ProtocolDispatchDeadlineSource, ) from omnibase_infra.runtime.health.projection_apply_flow import ( - FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS, describe_projection_apply_divergence, describe_projection_delta_dropped, evaluate_projection_apply_flow, @@ -1138,12 +1137,6 @@ async def run_once(self) -> ModelRuntimeHealthCheckEvent: registered_projections=apply_counters.registered_projections(), immutable_grain_projections=apply_counters.immutable_grain_projections(), grain_unresolved_projections=apply_counters.grain_unresolved_projections(), - # OMN-19081, operator ruling 2026-09-21. Consulted ONLY where the - # contract resolved nothing, which is the window in which a - # deployed runtime predates the key_grain declaration. Deleting - # the literal outright was measured on dogfood-101 to turn the two - # content-addressed exposures DEGRADED on healthy behaviour. - fallback_immutable_projections=FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS, ) dimensions.append( ModelRuntimeHealthDimension( diff --git a/tests/integration/test_key_grain_contract_seam_omn19081.py b/tests/integration/test_key_grain_contract_seam_omn19081.py index ccbe11f255..920a495247 100644 --- a/tests/integration/test_key_grain_contract_seam_omn19081.py +++ b/tests/integration/test_key_grain_contract_seam_omn19081.py @@ -10,9 +10,9 @@ Those are different failures. Every accessor can be correct while the wiring never passes a grain, and that shape ships a silent regression past every -unit test in the sibling module: the exemption would quietly come from the -fallback on every runtime, including ones whose contracts declare it, and -nothing would say so. +unit test in the sibling module: no exemption would ever be taken, on any +runtime, including ones whose contracts declare it, and the live content- +addressed exposures would read as loss. So this walks the chain the runtime walks: @@ -27,7 +27,7 @@ - OMN-19081: this change - OMN-18908: the contract field it reads - OMN-18910: the dimension it feeds - - OMN-19093: deleting the fallback once every runtime declares + - OMN-19093: the version-window fallback this change deleted """ from __future__ import annotations @@ -43,7 +43,6 @@ resolve_key_grain, ) from omnibase_infra.runtime.health.projection_apply_flow import ( - FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS, evaluate_projection_apply_flow, projection_delta_dropped_status, ) @@ -111,12 +110,11 @@ def _rising_verdict(projection: str = "_Writer") -> object: registered_projections=counters.registered_projections(), immutable_grain_projections=counters.immutable_grain_projections(), grain_unresolved_projections=counters.grain_unresolved_projections(), - fallback_immutable_projections=FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS, ) class _Writer: - """A projection whose class name is NOT in the fallback list. + """A projection whose class name this repository has never listed. Deliberately unknown to this repository, so an exemption it receives can only have come from the contract. @@ -139,7 +137,6 @@ def test_a_declared_immutable_grain_reaches_the_verdict_through_the_seam( verdict = _rising_verdict() assert verdict.excluded_immutable_grain == ("_Writer",) - assert verdict.fallback_exempted_projections == () assert projection_delta_dropped_status(verdict) == "HEALTHY" @@ -166,11 +163,11 @@ def test_negative_control_a_declared_mutable_grain_still_grades( def test_an_undeclared_grain_is_graded_and_named_rather_than_exempted( tmp_path: Path, ) -> None: - """The version window, for a projection the fallback does not cover. + """No declaration: graded and reported rather than silently exempted. - This is the case the pre-PR proof measured on a runtime predating the - declaration: no grain resolves, the fallback does not name this handler, - so it is graded and reported rather than silently exempted. + A contract that declares no grain resolves none, and there is no list to + fall back on, so the projection is graded and named under the unresolved + token. """ handler = _Writer() _drive(handler, _contract(tmp_path, None)) @@ -178,7 +175,6 @@ def test_an_undeclared_grain_is_graded_and_named_rather_than_exempted( verdict = _rising_verdict() assert verdict.grain_unresolved_projections == ("_Writer",) - assert verdict.fallback_exempted_projections == () assert projection_delta_dropped_status(verdict) == "DEGRADED" diff --git a/tests/unit/runtime/health/test_key_grain_from_contract_omn19081.py b/tests/unit/runtime/health/test_key_grain_from_contract_omn19081.py index b5d964366b..7cf1cb0fa4 100644 --- a/tests/unit/runtime/health/test_key_grain_from_contract_omn19081.py +++ b/tests/unit/runtime/health/test_key_grain_from_contract_omn19081.py @@ -28,10 +28,14 @@ from __future__ import annotations +import inspect from pathlib import Path import pytest +from omnibase_infra.models.health.model_projection_apply_flow_verdict import ( + ModelProjectionApplyFlowVerdict, +) from omnibase_infra.runtime.auto_wiring import handler_wiring from omnibase_infra.runtime.health import projection_apply_flow from omnibase_infra.runtime.health.projection_apply_flow import ( @@ -70,19 +74,20 @@ def _contract(tmp_path: Path, *grains: str | None) -> Path: # -------------------------------------------------------------------------- -def test_ac1_the_unconditional_exemption_list_is_gone() -> None: - """The literal that applied unconditionally no longer exists. +def test_ac1_no_literal_exemption_list_remains() -> None: + """No literal exemption list exists, conditional or otherwise. - REVISED after the pre-PR proof failed and the operator ruled, 2026-09-21. - The first version of this test asserted no literal at all, and that shape - of the change was measured on dogfood-101 to turn both content-addressed - exposures DEGRADED on a runtime carrying an omnimarket predating the - declaration. What is required is not the absence of a literal but the - absence of an UNCONDITIONAL one: the surviving list is reached only where - the contract resolves nothing, and its name says so. + The deployed runtime carries an omnimarket past the release that + introduced ``key_grain``, so the version-window fallback OMN-19081 kept + (deletion tracked as OMN-19093) is gone. The declaration is the only + source of an exemption. """ assert not hasattr(projection_apply_flow, "IMMUTABLE_GRAIN_PROJECTIONS") - assert projection_apply_flow.FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS + assert not hasattr(projection_apply_flow, "FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS") + assert ( + "fallback_immutable_projections" + not in inspect.signature(evaluate_projection_apply_flow).parameters + ) def test_ac1_an_immutable_declaration_is_read_from_the_contract( @@ -333,9 +338,8 @@ def handle(self, input_data: dict[str, object]) -> dict[str, int]: # -------------------------------------------------------------------------- -# The fallback, and its resolution order. Operator ruling 2026-09-21, option -# two, after the pre-PR proof on dogfood-101 measured the literal-free shape -# turning healthy behaviour DEGRADED on a pre-declaration runtime. +# With the fallback deleted, the declaration is the only source of an +# exemption: a name no contract declares immutable is graded. # -------------------------------------------------------------------------- @@ -349,72 +353,46 @@ def _rising_gauge(projection: str, grain: str | None) -> ProjectionApplyCounters return counters -def _verdict(counters: ProjectionApplyCounters) -> object: +def _verdict(counters: ProjectionApplyCounters) -> ModelProjectionApplyFlowVerdict: return evaluate_projection_apply_flow( windows=counters.retained_windows(), registered_projections=counters.registered_projections(), immutable_grain_projections=counters.immutable_grain_projections(), grain_unresolved_projections=counters.grain_unresolved_projections(), - fallback_immutable_projections=( - projection_apply_flow.FALLBACK_IMMUTABLE_GRAIN_PROJECTIONS - ), ) -def test_the_0_4_178_replay_reads_healthy() -> None: - """The exact case the proof failed on, now the regression test. - - A runtime whose omnimarket predates key_grain resolves no grain, so the - fallback carries the exemption and the intended idempotence of a - content-addressed exposure does not alarm. - """ - verdict = _verdict(_rising_gauge("HandlerProjectionWorkEvents", None)) +@pytest.mark.parametrize( + "projection", + ["HandlerProjectionSessionReplay", "HandlerProjectionWorkEvents"], +) +def test_the_two_live_exemptions_pass_on_their_declaration(projection: str) -> None: + """A rising gauge on a declared-immutable live exposure reads healthy.""" + verdict = _verdict(_rising_gauge(projection, "immutable")) assert verdict.drop_accumulating_projections == () + assert verdict.excluded_immutable_grain == (projection,) assert projection_delta_dropped_status(verdict) == "HEALTHY" - assert verdict.fallback_exempted_projections == ("HandlerProjectionWorkEvents",) -def test_the_contract_wins_over_the_fallback() -> None: - """POSITIVE CONTROL for the resolution order, and the point of the change. - - A projection named in the fallback whose contract declares it MUTABLE is - GRADED. The literal can never override a live declaration, which is what - keeps it a window-cover rather than a second opinion. - """ +def test_a_declared_mutable_grain_is_graded() -> None: + """A mutable declaration is graded, even on a name that was once exempt.""" verdict = _verdict(_rising_gauge("HandlerProjectionWorkEvents", "mutable")) assert verdict.drop_accumulating_projections == ("HandlerProjectionWorkEvents",) - assert verdict.fallback_exempted_projections == () + assert verdict.excluded_immutable_grain == () assert projection_delta_dropped_status(verdict) == "DEGRADED" -def test_a_contract_declared_immutable_is_not_credited_to_the_fallback() -> None: - """A name the fallback has never heard of, exempted by its declaration.""" - verdict = _verdict(_rising_gauge("HandlerNeverSeenBefore", "immutable")) - assert verdict.drop_accumulating_projections == () - assert verdict.fallback_exempted_projections == () - assert verdict.excluded_immutable_grain == ("HandlerNeverSeenBefore",) - - -def test_negative_control_absent_from_both_is_graded() -> None: - """Unresolved and not in the fallback: graded, never exempted. +def test_an_undeclared_grain_is_graded_and_named_not_exempted() -> None: + """No declaration means graded, including a name the old literal listed. - Without this the fallback could be widened to everything and every test - above would still pass. - """ - verdict = _verdict(_rising_gauge("HandlerProjectionRunnerFleet", None)) - assert verdict.drop_accumulating_projections == ("HandlerProjectionRunnerFleet",) - assert verdict.fallback_exempted_projections == () - assert projection_delta_dropped_status(verdict) == "DEGRADED" - - -def test_an_unresolved_grain_is_still_reported_even_when_the_fallback_exempts() -> None: - """The token is a REPORT, not an exemption, per the ruling. - - A reader deciding whether the literal can be deleted has to be able to see - which exposures still depend on it. + The deleted literal named HandlerProjectionWorkEvents. Without a + declaration it now reads DEGRADED and is reported under the unresolved + token, so the missing declaration is what gets fixed. """ verdict = _verdict(_rising_gauge("HandlerProjectionWorkEvents", None)) + assert verdict.drop_accumulating_projections == ("HandlerProjectionWorkEvents",) assert verdict.grain_unresolved_projections == ("HandlerProjectionWorkEvents",) + assert verdict.excluded_immutable_grain == () + assert projection_delta_dropped_status(verdict) == "DEGRADED" detail = projection_apply_flow.describe_projection_delta_dropped(verdict) assert projection_apply_flow.OUTCOME_KEY_GRAIN_UNRESOLVED in detail - assert "INTERIM fallback" in detail