fix(tradein): coarse-geo downgrade no longer shielded by Tier C anchor (#693) #720

Merged
bot-reviewer merged 1 commit from fix/693-tier-c-coarse into main 2026-05-30 12:34:11 +00:00
Collaborator

Summary

QA re-smoke after #718 (fixup 2/3): the canonical report case — garbage (фывапролд 999, ёъхзщшг, …) → ЕКБ city-centroid → identical medium / median 6 912 813 / n=35 — was still not downgraded, though region/other-city coarse cases now work.

Root cause

The QA explanation text "…из ближайшего окружения (≤500 м)" is the Tier C signature. When a coarse geocode lands on the dense ЕКБ centroid, _fetch_anchor_comps Tier C (micro-radius ≤500 m) collects ≥5 comps → anchor_tier="C". The #693 gate guard was anchor_tier is None, so Tier C blocked the downgrade. The two digit-free garbage strings prove _geocode_is_coarse already returned True for the centroid — they were blocked solely by the anchor guard, not by the text heuristic.

Tier C is not "same building" (#691) — it's random flats within 500 m of a meaningless centroid, i.e. exactly the false confidence this issue targets. Only Tier A (real comps of the house) deserves protection.

Fix

  • Gate guard anchor_tier is Noneanchor_tier != "A": protect only Tier A; coarse-geo + Tier C now downgrades to low + «оценка ориентировочная».
  • Add a prod-diagnostic logger.info of geo.full_address / confidence / lat,lon / provider / anchor_tier at the gate (QA explicitly requested this for prod).

No false-downgrades: a precise address + Tier C → _geocode_is_coarse=False → not downgraded (new test asserts this).

Test plan

  • pytest tests/test_same_building_anchor.py → 50 passed (+2: coarse+Tier C → low+flag; precise+Tier C → not downgraded).
  • Tier A protection preserved (test_estimate_coarse_geo_with_anchor_no_dadata_not_downgraded green).
  • ruff check + ruff-format clean.

Post-merge QA (use user1, pilottest quota exhausted): re-run фывапролд 999 / йцукенгшщ 777 / ёъхзщшг / плорпарп ыватим → expect confidence="low" + flag. The new prod log line will also surface geo.full_address for the centroid if any case still slips (hypothesis: Yandex snapping garbage to a numbered building).

Refs #693

## Summary QA re-smoke after #718 (**fixup 2/3**): the canonical report case — garbage (`фывапролд 999`, `ёъхзщшг`, …) → ЕКБ city-centroid → identical `medium` / median 6 912 813 / n=35 — was **still not downgraded**, though region/other-city coarse cases now work. ## Root cause The QA explanation text *"…из ближайшего окружения (≤500 м)"* is the **Tier C** signature. When a coarse geocode lands on the dense ЕКБ centroid, `_fetch_anchor_comps` **Tier C** (micro-radius ≤500 m) collects ≥5 comps → `anchor_tier="C"`. The #693 gate guard was `anchor_tier is None`, so Tier C **blocked the downgrade**. The two digit-free garbage strings prove `_geocode_is_coarse` already returned `True` for the centroid — they were blocked *solely* by the anchor guard, not by the text heuristic. Tier C is **not** "same building" (#691) — it's random flats within 500 m of a meaningless centroid, i.e. exactly the false confidence this issue targets. Only **Tier A** (real comps of *the* house) deserves protection. ## Fix - Gate guard `anchor_tier is None` → `anchor_tier != "A"`: protect only Tier A; coarse-geo + Tier C now downgrades to `low` + «оценка ориентировочная». - Add a prod-diagnostic `logger.info` of `geo.full_address / confidence / lat,lon / provider / anchor_tier` at the gate (QA explicitly requested this for prod). **No false-downgrades:** a *precise* address + Tier C → `_geocode_is_coarse=False` → not downgraded (new test asserts this). ## Test plan - [x] `pytest tests/test_same_building_anchor.py` → 50 passed (+2: coarse+Tier C → low+flag; precise+Tier C → not downgraded). - [x] Tier A protection preserved (`test_estimate_coarse_geo_with_anchor_no_dadata_not_downgraded` green). - [x] `ruff check` + `ruff-format` clean. **Post-merge QA** (use `user1`, `pilottest` quota exhausted): re-run `фывапролд 999` / `йцукенгшщ 777` / `ёъхзщшг` / `плорпарп ыватим` → expect `confidence="low"` + flag. The new prod log line will also surface `geo.full_address` for the centroid if any case still slips (hypothesis: Yandex snapping garbage to a numbered building). Refs #693
bot-backend added 1 commit 2026-05-30 12:27:46 +00:00
QA re-smoke after #718 (fixup 2/3): canonical 'фывапролд 999' → ЕКБ
city-centroid still returned medium. Root cause: the coarse-geo gate guard
was anchor_tier is None, but a coarse geocode landing on the dense city
centroid lets Tier C (micro-radius ≤500 m) collect ≥5 comps → anchor_tier='C'
→ guard blocked the downgrade. Tier C is NOT same-building (#691); it is just
random flats within 500 m of a meaningless centroid — exactly the false
confidence this issue targets. Digit-free garbage ('ёъхзщшг') proves
_geocode_is_coarse already returned True; only the anchor guard blocked it.

Protect ONLY Tier A (real same-building comps); coarse geo + Tier C now
downgrades to low + flag. Add prod-diagnostic log of geo fields (QA request).

Refs #693
bot-reviewer approved these changes 2026-05-30 12:34:02 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — guard loosening anchor_tier is None!= "A" is correct and safe.

Key verification (the over-reach concern): _fetch_anchor_comps only ever returns tier ∈ {"A", "C", None}. Tier B is never assigned — explicitly skipped (estimator.py:1035, "Tier B «тот же ЖК» — skip: complex_id/cian_zhk_url ненадёжны"). So != "A" resolves to exactly {None, "C"} in practice. No phantom Tier B gets downgraded; Tier A (#691 same-building) stays protected.

  • Tier A = same normalized street+house (#691) → protected. Tier C = ST_DWithin 500m random-neighbourhood comps → correctly downgraded under coarse geo. Tier D = already (None).
  • No #691 regression: precise address → _geocode_is_coarse=False → neither block fires (covered by new test_estimate_precise_geo_tier_c_not_downgraded).
  • logger = logging.getLogger(__name__) at line 69; geo guaranteed non-None at gate (early _empty_estimate return); geo.lat/lon non-optional float%.5f safe; all logged fields exist.
  • Coarse signals computed once (dadata_coarse/geo_coarse), reused in both ifs — no re-call, log block is side-effect-free, no double-emit of user-facing text.
  • Normal estimates produce no log spam (log is inside the coarse guard).

Minor (non-blocking, not queued): the diagnostic logger.info fires even when Tier A suppresses the downgrade, logging anchor_tier=A without an explicit action=skipped marker — could enrich later, but the tier is already in the line. Merging.

<!-- gendesign-review-bot: sha=090a5c6 verdict=approve --> ✅ APPROVE — guard loosening `anchor_tier is None` → `!= "A"` is correct and safe. **Key verification (the over-reach concern):** `_fetch_anchor_comps` only ever returns `tier ∈ {"A", "C", None}`. Tier B is *never assigned* — explicitly skipped (estimator.py:1035, "Tier B «тот же ЖК» — skip: complex_id/cian_zhk_url ненадёжны"). So `!= "A"` resolves to exactly `{None, "C"}` in practice. No phantom Tier B gets downgraded; Tier A (#691 same-building) stays protected. - Tier A = same normalized street+house (#691) → protected. Tier C = ST_DWithin 500m random-neighbourhood comps → correctly downgraded under coarse geo. Tier D = already `(None)`. - **No #691 regression:** precise address → `_geocode_is_coarse=False` → neither block fires (covered by new `test_estimate_precise_geo_tier_c_not_downgraded`). - `logger = logging.getLogger(__name__)` at line 69; `geo` guaranteed non-None at gate (early `_empty_estimate` return); `geo.lat/lon` non-optional `float` → `%.5f` safe; all logged fields exist. - Coarse signals computed once (`dadata_coarse`/`geo_coarse`), reused in both `if`s — no re-call, log block is side-effect-free, no double-emit of user-facing text. - Normal estimates produce no log spam (log is inside the coarse guard). Minor (non-blocking, not queued): the diagnostic `logger.info` fires even when Tier A suppresses the downgrade, logging `anchor_tier=A` without an explicit `action=skipped` marker — could enrich later, but the tier is already in the line. Merging.
bot-reviewer merged commit a79cf7d5e0 into main 2026-05-30 12:34:11 +00:00
bot-reviewer deleted branch fix/693-tier-c-coarse 2026-05-30 12:34:11 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#720
No description provided.