[Fable 5][P3] recommend_mix: _district_market_saturation без latest-snapshot фильтра — каждый ЖК считается ~3 раза, медиана и гейт n>=5 искажены #1221

Closed
opened 2026-06-13 04:49:46 +00:00 by bot-backend · 0 comments
Collaborator

Из армейского ревью Claude Fable 5 (2026-06-11)

Confirmed by 2/2 адверсариальных верификатора, severity P3 (correctness), confidence 0.65.

Файл

backend/app/services/analytics_queries.py:1421

Evidence (цитата кода)

SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.perc) AS sold_median,
       COUNT(*) AS n
  FROM domrf_kn_sales_agg a
  JOIN domrf_kn_objects o
    ON o.obj_id = a.obj_id
   AND o.snapshot_date = a.snapshot_date

Why bug

domrf_kn_objects хранит ~3 snapshot'а на obj_id (comparables-CTE этого же файла дедуплицирует: «иначе comparables дублируются»). Здесь дедупликации нет: один ЖК входит в медиану несколько раз, старые snapshot'ы с меньшим perc тянут sold_median вниз (sold% растёт во времени) → sat_factor занижен → velocity/срок распродажи искажены; гейт «<5 ЖК» проходит уже при 2 ЖК, saturation_n в scope завышен ~3x.

Suggested fix

Брать последний snapshot: DISTINCT ON (obj_id) … ORDER BY obj_id, snapshot_date DESC (как latest_obj в comparables-запросе) и COUNT(DISTINCT a.obj_id).

Verify notes

  • [real/P3] Подтверждено трассировкой: loader копит снапшоты (ON CONFLICT (obj_id,[type,]snapshot_date) в обеих таблицах, одинаковая дата за прогон), а запрос джойнит без latest-фильтра — единственное место в файле без дедупликации (ср. DISTINCT ON/MAX везде ещё). Сценарий: 2 ЖК × 3 снапшота = n=6 → гейт «<5 ЖК» пройден; медиана тянется старыми perc; sat_factor → bucket_velocity → months_to_sellout; saturation_n в scope ~3x; сданные ЖК просачиваются через старые снапшоты со статусом 'Строящиеся'.
  • [real/P3] Подтверждено: 50_schema_kn_extensions.sql сносит PK, вводит UNIQUE(obj_id, snapshot_date) ради нескольких срезов; ретенции нет; сам файл (стр. 2744) фиксирует «~3 snapshot'а на obj_id» и дедуплицирует comparables (DISTINCT ON/MAX), как и стр. 733, 1200. Тут latest-фильтра нет: join лишь спаривает срезы, ЖК входит ~3 раза — медиана тянется старыми perc, гейт n>=5 проходит при 2 ЖК, sat_n завышен. Эффект на sat_factor ограничен (±0.3) — P3.

Provenance

  • Finders: forecasting
  • Confidence: 0.65
  • Originally batched at: main@d796027
## Из армейского ревью Claude Fable 5 (2026-06-11) Confirmed by 2/2 адверсариальных верификатора, severity **P3** (correctness), confidence 0.65. ## Файл [`backend/app/services/analytics_queries.py:1421`](backend/app/services/analytics_queries.py:1421) ## Evidence (цитата кода) ``` SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.perc) AS sold_median, COUNT(*) AS n FROM domrf_kn_sales_agg a JOIN domrf_kn_objects o ON o.obj_id = a.obj_id AND o.snapshot_date = a.snapshot_date ``` ## Why bug domrf_kn_objects хранит ~3 snapshot'а на obj_id (comparables-CTE этого же файла дедуплицирует: «иначе comparables дублируются»). Здесь дедупликации нет: один ЖК входит в медиану несколько раз, старые snapshot'ы с меньшим perc тянут sold_median вниз (sold% растёт во времени) → sat_factor занижен → velocity/срок распродажи искажены; гейт «<5 ЖК» проходит уже при 2 ЖК, saturation_n в scope завышен ~3x. ## Suggested fix Брать последний snapshot: DISTINCT ON (obj_id) … ORDER BY obj_id, snapshot_date DESC (как latest_obj в comparables-запросе) и COUNT(DISTINCT a.obj_id). ## Verify notes - [real/P3] Подтверждено трассировкой: loader копит снапшоты (ON CONFLICT (obj_id,[type,]snapshot_date) в обеих таблицах, одинаковая дата за прогон), а запрос джойнит без latest-фильтра — единственное место в файле без дедупликации (ср. DISTINCT ON/MAX везде ещё). Сценарий: 2 ЖК × 3 снапшота = n=6 → гейт «<5 ЖК» пройден; медиана тянется старыми perc; sat_factor → bucket_velocity → months_to_sellout; saturation_n в scope ~3x; сданные ЖК просачиваются через старые снапшоты со статусом 'Строящиеся'. - [real/P3] Подтверждено: 50_schema_kn_extensions.sql сносит PK, вводит UNIQUE(obj_id, snapshot_date) ради нескольких срезов; ретенции нет; сам файл (стр. 2744) фиксирует «~3 snapshot'а на obj_id» и дедуплицирует comparables (DISTINCT ON/MAX), как и стр. 733, 1200. Тут latest-фильтра нет: join лишь спаривает срезы, ЖК входит ~3 раза — медиана тянется старыми perc, гейт n>=5 проходит при 2 ЖК, sat_n завышен. Эффект на sat_factor ограничен (±0.3) — P3. ## Provenance - Finders: `forecasting` - Confidence: 0.65 - Originally batched at: main@d796027
bot-backend added the
Fable 5 ревью
label 2026-06-13 04:49:46 +00:00
Sign in to join this conversation.
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#1221
No description provided.