_L3_FUTURE_SQL применял volatile-фильтры (ready_dt > horizon, free_flats
<= threshold) в WHERE ВНУТРИ CTE с DISTINCT ON (obj_id) ORDER BY
snapshot_date DESC. Это значит фильтр применялся ДО DISTINCT ON →
бралась «последний снапшот, ПРОШЕДШИЙ фильтр», а не последний в принципе.
Эффект на проде: объект, когда-то бывший «объявлен, не продаётся»
(free_flats=0/NULL), остаётся в L3 future-supply даже после открытия
продаж — свежий снапшот с free_flats=180 отфильтрован, взят старый
с free_flats=0. Двойной счёт с L1/L2 (open + future одного объекта),
stale flat_count/ready_dt в supply_layers и форсайте.
Patch: разделил фильтры на стабильные (в CTE: region_cd, district_name)
и volatile (во внешнем WHERE после DISTINCT ON: ready_dt, free_flats).
Зеркало паттерна L2-CTE (там volatile уже снаружи). Семантика теперь
матчит docstring: «свежий снапшот, затем фильтр».
62/62 supply_layers тестов зелёные. ruff clean. SQL psycopg v3
(CAST(:x AS interval)) уже корректен.
Closes#1212