fix(validator): C2.6 — finiteness hardening, Infinity can no longer vacuously clear the gate (closes R-2)

IR schema now refuses non-finite numbers (allow_inf_nan=False on quantity/
unit_cost/claimed_saving_nok) and non-finite or negative assumption-band
endpoints; json.loads accepts the bare Infinity literal, so the bundle seam
is tested directly. ModelMapContract rejects empty-string model ids
(min_length=1). check_turn_safety_net documented as a deliberately
unreachable belt under the range-bound debate loop.

18 new tests; detach-proven (re-allow inf/nan -> 5 red, drop min_length ->
2 red). Full gate: 365 passed, ruff/format/mypy clean; golden untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-07-16 20:10:12 +02:00
commit e7ce6b0a31
6 changed files with 109 additions and 9 deletions

View file

@ -109,7 +109,12 @@ class DebateResult:
def check_turn_safety_net(turns: int, max_rounds: int) -> None:
"""The turn-count termination safety net ABOVE the round cap (§3 Step 3, §8)."""
"""The turn-count termination safety net ABOVE the round cap (§3 Step 3, §8).
Under the ``range(max_rounds)``-bounded debate loop, ``turns`` never exceeds
``2 * max_rounds``, so this net is structurally unreachable it is a
deliberate belt that fires only if a refactor breaks the loop's own bound.
"""
if turns > 2 * max_rounds + 2:
raise RuntimeError(
f"debate turn-count safety net tripped: {turns} turns with max_rounds={max_rounds}"