[medium] is_residential_zone: prefix-проверка мертва для NSPD zone_code, детект жилой зоны держится на хрупкой подстроке 'жил' без negative-list #1353

Closed
opened 2026-06-14 18:09:00 +00:00 by bot-backend · 1 comment
Collaborator

Описание

Для nspd-quarter-dump пути zone_code приходит как reg_numb_border (формат '66:41-6.123', см. quarter_dump_lookup.py:301), а не как ПЗЗ-индекс 'Ж-1'. Поэтому ветка prefix-проверки (строки 75-77) для NSPD-данных практически никогда не срабатывает, и residential-детект целиком держится на единственной подстроке 'жил' в zone_name=type_zone (строки 78-81). Последствия: (1) если NSPD жилой зоны не содержит корня 'жил' (напр. «Зона многоквартирной застройки»), is_residential_zone вернёт False → blocker PZZ_NOT_RESIDENTIAL → ложный «Нельзя»; (2) зеркально — «нежилая» / «жилищно-коммунальное хозяйство» содержат подстроку 'жил' → ложный residential pass («Можно» для нежилой зоны).

Корень

Детект жилой зоны построен на единственной подстроке без отрицаний. Архитектурно: prefix-проверка мертва для формата NSPD, нет распознавания ПЗЗ-индекса из raw_props, нет negative-list ('нежил', 'жилищно-коммун').

Evidence

gate_verdict.py:15-17, 75-82:

RESIDENTIAL_ZONE_PREFIXES = ("Ж-", "Ж1", "Ж2", "Ж3", "Ж4", "Ж5")   # не совпадает с reg_numb_border '66:41-6.x'
RESIDENTIAL_KEYWORDS = ("жил",)
...
if zone_code:
    if any(zone_code.startswith(p) for p in RESIDENTIAL_ZONE_PREFIXES):  # для NSPD zone_code=reg_numb_border не матчит
        return True
if zone_name:
    zn_lower = zone_name.lower()
    if any(kw in zn_lower for kw in RESIDENTIAL_KEYWORDS):  # единственная реально работающая ветка для NSPD
        return True
return False

quarter_dump_lookup.py:301zone_code = props.get("reg_numb_border") or .... Тесты в test_gate_verdict.py используют синтетический zone_code="Ж-2", а не реальный reg_numb_border — покрытие слепое к этому кейсу.

Suggested fix

Не строить residential-детект на единственной подстроке 'жил': (а) распознавать ПЗЗ-индекс из raw_props (если NSPD/urbanCard отдаёт zone_index 'Ж-..'), (б) добавить явный negative-list ('нежил', 'жилищно-коммун') с приоритетом над positive, (в) прогнать на реальной выборке NSPD type_zone значений ЕКБ.

Обоснование severity (medium)

Ложный вердикт по зонированию — ошибка для пользователя-девелопера. Не финансовая транзакция, проверяется вручную. Confidence 0.82: неизвестно, встречаются ли реально 'нежил*' / жилые без корня 'жил' в дампе ЕКБ — баг может быть спящим, но архитектурно реален.


week ревью 1 · multi-agent аудит 2026-06-14 · severity=medium · category=logic

Затронутые файлы:

  • /Users/anton/Птица/gendesign/backend/app/services/site_finder/gate_verdict.py
## Описание Для nspd-quarter-dump пути `zone_code` приходит как `reg_numb_border` (формат `'66:41-6.123'`, см. quarter_dump_lookup.py:301), а не как ПЗЗ-индекс `'Ж-1'`. Поэтому ветка prefix-проверки (строки 75-77) для NSPD-данных практически никогда не срабатывает, и residential-детект целиком держится на единственной подстроке `'жил'` в `zone_name=type_zone` (строки 78-81). Последствия: (1) если NSPD жилой зоны не содержит корня 'жил' (напр. «Зона многоквартирной застройки»), `is_residential_zone` вернёт False → blocker `PZZ_NOT_RESIDENTIAL` → ложный «Нельзя»; (2) зеркально — «нежилая» / «жилищно-коммунальное хозяйство» содержат подстроку 'жил' → ложный residential pass («Можно» для нежилой зоны). ## Корень Детект жилой зоны построен на единственной подстроке без отрицаний. Архитектурно: prefix-проверка мертва для формата NSPD, нет распознавания ПЗЗ-индекса из raw_props, нет negative-list ('нежил', 'жилищно-коммун'). ## Evidence `gate_verdict.py:15-17, 75-82`: ```python RESIDENTIAL_ZONE_PREFIXES = ("Ж-", "Ж1", "Ж2", "Ж3", "Ж4", "Ж5") # не совпадает с reg_numb_border '66:41-6.x' RESIDENTIAL_KEYWORDS = ("жил",) ... if zone_code: if any(zone_code.startswith(p) for p in RESIDENTIAL_ZONE_PREFIXES): # для NSPD zone_code=reg_numb_border не матчит return True if zone_name: zn_lower = zone_name.lower() if any(kw in zn_lower for kw in RESIDENTIAL_KEYWORDS): # единственная реально работающая ветка для NSPD return True return False ``` `quarter_dump_lookup.py:301` — `zone_code = props.get("reg_numb_border") or ...`. Тесты в `test_gate_verdict.py` используют синтетический `zone_code="Ж-2"`, а не реальный `reg_numb_border` — покрытие слепое к этому кейсу. ## Suggested fix Не строить residential-детект на единственной подстроке 'жил': (а) распознавать ПЗЗ-индекс из raw_props (если NSPD/urbanCard отдаёт `zone_index 'Ж-..'`), (б) добавить явный negative-list ('нежил', 'жилищно-коммун') с приоритетом над positive, (в) прогнать на реальной выборке NSPD `type_zone` значений ЕКБ. ## Обоснование severity (medium) Ложный вердикт по зонированию — ошибка для пользователя-девелопера. Не финансовая транзакция, проверяется вручную. Confidence 0.82: неизвестно, встречаются ли реально 'нежил*' / жилые без корня 'жил' в дампе ЕКБ — баг может быть спящим, но архитектурно реален. --- _week ревью 1 · multi-agent аудит 2026-06-14 · severity=**medium** · category=`logic`_ Затронутые файлы: - `/Users/anton/Птица/gendesign/backend/app/services/site_finder/gate_verdict.py`
bot-backend added the
week ревью 1
label 2026-06-14 18:09:00 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:01:51 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода: gate_verdict.py:15 RESIDENTIAL_ZONE_PREFIXES = ("Ж-", "Ж1", "Ж2", "Ж3", "Ж4", "Ж5"); :17 RESIDENTIAL_KEYWORDS = ("жил",). is_residential_zone (:69-82): :75-77 prefix-проверка zone_code.startswith(p), затем :78-81 единственная positive-подстрока 'жил' в zone_name, без negative-list. quarter_dump_lookup.py:301 zone_code = props.get("reg_numb_border") or props.get("zone_code") or props.get("name") — для NSPD-quarter-dump приходит формат '66:41-6.123', который не матчит ни один RESIDENTIAL_ZONE_PREFIXES → prefix-ветка мертва, детект целиком на 'жил'.

Корень: Residential-детект для NSPD-пути держится на одной подстроке 'жил' без negative-list и без распознавания ПЗЗ-индекса. Последствия: (1) жилая зона без корня 'жил' («Зона многоквартирной застройки») → False → blocker PZZ_NOT_RESIDENTIAL (gate_verdict.py:136-141) → ложное «Нельзя»; (2) «нежилая»/«жилищно-коммунальное хозяйство» содержат 'жил' → ложный residential pass. Confidence 0.82 — может быть спящим (зависит от реальных type_zone дампа ЕКБ), но архитектурно реален. Тесты test_gate_verdict.py используют синтетический zone_code="Ж-2", не реальный reg_numb_border.

Что править:

  • Добавить negative-list ('нежил', 'жилищно-коммун') с приоритетом над positive 'жил'.
  • Распознавать ПЗЗ-индекс из raw_props, если NSPD/urbanCard отдаёт zone_index 'Ж-..' отдельным полем (а не reg_numb_border).
  • Прогнать на реальной выборке NSPD type_zone значений ЕКБ до фикса (валидировать список positive/negative-маркеров).

Что посмотреть:

  • Код: gate_verdict.py (is_residential_zone:69, константы :15-17, Check 1 :129-148), quarter_dump_lookup.py:301 (формирование zone_code), test_gate_verdict.py (добавить кейсы reg_numb_border + 'нежил*' + жилая без 'жил').
  • Vault: domains/api/endpoints/Endpoint_Parcel_Analyze.md (gate_verdict zoning/cad_zouit логика). Прямой заметки про is_residential_zone не найдено.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `gate_verdict.py:15` `RESIDENTIAL_ZONE_PREFIXES = ("Ж-", "Ж1", "Ж2", "Ж3", "Ж4", "Ж5")`; `:17` `RESIDENTIAL_KEYWORDS = ("жил",)`. `is_residential_zone` (`:69-82`): `:75-77` prefix-проверка `zone_code.startswith(p)`, затем `:78-81` единственная positive-подстрока `'жил'` в `zone_name`, без negative-list. `quarter_dump_lookup.py:301` `zone_code = props.get("reg_numb_border") or props.get("zone_code") or props.get("name")` — для NSPD-quarter-dump приходит формат `'66:41-6.123'`, который не матчит ни один `RESIDENTIAL_ZONE_PREFIXES` → prefix-ветка мертва, детект целиком на `'жил'`. **Корень:** Residential-детект для NSPD-пути держится на одной подстроке 'жил' без negative-list и без распознавания ПЗЗ-индекса. Последствия: (1) жилая зона без корня 'жил' («Зона многоквартирной застройки») → False → blocker `PZZ_NOT_RESIDENTIAL` (`gate_verdict.py:136-141`) → ложное «Нельзя»; (2) «нежилая»/«жилищно-коммунальное хозяйство» содержат 'жил' → ложный residential pass. Confidence 0.82 — может быть спящим (зависит от реальных `type_zone` дампа ЕКБ), но архитектурно реален. Тесты `test_gate_verdict.py` используют синтетический `zone_code="Ж-2"`, не реальный `reg_numb_border`. **Что править:** - Добавить negative-list ('нежил', 'жилищно-коммун') с приоритетом над positive 'жил'. - Распознавать ПЗЗ-индекс из raw_props, если NSPD/urbanCard отдаёт `zone_index 'Ж-..'` отдельным полем (а не reg_numb_border). - Прогнать на реальной выборке NSPD `type_zone` значений ЕКБ до фикса (валидировать список positive/negative-маркеров). **Что посмотреть:** - Код: `gate_verdict.py` (`is_residential_zone:69`, константы `:15-17`, Check 1 `:129-148`), `quarter_dump_lookup.py:301` (формирование zone_code), `test_gate_verdict.py` (добавить кейсы reg_numb_border + 'нежил*' + жилая без 'жил'). - Vault: `domains/api/endpoints/Endpoint_Parcel_Analyze.md` (gate_verdict zoning/cad_zouit логика). Прямой заметки про is_residential_zone не найдено.
bot-backend added the
needs-human
label 2026-06-15 16:43:59 +00:00
Sign in to join this conversation.
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#1353
No description provided.