feat(s51): route pending proposals to experts by code-prefix

Gate: pytest tests/test_hitl.py tests/test_hitl_loadbearing.py -k route → 5 passed.
This commit is contained in:
Kjell Tore Guttormsen 2026-07-15 19:38:00 +02:00
commit 62d6b40eae
3 changed files with 162 additions and 0 deletions

View file

@ -213,3 +213,89 @@ def test_routing_config_valid_variant_loads(tmp_path: Path) -> None:
)
assert config.entries[0].expert == "Per"
assert config.entries[0].allowed_code_prefixes == frozenset()
# --- route() classification -----------------------------------------------------------------------
def test_route_by_code_prefix_with_measure_optional(tmp_path: Path) -> None:
"""A proposal with a REAL prose measure routes by code prefix when the entry sets no measure gate
(``allowed_measure_types`` empty = "any") proving measure-optional works on real prose, not a
fixture-tuned token."""
outbox = tmp_path / "outbox"
inbox = tmp_path / "inbox"
_write_proposal(
outbox, "run-1", verdict_id="v1", measure="LED-retrofit av kontorbelysning", codes=["05.2"]
)
config = hitl.RoutingConfig(
entries=[
hitl.RoutingEntry(id="energi", allowed_code_prefixes=frozenset({"05"}), expert="Ola")
]
)
routed = hitl.route(str(outbox), str(inbox), config)
assert len(routed) == 1
r = routed[0]
assert r.expert == "Ola"
assert r.dimension_id == "energi"
assert r.ambiguous is False
assert r.pending.measure == "LED-retrofit av kontorbelysning"
def test_route_unroutable_when_no_entry_matches(tmp_path: Path) -> None:
"""A proposal whose codes match no entry is emitted UNROUTABLE (expert/dimension None), never
silently dropped."""
outbox = tmp_path / "outbox"
inbox = tmp_path / "inbox"
_write_proposal(outbox, "run-1", verdict_id="v1", codes=["99.9"])
config = hitl.RoutingConfig(
entries=[
hitl.RoutingEntry(id="energi", allowed_code_prefixes=frozenset({"05"}), expert="Ola")
]
)
routed = hitl.route(str(outbox), str(inbox), config)
assert len(routed) == 1
assert routed[0].expert is None
assert routed[0].dimension_id is None
assert routed[0].ambiguous is False
def test_route_ambiguous_uses_sorted_first_tie_break(tmp_path: Path) -> None:
"""A proposal admitted by two entries routes to the sorted-first ``entry.id`` (deterministic) and
is flagged ``ambiguous``."""
outbox = tmp_path / "outbox"
inbox = tmp_path / "inbox"
_write_proposal(outbox, "run-1", verdict_id="v1", codes=["05.2"])
config = hitl.RoutingConfig(
entries=[
hitl.RoutingEntry(id="b-energi", allowed_code_prefixes=frozenset({"05"}), expert="Beta"),
hitl.RoutingEntry(id="a-energi", allowed_code_prefixes=frozenset({"05"}), expert="Alpha"),
]
)
r = hitl.route(str(outbox), str(inbox), config)[0]
assert r.ambiguous is True
assert r.dimension_id == "a-energi"
assert r.expert == "Alpha"
def test_route_measure_constrained_entry_filters(tmp_path: Path) -> None:
"""A measure-constrained entry filters out a proposal whose prose measure is not in
``allowed_measure_types`` (unroutable); the same entry routes a matching measure proving the
gate fires only on the mismatch."""
inbox = tmp_path / "inbox"
config = hitl.RoutingConfig(
entries=[
hitl.RoutingEntry(
id="energi",
allowed_measure_types=frozenset({"scope_reduction"}),
allowed_code_prefixes=frozenset({"05"}),
expert="Ola",
)
]
)
miss = tmp_path / "miss"
_write_proposal(miss, "run-1", verdict_id="v1", measure="rate_renegotiation", codes=["05.2"])
assert hitl.route(str(miss), str(inbox), config)[0].expert is None
hit = tmp_path / "hit"
_write_proposal(hit, "run-1", verdict_id="v1", measure="scope_reduction", codes=["05.2"])
assert hitl.route(str(hit), str(inbox), config)[0].expert == "Ola"