fix(tradein/matching): Tier-2a city-guard для non-ЕКБ coord-less (п.2, defense-in-depth) #2502

Merged
bot-backend merged 1 commit from fix/tradein-oblast-tier2a-cityguard into main 2026-07-12 21:04:29 +00:00
Collaborator

п.2 (#2500 follow-up) — Tier-2a city-guard (defense-in-depth, LATENT)

resolve_city_token() в normalize.py + guard в matching/houses.py: coord-less карточка с resolved НЕ-ЕКБ городом → skip Tier-2a/2b → New house (не мис-баскет в ЕКБ-дом). ЕКБ happy-path байт-в-байт (guard активен только при lat=lon=None И resolved non-ЕКБ; ЕКБ bare/city-qualified/coord-bearing — не затронуты). Zero prod-impact (oblast-sweep выключен). 66+155 тестов, live-verified (ЕКБ-alias 6675 fingerprint сохранён; synthetic Н.Тагил → skip).

⚠️ ВАЖНО — что этот PR НЕ закрывает

Реальные Avito SERP-карточки bare (только улица+№, БЕЗ города — 0 oblast-токенов, 2.1% «екатеринбург»). Для bare-карточки resolve_city_token→None → guard дремлет → настоящий bare-oblast Tier-2a-остаток НЕ закрыт этим PR (закрыт только city-qualified путь, которого в Avito сегодня нет). Полное закрытие bare-пути невозможно из адреса (нет сигнала «не ЕКБ») — нужен sweep-context: прокинуть target-город свипа (#2500 уже тянет city_slug) в match_or_create_house как coord-less city-override. Это трогает ingest→match цепочку.

GATE: oblast per-city sweep НЕ включать, пока bare-путь не закрыт (sweep-city threading ИЛИ city-keyed aliases). Этот PR — безопасный defense-in-depth слой, не полное закрытие.

## п.2 (#2500 follow-up) — Tier-2a city-guard (defense-in-depth, LATENT) `resolve_city_token()` в normalize.py + guard в matching/houses.py: coord-less карточка с resolved НЕ-ЕКБ городом → skip Tier-2a/2b → New house (не мис-баскет в ЕКБ-дом). ЕКБ happy-path **байт-в-байт** (guard активен только при `lat=lon=None` И resolved non-ЕКБ; ЕКБ bare/city-qualified/coord-bearing — не затронуты). Zero prod-impact (oblast-sweep выключен). 66+155 тестов, live-verified (ЕКБ-alias 6675 fingerprint сохранён; synthetic Н.Тагил → skip). ## ⚠️ ВАЖНО — что этот PR НЕ закрывает Реальные Avito SERP-карточки **bare** (только улица+№, БЕЗ города — 0 oblast-токенов, 2.1% «екатеринбург»). Для bare-карточки `resolve_city_token`→None → guard **дремлет** → настоящий bare-oblast Tier-2a-остаток НЕ закрыт этим PR (закрыт только city-qualified путь, которого в Avito сегодня нет). Полное закрытие bare-пути невозможно из адреса (нет сигнала «не ЕКБ») — нужен **sweep-context**: прокинуть target-город свипа (#2500 уже тянет `city_slug`) в `match_or_create_house` как coord-less city-override. Это трогает ingest→match цепочку. **GATE:** oblast per-city sweep НЕ включать, пока bare-путь не закрыт (sweep-city threading ИЛИ city-keyed aliases). Этот PR — безопасный defense-in-depth слой, не полное закрытие.
bot-backend added 1 commit 2026-07-12 20:28:28 +00:00
fix(tradein/oblast): Tier-2a guard — coord-less non-ЕКБ карточка не матчит ЕКБ-alias
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 52s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
d92ff5c3d3
Follow-up к #2500: закрывает Tier-2a-дыру, которую geo-guard Tier-2b не доставал.
Coord-less карточка, чей адрес РАЗРЕШАЕТ non-ЕКБ город обл.66 (resolve_city_token),
больше не матчит глобально-уникальный alias (ни по fingerprint, ни по
normalized_address) — иначе non-ЕКБ карточка баккетилась бы в одноимённый ЕКБ-дом и
корраптила ЕКБ-данные. При срабатывании guard'а обе alias-выборки пропускаются →
fall-through в New house (Tier-3 coord-gated, тоже пропускается).

normalize.py: + resolve_city_token() (город обл.66 или None), + EKB_CITY_TOKEN;
has_city_token переиспользует resolve_city_token.

EKB happy-path байт-в-байт: guard срабатывает ТОЛЬКО когда адрес называет non-ЕКБ
город И нет координат. ЕКБ-карточки (resolved city = екатеринбург) и доминирующие
bare/city-less coord-less карточки Avito (resolved None) идут Tier-2a/2b как раньше.

ВАЖНО (документировано в коде и отчёте): реальные Avito SERP-адреса — bare (без
city-токена; 2% из 5037 avito-alias'ов несут 'екатеринбург', 0% — oblast). Значит для
bare oblast-карточки resolve_city_token=None и guard дремлет: полное закрытие
bare-Tier-2a-остатка требует sweep-context/city-keyed aliases — отдельный follow-up,
вне scope, актуален лишь при включённом oblast-sweep. Zero prod-impact сегодня.
bot-backend merged commit c98a879a79 into main 2026-07-12 21:04:29 +00:00
bot-backend deleted branch fix/tradein-oblast-tier2a-cityguard 2026-07-12 21:04:29 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#2502
No description provided.