fix(ptica): невозможные параметры регламента не превращаются в деньги (#2464) #3000

Merged
bot-backend merged 1 commit from fix/2464-building-pct-guard into main 2026-08-20 18:39:10 +00:00
Collaborator

Дефект

synthesize_teap_from_buildability проверяла параметры градрегламента только на > 0:

if max_building_pct is not None and max_building_pct > 0:
    built_area = area_m2 * (max_building_pct / 100.0)

Процент застройки 150 на участке 10 000 м² даёт пятно 15 000 м² — больше самого участка. Дальше из него считаются жилая площадь, число квартир, парковки и выручка: физически невозможные числа, поданные как обычные цифры финмодели. КСИТ 500 даёт GFA 5 000 000 м² на гектаре.

Параметры приходят из ПЗЗ-регламента (zone_regulation_cache) — это внешние разобранные данные, то есть граница доверия.

Правка

Невозможное значение отбрасывается, а не роняет расчёт: если рядом есть КСИТ, GFA считается по нему и остаётся верной. Если вменяемых параметров не осталось — None, и caller штатно показывает отсутствие финоценки с caveat, а не ноль.

Границы взяты с запасом к реальным данным прода 20.08.2026 (33 строки zone_regulation_cache):

параметр на проде граница
max_building_pct 0 .. 100 ≤ 100
max_far 1 .. 4 ≤ 30
max_floors 0 .. 5 ≤ 100

Это сито против порчи разбора, а не норматив: запас более чем семикратный.

Второй дефект — нашли эти же тесты

Ветка «нет ни процента, ни этажности → пятно = GFA» неявно предполагает один этаж. При КСИТ > 1 она давала пятно больше участка: 10 000 м² с far=2 → пятно 20 000 м². Это существовало и до моей правки, без всяких невозможных входных значений.

Добавлен физический инвариант «пятно ≤ участок» — не эвристика, а геометрия. Стоит один раз после всех ветвей, чтобы держаться и для будущих способов оценки пятна. GFA при этом не меняется.

Как проверено

  • Двусторонне: против origin/main четыре теста красные с конкретными невозможными значениями:
    • пятно 15000.0 больше участка 10000.0;
    • использован невозможный КСИТ: GFA=5000000.0;
    • пятно 20000.0 на участке 10000.0 (второй дефект — красный на main сам по себе).
  • Контроли от переусердствования, зелёные с обеих сторон: пять сочетаний (pct, far, floors), взятых дословно с прода(80, 2.4, 2), (100, 4, 5), (80, 2.5, None), (None, 4, None), (40, None, 10); плюс граница pct = 100 %, которая законна и на проде встречается.
  • pytest backend/tests/services/site_finder/ — 656 passed; смежные test_parcel_financial + services/generative — 185 passed.

Текущих ошибок на проде нет

Проверено: в zone_regulation_cache 33 строки, значений вне диапазона 0. Правка предупредительная для первой части и исправляющая для второй: инвариант «пятно ≤ участок» нарушался бы уже на сегодняшних данных при сочетании «КСИТ есть, процент и этажность отсутствуют».

Часть эпика #2464.

## Дефект `synthesize_teap_from_buildability` проверяла параметры градрегламента только на `> 0`: ```python if max_building_pct is not None and max_building_pct > 0: built_area = area_m2 * (max_building_pct / 100.0) ``` Процент застройки 150 на участке 10 000 м² даёт пятно **15 000 м²** — больше самого участка. Дальше из него считаются жилая площадь, число квартир, парковки и выручка: физически невозможные числа, поданные как обычные цифры финмодели. КСИТ 500 даёт GFA **5 000 000 м²** на гектаре. Параметры приходят из ПЗЗ-регламента (`zone_regulation_cache`) — это внешние разобранные данные, то есть граница доверия. ## Правка Невозможное значение **отбрасывается**, а не роняет расчёт: если рядом есть КСИТ, GFA считается по нему и остаётся верной. Если вменяемых параметров не осталось — `None`, и caller штатно показывает отсутствие финоценки с caveat, а не ноль. Границы взяты **с запасом к реальным данным** прода 20.08.2026 (33 строки `zone_regulation_cache`): | параметр | на проде | граница | |---|---|---| | `max_building_pct` | 0 .. 100 | ≤ 100 | | `max_far` | 1 .. 4 | ≤ 30 | | `max_floors` | 0 .. 5 | ≤ 100 | Это сито против порчи разбора, а не норматив: запас более чем семикратный. ## Второй дефект — нашли эти же тесты Ветка «нет ни процента, ни этажности → пятно = GFA» неявно предполагает один этаж. При КСИТ > 1 она давала пятно больше участка: **10 000 м² с far=2 → пятно 20 000 м²**. Это существовало и **до** моей правки, без всяких невозможных входных значений. Добавлен физический инвариант «пятно ≤ участок» — не эвристика, а геометрия. Стоит **один раз после всех ветвей**, чтобы держаться и для будущих способов оценки пятна. GFA при этом не меняется. ## Как проверено - **Двусторонне:** против `origin/main` четыре теста красные с конкретными невозможными значениями: - `пятно 15000.0 больше участка 10000.0`; - `использован невозможный КСИТ: GFA=5000000.0`; - `пятно 20000.0 на участке 10000.0` (второй дефект — красный на main сам по себе). - **Контроли от переусердствования, зелёные с обеих сторон:** пять сочетаний `(pct, far, floors)`, взятых **дословно с прода** — `(80, 2.4, 2)`, `(100, 4, 5)`, `(80, 2.5, None)`, `(None, 4, None)`, `(40, None, 10)`; плюс граница `pct = 100 %`, которая законна и на проде встречается. - `pytest backend/tests/services/site_finder/` — 656 passed; смежные `test_parcel_financial` + `services/generative` — 185 passed. ## Текущих ошибок на проде нет Проверено: в `zone_regulation_cache` 33 строки, значений вне диапазона **0**. Правка предупредительная для первой части и **исправляющая** для второй: инвариант «пятно ≤ участок» нарушался бы уже на сегодняшних данных при сочетании «КСИТ есть, процент и этажность отсутствуют». Часть эпика #2464.
bot-backend added 1 commit 2026-08-20 18:15:20 +00:00
fix(ptica): невозможные параметры регламента не превращаются в деньги (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 17m8s
06b0063c67
`synthesize_teap_from_buildability` проверяла параметры только на `> 0`.
Процент застройки 150 давал пятно БОЛЬШЕ участка (10 000 м² → 15 000 м²),
а дальше — жилую площадь, число квартир и выручку: физически невозможные
числа, поданные как обычные цифры финмодели. КСИТ 500 давал GFA
5 000 000 м² на гектаре.

Параметры приходят из ПЗЗ-регламента (`zone_regulation_cache`) — внешние
разобранные данные, то есть граница доверия.

Невозможное значение ОТБРАСЫВАЕТСЯ, а не роняет расчёт: если рядом есть
КСИТ, GFA считается по нему и остаётся верной. Лучше отсутствие
параметра, чем неверный. Если вменяемых не осталось — None, и caller
штатно показывает отсутствие финоценки с caveat, а не ноль.

Границы взяты с запасом к реальным данным прода 20.08.2026 (33 строки
zone_regulation_cache: pct 0..100, far 1..4, floors 0..5): pct ≤ 100,
far ≤ 30, этажей ≤ 100 — сито против порчи разбора, а не норматив.

ВТОРОЙ дефект, найденный этими же тестами и существовавший до правки:
ветка «нет ни процента, ни этажности → пятно = GFA» неявно предполагает
один этаж, и при КСИТ > 1 давала пятно больше участка (10 000 м² с far=2
→ 20 000 м²). Добавлен физический инвариант «пятно ≤ участок» — не
эвристика, а геометрия, и стоит он ОДИН раз после всех ветвей, чтобы
держаться и для будущих способов оценки пятна. GFA при этом не меняется.

Двусторонне: против origin/main четыре теста красные с конкретными
невозможными значениями («пятно 15000.0 больше участка 10000.0»,
«GFA=5000000.0»). Восемь контролей зелёные с обеих сторон — среди них
пять сочетаний (pct, far, floors), взятых ДОСЛОВНО с прода, и граница
100 % застройки, которая законна и на проде есть.

pytest test_parcel_financial + services/generative + новый файл — 185 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit 9810a946a6 into main 2026-08-20 18:39:10 +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#3000
No description provided.