(medium) gate_verdict: blocker subcategory=17 сравнивается через isinstance(int) — строковый subcategory из nspd-dump молча деградирует до warning #1360

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

Описание

В nspd-dump ветке Check 2 blocker срабатывает только при isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES (ключ 17, int). Но overlap['subcategory'] формируется в quarter_dump_lookup._get_zouit_overlaps:365 как props.get('subcategory') or props.get('type_zone') — и в реальных/тестовых данных это часто СТРОКА ('17' или 'engineering', либо fallback на type_zone). Для строкового subcategory isinstance(sub,int)==False → blocker subcategory-17 (охранная зона ЛЭП/газа) НЕ добавляется, охранная инженерная зона тихо деградирует до warning. Риск: пропущенный hard-blocker → ложный вердикт «Можно/С ограничениями» на участке в охранной зоне.

Корень

Отсутствует нормализация типа перед сравнением. Параллельная функция _get_zouit_engineering_overlaps (строки 1168-1174) ту же выборку нормализует через int(subcategory_raw) в try/except — прямое свидетельство, что значение в сыром JSONB может быть НЕ Python int.

Evidence

gate_verdict.py:23-25, 188-203:

BLOCKER_SUBCATEGORIES: dict[int, str] = {17: "Инженерные коммуникации ..."}
sub = overlap.get("subcategory")
if isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES:   # ложь при sub='17' / sub='engineering'
    blockers.append(...)
else:
    warnings.append(...)   # охранная инж.зона тихо понижается до warning

quarter_dump_lookup.py:365: "subcategory": props.get("subcategory") or props.get("type_zone") → часто str

Suggested fix

Нормализовать subcategory к int перед сравнением (как в _get_zouit_engineering_overlaps): sub_int = int(sub) в try/except, либо принимать и int('17'), и числовой код. Альтернативно — для nspd-dump пути классифицировать по type_zone-ключам (как в cad_zouit). Добавить тест с subcategory='17' (строка).

Обоснование severity

medium: реальный структурный дефект (отсутствует нормализация, подтверждённая параллельной функцией), но срабатывает только если НСПД отдаёт subcategory строкой/отсутствующим — не доказано из кода. confidence 0.65.


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

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

  • /Users/anton/Птица/gendesign/backend/app/services/site_finder/gate_verdict.py
  • /Users/anton/Птица/gendesign/backend/app/services/site_finder/quarter_dump_lookup.py
## Описание В nspd-dump ветке Check 2 blocker срабатывает только при `isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES` (ключ 17, int). Но `overlap['subcategory']` формируется в `quarter_dump_lookup._get_zouit_overlaps:365` как `props.get('subcategory') or props.get('type_zone')` — и в реальных/тестовых данных это часто СТРОКА ('17' или 'engineering', либо fallback на type_zone). Для строкового subcategory `isinstance(sub,int)==False` → blocker subcategory-17 (охранная зона ЛЭП/газа) НЕ добавляется, охранная инженерная зона тихо деградирует до warning. Риск: пропущенный hard-blocker → ложный вердикт «Можно/С ограничениями» на участке в охранной зоне. ## Корень Отсутствует нормализация типа перед сравнением. Параллельная функция `_get_zouit_engineering_overlaps` (строки 1168-1174) ту же выборку нормализует через `int(subcategory_raw)` в try/except — прямое свидетельство, что значение в сыром JSONB может быть НЕ Python int. ## Evidence `gate_verdict.py:23-25, 188-203`: ```python BLOCKER_SUBCATEGORIES: dict[int, str] = {17: "Инженерные коммуникации ..."} sub = overlap.get("subcategory") if isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES: # ложь при sub='17' / sub='engineering' blockers.append(...) else: warnings.append(...) # охранная инж.зона тихо понижается до warning ``` `quarter_dump_lookup.py:365`: `"subcategory": props.get("subcategory") or props.get("type_zone")` → часто str ## Suggested fix Нормализовать subcategory к int перед сравнением (как в `_get_zouit_engineering_overlaps`): `sub_int = int(sub)` в try/except, либо принимать и `int('17')`, и числовой код. Альтернативно — для nspd-dump пути классифицировать по type_zone-ключам (как в cad_zouit). Добавить тест с `subcategory='17'` (строка). ## Обоснование severity medium: реальный структурный дефект (отсутствует нормализация, подтверждённая параллельной функцией), но срабатывает только если НСПД отдаёт subcategory строкой/отсутствующим — не доказано из кода. confidence 0.65. --- _week ревью 1 · multi-agent аудит 2026-06-14 · severity=**medium** · category=`data-integrity`_ Затронутые файлы: - `/Users/anton/Птица/gendesign/backend/app/services/site_finder/gate_verdict.py` - `/Users/anton/Птица/gendesign/backend/app/services/site_finder/quarter_dump_lookup.py`
bot-backend added the
week ревью 1
label 2026-06-14 18:09:08 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:01:52 +00:00
Owner

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

Текущее состояние кода: gate_verdict.py:23-25 BLOCKER_SUBCATEGORIES: dict[int, str] = {17: "Инженерные коммуникации ..."}. nspd-dump-ветка Check 2: :189 sub = overlap.get("subcategory"), :190 if isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES: → blocker, else (:197) → warning. quarter_dump_lookup.py:365 "subcategory": props.get("subcategory") or props.get("type_zone") — значение из сырого JSONB, fallback на type_zone-строку. Параллельная _get_zouit_engineering_overlaps нормализует то же поле: quarter_dump_lookup.py:1162-1168 subcategory = int(subcategory_raw) в try/except (ValueError, TypeError) — прямое свидетельство, что raw-значение может быть НЕ Python int (str '17' / 'engineering' / type_zone).

Корень: Отсутствует нормализация типа subcategory перед isinstance(sub, int). При строковом subcategory ('17') isinstance==False → blocker subcategory-17 (охранная зона ЛЭП/газа) не добавляется, охранная инж.зона тихо деградирует до warning (:197-203). Confidence 0.65 — срабатывает только если NSPD-dump отдаёт subcategory строкой/из type_zone fallback, что не доказано из кода, но структурно подтверждено параллельной функцией.

Что править:

  • Нормализовать subcategory к int перед сравнением (как _get_zouit_engineering_overlaps:1162-1168): try: sub_int = int(sub) except (ValueError, TypeError): sub_int = None, затем if sub_int in BLOCKER_SUBCATEGORIES.
  • Альтернатива: для nspd-dump-пути классифицировать по type_zone-ключам (как в cad_zouit-ветке).
  • Добавить тест с subcategory='17' (строка) → ожидать blocker.

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

  • Код: gate_verdict.py (BLOCKER_SUBCATEGORIES:23, nspd-dump-ветка :187-203), quarter_dump_lookup.py (_get_zouit_overlaps:365, нормализация-эталон _get_zouit_engineering_overlaps:1162-1168).
  • Vault: domains/api/endpoints/Endpoint_Parcel_Analyze.md (NSPD dump subcategory path). Прямой заметки нет.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `gate_verdict.py:23-25` `BLOCKER_SUBCATEGORIES: dict[int, str] = {17: "Инженерные коммуникации ..."}`. nspd-dump-ветка Check 2: `:189` `sub = overlap.get("subcategory")`, `:190` `if isinstance(sub, int) and sub in BLOCKER_SUBCATEGORIES:` → blocker, `else` (`:197`) → warning. `quarter_dump_lookup.py:365` `"subcategory": props.get("subcategory") or props.get("type_zone")` — значение из сырого JSONB, fallback на type_zone-строку. Параллельная `_get_zouit_engineering_overlaps` нормализует то же поле: `quarter_dump_lookup.py:1162-1168` `subcategory = int(subcategory_raw)` в `try/except (ValueError, TypeError)` — прямое свидетельство, что raw-значение может быть НЕ Python int (str '17' / 'engineering' / type_zone). **Корень:** Отсутствует нормализация типа subcategory перед `isinstance(sub, int)`. При строковом subcategory ('17') isinstance==False → blocker subcategory-17 (охранная зона ЛЭП/газа) не добавляется, охранная инж.зона тихо деградирует до warning (`:197-203`). Confidence 0.65 — срабатывает только если NSPD-dump отдаёт subcategory строкой/из type_zone fallback, что не доказано из кода, но структурно подтверждено параллельной функцией. **Что править:** - Нормализовать subcategory к int перед сравнением (как `_get_zouit_engineering_overlaps:1162-1168`): `try: sub_int = int(sub) except (ValueError, TypeError): sub_int = None`, затем `if sub_int in BLOCKER_SUBCATEGORIES`. - Альтернатива: для nspd-dump-пути классифицировать по type_zone-ключам (как в cad_zouit-ветке). - Добавить тест с `subcategory='17'` (строка) → ожидать blocker. **Что посмотреть:** - Код: `gate_verdict.py` (`BLOCKER_SUBCATEGORIES:23`, nspd-dump-ветка `:187-203`), `quarter_dump_lookup.py` (`_get_zouit_overlaps:365`, нормализация-эталон `_get_zouit_engineering_overlaps:1162-1168`). - Vault: `domains/api/endpoints/Endpoint_Parcel_Analyze.md` (NSPD dump subcategory path). Прямой заметки нет.
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#1360
No description provided.