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

@ -141,6 +141,32 @@ def test_inbox_idset_skips_wrong_decision(tmp_path: Path) -> None:
assert [p.run_id for p in hitl.pending(str(outbox), str(inbox))] == ["run-1"]
# --- route classification (Step 3) ----------------------------------------------------------------
def test_route_classification_is_load_bearing(tmp_path: Path) -> None:
"""LOAD-BEARING: ``_matches`` genuinely classifies. Detach it (route everything to ``entries[0]``)
both assertions flip RED: an unmatched proposal would stop being UNROUTABLE, and a two-entry
match would tie-break to ``entries[0]`` instead of the sorted-first id. ``entries[0]`` is
deliberately NOT the sorted-first id, so the tie-break assertion is sensitive to the detach."""
outbox = tmp_path / "outbox"
inbox = tmp_path / "inbox"
config = hitl.RoutingConfig(
entries=[
hitl.RoutingEntry(id="z-vei", allowed_code_prefixes=frozenset({"07"}), expert="Zeta"),
hitl.RoutingEntry(id="a-energi", allowed_code_prefixes=frozenset({"05"}), expert="Alpha"),
]
)
_write_proposal(outbox, "run-A", verdict_id="vA", codes=["99.9"]) # matches nothing
_write_proposal(outbox, "run-C", verdict_id="vC", codes=["05.1", "07.1"]) # matches BOTH
routed = {r.pending.run_id: r for r in hitl.route(str(outbox), str(inbox), config)}
assert routed["run-A"].expert is None # detach → routed to Zeta → RED
assert routed["run-C"].ambiguous is True
assert routed["run-C"].dimension_id == "a-energi" # sorted-first, NOT entries[0] z-vei
assert routed["run-C"].expert == "Alpha"
def test_inbox_idset_skips_malformed_features(tmp_path: Path) -> None:
"""An inbox verdict with all four top-level keys but ``proposal_features`` MISSING ``measure_type``
(which ``verdict_from_dict`` reads would raise) is skipped, so it does NOT clear pending