Bot review #558 нашёл 🔴: trade_in_estimates.floor/total_floors имеют
NOT NULL constraint в DDL (001_trade_in_estimates.sql) — INSERT в
estimator.py:893 и _empty_estimate шлют payload.floor=None →
psycopg.errors.NotNullViolation на user-submit без этажа.
- new migration 065: DROP NOT NULL для обеих колонок (idempotent)
- PDF exporter: fallback "—" вместо "0" при missing floor/total_floors
- estimator.py:744 style: `is not None` для consistency с pre-call gates
- integration test: estimate без floor не крашится
Bot reviewer ⚠️ findings addressed:
🔴 NOT NULL DB violation — FIXED via migration 065
🟠 PDF "0 / 0" regression — FIXED via or "—"
🟡 style inconsistency — FIXED
🟢 test coverage gap — FIXED via test_estimator_floor_optional.py
UX: floor и total_floors теперь optional — все 6 мест в estimator.py уже
имеют None-guards (Tier H/IMV/Cian валидируют наличие до вызова).
При None — Tier H/IMV/Cian skip, оценка через Tier W (wide) с понижением
confidence до medium/low. Critical guard: tradein.deals также не имеют
жёстких NOT NULL на этих полях — schema-only change достаточен.
Test presets: заменены на top-6 ЕКБ улиц по deal_count за 12 мес
(rosreestr_deals ДКП квартиры):
- Академика Ландау (686 сделок/год, Академический новостройки)
- Малышева 84 (центр классика — оставлен)
- Краснолесья (487 сделок/год, Академический эконом)
- Космонавтов 50 (415 сделок/год, Уралмаш бюджет)
- 8 Марта 80 (305 сделок/год, центр Ленинский)
- Московская (426 сделок/год, центр премиум)
Разброс: 4 района, размеры 35-75м², 1-3 комнаты, цены 120-185 К ₽/м².
Все адреса дают medium+ confidence из-за объёма ДКП-сделок на улице.