(high) site-finder-api.ts/useParcelPoiScoreQuery: poi_weighted_score из weight в неверной шкале — клиент видит абсурдный «~10/100» #1486

Closed
opened 2026-06-15 05:57:50 +00:00 by bot-backend · 1 comment
Collaborator

Severity: high · Категория: report · Файл: frontend/src/lib/site-finder-api.ts:556-568

site-finder-api.ts:556-568. В non-mock ветке score_contribution = Math.round(p.weight * 100), poi_weighted_score = сумма. Комментарий (стр.561) предполагает weight ∈ [0..1], но backend default (decay=straight) считает compute_poi_weighted_top7: weight = (1/(distance_m+100)) * category_weight, где category_weight ≤ 6.0 (poi_score.py:31-43,135). Подтверждено: parcels.py:3077,3128 — default decay=straight → именно эта формула. Реальные значения weight крошечные: метро в 0 м = 6.0/100 = 0.06, метро в 340 м = 6.0/440 ≈ 0.0136, школа в 480 м ≈ 0.0086. Тогда score_contribution = round(0.0136*100) = 1 (часто 0), сумма по top-7 ≈ 1–10. Рендерится в Section1ParcelInfo.tsx:297-299 → PoiList2Gis.tsx:133 как {totalScore.toFixed(0)} / 100 — клиент видит ~5/100 для участка с отличной инфраструктурой. Доп. дефект: пороги weightBadgeVariant (PoiList2Gis.tsx:54-61: ≥0.2/≥0.12/≥0.08) недостижимы при max weight ≈0.06 → каждый POI получает badge 'warning' (амбер). Мок poi-score.json содержит подогнанные weight (0.25/0.15/0.1) и score_contribution (22/9/11/…=76), не воспроизводящие реальную формулу, поэтому в MOCK_POI_SCORE баг скрыт. Routing-режим тоже не спасает: там weight = category_weight * decay(0..1) ≈ 0..6, т.е. тоже не доля. Как чинить: либо backend должен отдавать нормированный 0..100 score, либо frontend нормировать weight (например по max в выборке) перед умножением на 100; пороги badge привести к реальной шкале weight.

Почему баг: Фронт предполагает weight ∈ [0,1] (комментарий строки 561: 'weight (0..1) × 100'), но backend-формула weight = cat_weight/(dist+100) даёт значения ~0.0001–0.06. Умножение на 100 и суммирование даёт число в шкале, не имеющей отношения к 0..100, после чего оно выводится клиенту как «/100». Подтверждено чтением poi_score.py:31-67,142 (CATEGORY_WEIGHTS до 6.0) и PoiList2Gis.tsx:133, :213, :54-61.

Как чинить: Нормализовать POI-скор в честную шкалу 0..100 (например, относительно max достижимого weighted score или нормировкой суммы category_weight) на backend и отдавать готовый poi_weighted_score + percentile, а не пытаться восстановить его на фронте из сырых weight. Если деривация остаётся на фронте — не умножать weight*100, а маппить реальный диапазон в 0..100; пороги weightBadgeVariant пересчитать под фактическую шкалу weight.


deep-audit «армия ботов 2» · verify+harden, confidence 0.95 · unit U107

**Severity:** high · **Категория:** report · **Файл:** `frontend/src/lib/site-finder-api.ts:556-568` site-finder-api.ts:556-568. В non-mock ветке score_contribution = Math.round(p.weight * 100), poi_weighted_score = сумма. Комментарий (стр.561) предполагает weight ∈ [0..1], но backend default (decay=straight) считает compute_poi_weighted_top7: weight = (1/(distance_m+100)) * category_weight, где category_weight ≤ 6.0 (poi_score.py:31-43,135). Подтверждено: parcels.py:3077,3128 — default decay=straight → именно эта формула. Реальные значения weight крошечные: метро в 0 м = 6.0/100 = 0.06, метро в 340 м = 6.0/440 ≈ 0.0136, школа в 480 м ≈ 0.0086. Тогда score_contribution = round(0.0136*100) = 1 (часто 0), сумма по top-7 ≈ 1–10. Рендерится в Section1ParcelInfo.tsx:297-299 → PoiList2Gis.tsx:133 как `{totalScore.toFixed(0)} / 100` — клиент видит ~5/100 для участка с отличной инфраструктурой. Доп. дефект: пороги weightBadgeVariant (PoiList2Gis.tsx:54-61: ≥0.2/≥0.12/≥0.08) недостижимы при max weight ≈0.06 → каждый POI получает badge 'warning' (амбер). Мок poi-score.json содержит подогнанные weight (0.25/0.15/0.1) и score_contribution (22/9/11/…=76), не воспроизводящие реальную формулу, поэтому в MOCK_POI_SCORE баг скрыт. Routing-режим тоже не спасает: там weight = category_weight * decay(0..1) ≈ 0..6, т.е. тоже не доля. Как чинить: либо backend должен отдавать нормированный 0..100 score, либо frontend нормировать weight (например по max в выборке) перед умножением на 100; пороги badge привести к реальной шкале weight. **Почему баг:** Фронт предполагает weight ∈ [0,1] (комментарий строки 561: 'weight (0..1) × 100'), но backend-формула weight = cat_weight/(dist+100) даёт значения ~0.0001–0.06. Умножение на 100 и суммирование даёт число в шкале, не имеющей отношения к 0..100, после чего оно выводится клиенту как «/100». Подтверждено чтением poi_score.py:31-67,142 (CATEGORY_WEIGHTS до 6.0) и PoiList2Gis.tsx:133, :213, :54-61. **Как чинить:** Нормализовать POI-скор в честную шкалу 0..100 (например, относительно max достижимого weighted score или нормировкой суммы category_weight) на backend и отдавать готовый poi_weighted_score + percentile, а не пытаться восстановить его на фронте из сырых weight. Если деривация остаётся на фронте — не умножать weight*100, а маппить реальный диапазон в 0..100; пороги weightBadgeVariant пересчитать под фактическую шкалу weight. --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.95 · unit U107</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:57:50 +00:00
bot-backend added the
scope/frontend
label 2026-06-15 11:02:12 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода: lib/site-finder-api.ts:531-543 (non-mock: score_contribution: Math.round(p.weight * 100) line 537, комментарий «weight (0..1) × 100» line 536, сумма poi_weighted_score lines 539-542). Рендер Section1ParcelInfo.tsxPoiList2Gis.tsx:133 ({totalScore.toFixed(0)} / 100). Backend формула backend/app/services/site_finder/poi_score.py:131 (weight = (1.0 / (distance_m + 100.0)) * cat_weight), CATEGORY_WEIGHTS lines 21-34 (max metro_stop=6.0). Endpoint backend/app/api/v1/parcels.py:3080 зовёт compute_poi_weighted_top7 напрямую (без decay/routing-параметра).

Корень: Фронт предполагает weight ∈ [0,1], но backend отдаёт weight = cat_weight/(dist+100) ≈ 0.0001–0.06. Метро в 0 м = 6.0/100 = 0.06 → score_contribution = round(6); школа в 480 м ≈ 5.0/580 ≈ 0.0086 → round(1) или 0. Сумма top-7 ≈ 1–10 → клиент видит «~5/100» для отличной локации. Доп. дефект: weightBadgeVariant (PoiList2Gis.tsx:54-61, пороги ≥0.2/≥0.12/≥0.08) недостижимы при max weight ≈0.06 → каждый POI получает badge 'warning'. Badge также рендерит ×{item.weight.toFixed(2)} (PoiList2Gis.tsx:214) → «×0.06»/«×0.01». В dev скрыто (фикстура poi-score.json с подогнанными weight 0.25/0.15/0.1 и score_contribution 22/9/11).

Что править:

  • Предпочтительно backend: compute_poi_weighted_top7 отдаёт нормированный poi_weighted_score 0..100 (например относительно max достижимого weighted score / нормировкой суммы category_weight) + percentile; фронт перестаёт восстанавливать из сырого weight.
  • Если деривация остаётся на фронте: маппить реальный диапазон weight в 0..100 (не *100); пороги weightBadgeVariant пересчитать под фактическую шкалу.

Что посмотреть:

  • Код: lib/site-finder-api.ts:531-543, PoiList2Gis.tsx:54-61,133,214, poi_score.py:21-34,131, parcels.py:3027-3080.
  • Vault: релевантных заметок по POI-score шкале не найдено; B6-контекст в old/code/patterns/SiteFinder_Backend_Migration_Plan_May17.md.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `lib/site-finder-api.ts:531-543` (non-mock: `score_contribution: Math.round(p.weight * 100)` line 537, комментарий «weight (0..1) × 100» line 536, сумма `poi_weighted_score` lines 539-542). Рендер `Section1ParcelInfo.tsx` → `PoiList2Gis.tsx:133` (`{totalScore.toFixed(0)} / 100`). Backend формула `backend/app/services/site_finder/poi_score.py:131` (`weight = (1.0 / (distance_m + 100.0)) * cat_weight`), `CATEGORY_WEIGHTS` lines 21-34 (max metro_stop=6.0). Endpoint `backend/app/api/v1/parcels.py:3080` зовёт `compute_poi_weighted_top7` напрямую (без decay/routing-параметра). **Корень:** Фронт предполагает `weight ∈ [0,1]`, но backend отдаёт `weight = cat_weight/(dist+100)` ≈ 0.0001–0.06. Метро в 0 м = 6.0/100 = 0.06 → `score_contribution = round(6)`; школа в 480 м ≈ 5.0/580 ≈ 0.0086 → round(1) или 0. Сумма top-7 ≈ 1–10 → клиент видит «~5/100» для отличной локации. Доп. дефект: `weightBadgeVariant` (`PoiList2Gis.tsx:54-61`, пороги ≥0.2/≥0.12/≥0.08) недостижимы при max weight ≈0.06 → каждый POI получает badge 'warning'. Badge также рендерит `×{item.weight.toFixed(2)}` (`PoiList2Gis.tsx:214`) → «×0.06»/«×0.01». В dev скрыто (фикстура `poi-score.json` с подогнанными weight 0.25/0.15/0.1 и score_contribution 22/9/11). **Что править:** - Предпочтительно backend: `compute_poi_weighted_top7` отдаёт нормированный `poi_weighted_score` 0..100 (например относительно max достижимого weighted score / нормировкой суммы category_weight) + percentile; фронт перестаёт восстанавливать из сырого weight. - Если деривация остаётся на фронте: маппить реальный диапазон weight в 0..100 (не `*100`); пороги `weightBadgeVariant` пересчитать под фактическую шкалу. **Что посмотреть:** - Код: `lib/site-finder-api.ts:531-543`, `PoiList2Gis.tsx:54-61,133,214`, `poi_score.py:21-34,131`, `parcels.py:3027-3080`. - Vault: релевантных заметок по POI-score шкале не найдено; B6-контекст в `old/code/patterns/SiteFinder_Backend_Migration_Plan_May17.md`.
bot-backend added the
needs-human
label 2026-06-15 16:44:07 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#1486
No description provided.