The outages portal appends the district in parentheses, e.g.
"Медкадры улица, 1 (Заельцовский район)". That suffix hid the house number
from the parser, so every portal address failed house matching and /v1/outages
always returned empty.
- address: NormalizeText drops a trailing "(...)" group (root fix, benefits
every source)
- outages: introduce HouseUnit; the ArcGIS adapter parses the (cleaned) address
at the boundary and stores the structured unit; MatchingHouse matches on the
unit (with a parse fallback for sources that only provide text)
- config: portal.url is now optional (core runs without the events module)
- httpapi: always return "outages": [] (no more omitempty); report
"detail" when the events module is not configured
- tests: regression coverage for the portal district format
- match: require candidate ordinal to equal the query ordinal in fuzzy search
(deterministic typo recall 73% -> 80%, no wrong houses)
- resolver: compatibleVariant guard — LLM may only fix street spelling, keeping
the same words, ordinal and house unit; damerau distance for transpositions
- llm: prompt moved out of code (go:embed default_prompt.txt) with
llm.system_prompt / llm.system_prompt_file overrides; guided JSON parsing
tolerant of thinking blocks
- address: recognize ordinals appearing after the street name
- tests: first coverage for the LLM adapter and config prompt loading; README
Measured (sample 200): deterministic 94.7-95.8% exact / 0 wrong across 4 seeds;
LLM+guard typo 90.7-89.8% / 0 wrong. LLM fallback latency ~1.2-1.9s.