fix(cadastre): skip grid-walk for broken quarter geometries (#168) #179

Merged
lekss361 merged 2 commits from fix/cadastre-skip-broken-geoms into main 2026-05-15 13:25:06 +00:00
lekss361 commented 2026-05-15 13:15:11 +00:00 (Migrated from github.com)

Summary

Pilot v5 revealed что 1735/2408 (72%) cad_quarters_geom rows для ЕКБ имеют broken micro-precision geometry (width 0.01м вместо 200-4000м).

Effects

  • Grid-walk WMS получает bbox ~1mm² → NSPD 500 ServiceException на all 450 req per quarter
  • 1 quarter из pilot hung 2+ min (silent failure)

Fix

quarter_bbox_3857 sanity check: если width/height не в [100, 10000] метрах → return None → bulk_harvest_quarter скипает grid-walk, только snapshot phase запускается.

Pilot v5 dynamics after fix:

  • Все 50 quarters получают snapshot (20 per cat each)
  • Grid-walk runs только для ~14 из 50 healthy quarters → no 500s, no hangs

Follow-up

Re-import cad_quarters_geom geometry from NSPD (category 36381) — отдельный issue.

## Summary Pilot v5 revealed что 1735/2408 (72%) cad_quarters_geom rows для ЕКБ имеют broken micro-precision geometry (width 0.01м вместо 200-4000м). ## Effects - Grid-walk WMS получает bbox ~1mm² → NSPD 500 ServiceException на all 450 req per quarter - 1 quarter из pilot hung 2+ min (silent failure) ## Fix `quarter_bbox_3857` sanity check: если width/height не в [100, 10000] метрах → return None → bulk_harvest_quarter скипает grid-walk, только snapshot phase запускается. Pilot v5 dynamics after fix: - Все 50 quarters получают snapshot (20 per cat each) - Grid-walk runs только для ~14 из 50 healthy quarters → no 500s, no hangs ## Follow-up Re-import cad_quarters_geom geometry from NSPD (category 36381) — отдельный issue.
lekss361 commented 2026-05-15 13:18:25 +00:00 (Migrated from github.com)

Review

CI зелёный (backend 1m2s + frontend 1m39s), логика чистая, но тест на новую boundary не добавлен — тот же gap что был на #177.

🔴 Блокер: нет unit-теста на [100, 10000] boundary

Pure function, 4 очевидных boundary case (< 100, == 100, == 10000, > 10000), плюс happy path. Mock db.execute().mappings().first() тривиален — секция "Grid geometry" уже есть в backend/tests/services/test_cadastre_bulk.py рядом с тестами generate_grid_click_points. Добавить test_quarter_bbox_3857_skips_broken_geom и test_quarter_bbox_3857_skips_oversized — ~20 строк.

Per post-#177 правило (no "follow-up" для test coverage): тест должен поехать в этом же PR. Иначе следующий PR опять придёт без теста и регрессия (например, кто-то поменяет threshold 10000 → 1000 и сломает реальные quarters) пройдёт.

Что хорошо

  • Caller behavior: bulk_harvest.py:215-222 (_grid_walk_category) корректно обрабатывает None от quarter_bbox_3857 — логирует warning и возвращает (0, 0). Минор в PR body — author пишет "bulk_harvest_quarter скипает", реальный handler — _grid_walk_category, но поведение правильное.
  • Type signature без drift: quarter_bbox_3857 уже был tuple[...] | None до этого PR — TS/pydantic ничего не задевает.
  • Threshold [100m, 10000m]: для ЕКБ MVP scope разумно. Для городских кварталов 200-4000м — попадает в окно. 10km ceiling мог бы clipnut rural oblast quarters, но alternative — 2+ min hangs + 500s от NSPD, так что conservative skip правильнее. Author корректно отметил follow-up issue (re-import cad_quarters_geom geometry с NSPD category 36381) как real fix data quality.
  • Negative width / xmin > xmax: проваливается в skip (negative < 100) — defensive.
  • Logging: .2f на range 0.01..10000 — нет log poisoning.

Что нужно

  1. Обязательно: добавить unit-тесты на quarter_bbox_3857 boundary:
    • width < 100 → returns None
    • width > 10000 → returns None
    • 100 ≤ width ≤ 10000 happy path → returns tuple
    • (опционально) height < 100 → returns None (симметрия)

Жду новый SHA с тестом.

## Review CI зелёный (backend 1m2s + frontend 1m39s), логика чистая, но **тест на новую boundary не добавлен** — тот же gap что был на #177. ### 🔴 Блокер: нет unit-теста на [100, 10000] boundary Pure function, 4 очевидных boundary case (< 100, == 100, == 10000, > 10000), плюс happy path. Mock `db.execute().mappings().first()` тривиален — секция "Grid geometry" уже есть в `backend/tests/services/test_cadastre_bulk.py` рядом с тестами `generate_grid_click_points`. Добавить `test_quarter_bbox_3857_skips_broken_geom` и `test_quarter_bbox_3857_skips_oversized` — ~20 строк. Per post-#177 правило (no "follow-up" для test coverage): тест должен поехать в этом же PR. Иначе следующий PR опять придёт без теста и регрессия (например, кто-то поменяет threshold 10000 → 1000 и сломает реальные quarters) пройдёт. ### ✅ Что хорошо - **Caller behavior**: `bulk_harvest.py:215-222` (`_grid_walk_category`) корректно обрабатывает `None` от `quarter_bbox_3857` — логирует warning и возвращает `(0, 0)`. Минор в PR body — author пишет "bulk_harvest_quarter скипает", реальный handler — `_grid_walk_category`, но поведение правильное. - **Type signature без drift**: `quarter_bbox_3857` уже был `tuple[...] | None` до этого PR — TS/pydantic ничего не задевает. - **Threshold [100m, 10000m]**: для ЕКБ MVP scope разумно. Для городских кварталов 200-4000м — попадает в окно. 10km ceiling мог бы clipnut rural oblast quarters, но alternative — 2+ min hangs + 500s от NSPD, так что conservative skip правильнее. Author корректно отметил follow-up issue (re-import cad_quarters_geom geometry с NSPD category 36381) как real fix data quality. - **Negative width / xmin > xmax**: проваливается в skip (negative < 100) — defensive. - **Logging**: `.2f` на range 0.01..10000 — нет log poisoning. ### Что нужно 1. **Обязательно**: добавить unit-тесты на `quarter_bbox_3857` boundary: - `width < 100` → returns None - `width > 10000` → returns None - `100 ≤ width ≤ 10000` happy path → returns tuple - (опционально) `height < 100` → returns None (симметрия) Жду новый SHA с тестом.
lekss361 commented 2026-05-15 13:24:57 +00:00 (Migrated from github.com)

Re-review (SHA e18e146)

Все блокеры закрыты:

5 boundary tests добавлены

backend/tests/services/test_cadastre_bulk.py:201-244 — новая секция quarter_bbox_3857 sanity check:

  • test_quarter_bbox_3857_healthy_geom — happy path ~750м (real ЕКБ-квартал)
  • test_quarter_bbox_3857_skips_broken_micro_geom — width=60.7m (real broken example 66:41:0101005)
  • test_quarter_bbox_3857_skips_oversized_geom — width=15000m
  • test_quarter_bbox_3857_skips_zero_height — height=50m (симметрия по Y)
  • test_quarter_bbox_3857_returns_none_when_quarter_missing — null row

Хорошо: helper _mock_db_with_bbox для DRY, реальные значения из broken examples в качестве test data.

CI зелёный

backend 1m12s + frontend 1m29s — все 5 новых тестов прошли.

Логика без изменений с прошлого SHA

grid_geometry.py фикс identical (sanity check на [100, 10000]m, return None для broken). Caller (bulk_harvest._grid_walk_category) корректно обрабатывает None — логирует warning и возвращает (0, 0).

approve merge

## Re-review (SHA e18e146) Все блокеры закрыты: ### ✅ 5 boundary tests добавлены `backend/tests/services/test_cadastre_bulk.py:201-244` — новая секция `quarter_bbox_3857 sanity check`: - `test_quarter_bbox_3857_healthy_geom` — happy path ~750м (real ЕКБ-квартал) - `test_quarter_bbox_3857_skips_broken_micro_geom` — width=60.7m (real broken example 66:41:0101005) - `test_quarter_bbox_3857_skips_oversized_geom` — width=15000m - `test_quarter_bbox_3857_skips_zero_height` — height=50m (симметрия по Y) - `test_quarter_bbox_3857_returns_none_when_quarter_missing` — null row Хорошо: helper `_mock_db_with_bbox` для DRY, реальные значения из broken examples в качестве test data. ### ✅ CI зелёный backend 1m12s + frontend 1m29s — все 5 новых тестов прошли. ### ✅ Логика без изменений с прошлого SHA `grid_geometry.py` фикс identical (sanity check на [100, 10000]m, return None для broken). Caller (`bulk_harvest._grid_walk_category`) корректно обрабатывает None — логирует warning и возвращает `(0, 0)`. approve merge
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#179
No description provided.