fix(s51): close 2 review MINORs — non-UTF-8 skip + affected_codes parity

Both live in the tolerant inbox-read path and both contradicted the module's
own "skipped, never raised" / loader-parity docstrings (S5.1 review: ALLOW,
2 MINOR, non-gating):

- UnicodeDecodeError (a *.json hand-saved in Latin-1 with Norwegian æ/ø/å is
  invalid UTF-8) now SKIPPED in hitl._load_json_dict AND
  verdicts.load_verdicts_from_dir — was: crashed pending/route with a raw
  traceback (a ValueError subclass, caught by neither OSError nor
  JSONDecodeError). Fix symmetric across both readers.
- hitl._inbox_verdict_ids now skips a non-iterable affected_codes exactly as
  load_verdicts_from_dir does (frozenset() raises TypeError) — was: marked the
  proposal judged on key-presence alone → a silent false-negative in the
  operator pending queue. hitl-only: verdicts is the correct reference.

TDD: 3 RED-then-green (both UnicodeDecodeError twins + the parity gap) + 1
ground-truth pin locking the loader side so the parity cannot rot.

Gate: pytest → 389 passed, 4 skipped (was 385); ruff check + format clean; mypy clean.
This commit is contained in:
Kjell Tore Guttormsen 2026-07-15 20:59:01 +02:00
commit 7e86896bc8
5 changed files with 131 additions and 4 deletions

View file

@ -212,7 +212,10 @@ def load_verdicts_from_dir(
for file in files:
try:
data = json.loads(file.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
except (OSError, UnicodeDecodeError, json.JSONDecodeError):
# UnicodeDecodeError: a *.json file hand-saved in Latin-1 (Norwegian æ/ø/å) is invalid
# UTF-8 — a per-file tolerant skip, not a raise (a ValueError subclass, caught by neither
# OSError nor JSONDecodeError). Kept in lockstep with hitl._load_json_dict.
continue
if not isinstance(data, dict) or not _REQUIRED_VERDICT_KEYS <= data.keys():
continue