[AUDIT #1871 остаток] invest_score advisory flag + units_sold align + yandex total_seen obs + frontend polish #1926

Closed
opened 2026-06-26 17:39:49 +00:00 by bot-backend · 4 comments
Collaborator

Вынос un-ticketed остатка зонтика #1871 (закрыт). Каждый пункт с точным фиксом (verify-прогон 2026-06-26, готово к pickup).

🟠 Backend (bot-safe, подтверждено still-real)

  • units_sold недосчёт ~1600 — competitor-блок считает flats_sold через domrf_kn_flats.status (заполнен ~0.2%), а MarketMetrics — через objective_lots.contract_date (100%). Fix: в backend/app/services/site_finder/competitors.py:464-467 переключить flats_sold на objective_lots.contract_date IS NOT NULL (через objective_complex_mapping.domrf_obj_id, DISTINCT ON latest snapshot как в market_metrics). Backend-only, цифра вырастет до корректной.
  • yandex scrape_runs.total_seen=0 (observability врёт) — YandexCitySweepCounters.to_dict() (tradein-mvp/.../scrape_pipeline.py:1401-1420) пишет lots_fetched/inserted/updated только в counters jsonb, не в колонку total_seen. Fix: маппинг lots_fetched→total_seen, lots_inserted→new_count в to_dict() ИЛИ extend scrape_runs.mark_done/update_heartbeat (scrape_runs.py:141-158) на извлечение из counters. (То же касается CitySweepCounters — проверить avito/cian.)
  • invest_score advisory-флаг — бэк уже отдаёт score/score_label/score_max_reference/score_explanation (parcels.py:3152), но нет структурного флага. Fix: добавить score_is_advisory: true (или score_advisory:{kind:'poi_density_heuristic',scale_min:0,scale_max:40}) в payload (parcels.py:~3144) + в AnalyzeResponse (schemas/parcel.py) + cd frontend && npm run codegen. Additive, optional. (Низкий приоритет — prose-explanation уже есть.)

🟡 Frontend (домен #1922 / frontend-контрибьютора — СНАЧАЛА проверить какая поверхность live)

⚠️ verify-агент спутал /tradein-mvp/ (LIVE /trade-in, фиксы #1922/27766ac уже на проде с деплоем 87364ee) и откат ПТИЦА-кокпита (/frontend/src/components/site-finder/ptica, 21.06). Перед фиксом — определить live-поверхность.

  • viz-токены/frontend/src/components/trade-in/PriceRangeBar.tsx:33 хардкод #1d4ed8/#bfdbfevar(--viz-1..). (#1922 пофиксил аналог в tradein-mvp.)
  • <h1> на site-finder analysis (AnalysisPageContent.tsx — нет h1).
  • Forecast confidence bandForecastChart.tsx без band; confidenceLevelToValue() объявлен но не вызывается (forecast-helpers.ts:122-130) — латентный риск.
  • velocity disclosure rendering — бэк-поля есть (velocity_source/objective_coverage_pct/proxy_used/proxy_sqm_per_deal, velocity.py), фронт их НЕ рендерит → показать coverage% + proxy-флаг.
  • (проверить) ptica-adapt isReal — verify-агент: в live /frontend уже корректно (notReal() для стабов); MOI-тултип / footer — не найдены/unclear.

Уже сделано (закрыто в #1871)

§22 scenarios collapse (dcccebf) · ghost-anchor (#1919+#1920) · radius-dedup (#1919) · velocity fallback 4500→750 (adf3ec7) · velocity proxy-disclosure backend (есть) · market_trend staleness (status+as_of_date) · avito non-EKB coords (non-issue) · geom::geography GIST (#1189/#1924) · абсолюты/эмодзи/viz в tradein-mvp (#1922).

🔗 Tracked elsewhere

CIAN stale/freeze → #1781, #885, #883 · DomClick → #1846 · split-домов коридор → #1772, #1795 · n1_city_sweep off → #854.

Refs #1871. scope/backend (+frontend), site-finder, tradein, priority/p3.

Вынос un-ticketed остатка зонтика #1871 (закрыт). Каждый пункт с точным фиксом (verify-прогон 2026-06-26, готово к pickup). ## 🟠 Backend (bot-safe, подтверждено still-real) - [ ] **units_sold недосчёт ~1600** — competitor-блок считает `flats_sold` через `domrf_kn_flats.status` (заполнен ~0.2%), а MarketMetrics — через `objective_lots.contract_date` (100%). Fix: в `backend/app/services/site_finder/competitors.py:464-467` переключить `flats_sold` на `objective_lots.contract_date IS NOT NULL` (через `objective_complex_mapping.domrf_obj_id`, DISTINCT ON latest snapshot как в market_metrics). Backend-only, цифра вырастет до корректной. - [ ] **yandex `scrape_runs.total_seen=0`** (observability врёт) — `YandexCitySweepCounters.to_dict()` (`tradein-mvp/.../scrape_pipeline.py:1401-1420`) пишет `lots_fetched/inserted/updated` только в `counters` jsonb, не в колонку `total_seen`. Fix: маппинг `lots_fetched→total_seen`, `lots_inserted→new_count` в to_dict() ИЛИ extend `scrape_runs.mark_done`/`update_heartbeat` (scrape_runs.py:141-158) на извлечение из counters. (То же касается `CitySweepCounters` — проверить avito/cian.) - [ ] **invest_score advisory-флаг** — бэк уже отдаёт `score/score_label/score_max_reference/score_explanation` (`parcels.py:3152`), но нет структурного флага. Fix: добавить `score_is_advisory: true` (или `score_advisory:{kind:'poi_density_heuristic',scale_min:0,scale_max:40}`) в payload (`parcels.py:~3144`) + в `AnalyzeResponse` (schemas/parcel.py) + `cd frontend && npm run codegen`. Additive, optional. (Низкий приоритет — prose-explanation уже есть.) ## 🟡 Frontend (домен #1922 / frontend-контрибьютора — СНАЧАЛА проверить какая поверхность live) ⚠️ verify-агент спутал `/tradein-mvp/` (LIVE /trade-in, фиксы #1922/27766ac уже на проде с деплоем 87364ee) и откат ПТИЦА-кокпита (`/frontend/src/components/site-finder/ptica`, 21.06). Перед фиксом — определить live-поверхность. - [ ] **viz-токены** — `/frontend/src/components/trade-in/PriceRangeBar.tsx:33` хардкод `#1d4ed8/#bfdbfe` → `var(--viz-1..)`. (#1922 пофиксил аналог в tradein-mvp.) - [ ] **`<h1>`** на site-finder analysis (`AnalysisPageContent.tsx` — нет h1). - [ ] **Forecast confidence band** — `ForecastChart.tsx` без band; `confidenceLevelToValue()` объявлен но не вызывается (forecast-helpers.ts:122-130) — латентный риск. - [ ] **velocity disclosure rendering** — бэк-поля есть (`velocity_source/objective_coverage_pct/proxy_used/proxy_sqm_per_deal`, velocity.py), фронт их НЕ рендерит → показать coverage% + proxy-флаг. - [ ] (проверить) ptica-adapt isReal — verify-агент: в live `/frontend` уже корректно (`notReal()` для стабов); MOI-тултип / footer — не найдены/unclear. ## ✅ Уже сделано (закрыто в #1871) §22 scenarios collapse (dcccebf) · ghost-anchor (#1919+#1920) · radius-dedup (#1919) · velocity fallback 4500→750 (adf3ec7) · velocity proxy-disclosure backend (есть) · market_trend staleness (status+as_of_date) · avito non-EKB coords (non-issue) · geom::geography GIST (#1189/#1924) · абсолюты/эмодзи/viz в tradein-mvp (#1922). ## 🔗 Tracked elsewhere CIAN stale/freeze → #1781, #885, #883 · DomClick → #1846 · split-домов коридор → #1772, #1795 · n1_city_sweep off → #854. Refs #1871. scope/backend (+frontend), site-finder, tradein, priority/p3.
bot-backend added the
priority/p3
scope/backend
scope/frontend
site-finder
tradein
labels 2026-06-26 17:40:00 +00:00
Author
Collaborator

Backend-часть закрыта (2 из 3 backend-пунктов done)

  • units_sold недосчёт ~1600 — PR #1927 (merged): competitor flats_sold теперь через objective_lots (contract_date/is_sold/status='продан', premise_kind='квартира', COUNT(DISTINCT lot_id) fan-out-safe) — совпадает с MarketMetrics. deep code-review поймал 2 бага (JOIN fan-out + missing premise_kind) → починены до мержа. Full suite 3530 passed.
  • yandex total_seen=0 observability — PR #1928 (merged+deployed): _column_counts() мапит lots_fetched→total_seen, lots_inserted→new_count в mark_done/heartbeat/failed/banned (покрывает yandex+avito+cian+domclick+newbuilding).
  • invest_score advisory-флаг — остаётся (нужен codegen; low-pri, prose-explanation уже есть).
  • Frontend-полиш (viz-токены/h1/forecast-band/velocity-rendering) — домен frontend-контрибьютора (#1922-ветка), сначала verify live-поверхности после отката кокпита.

Остаётся 1 backend (invest_score flag, p3) + фронт. Держу open под них.

## Backend-часть закрыта (2 из 3 backend-пунктов done) - ✅ **units_sold недосчёт ~1600** — PR **#1927** (merged): competitor `flats_sold` теперь через `objective_lots` (contract_date/is_sold/status='продан', `premise_kind='квартира'`, `COUNT(DISTINCT lot_id)` fan-out-safe) — совпадает с MarketMetrics. deep code-review поймал 2 бага (JOIN fan-out + missing premise_kind) → починены до мержа. Full suite 3530 passed. - ✅ **yandex `total_seen=0` observability** — PR **#1928** (merged+deployed): `_column_counts()` мапит lots_fetched→total_seen, lots_inserted→new_count в mark_done/heartbeat/failed/banned (покрывает yandex+avito+cian+domclick+newbuilding). - ⬜ **invest_score advisory-флаг** — остаётся (нужен codegen; low-pri, prose-explanation уже есть). - ⬜ **Frontend-полиш** (viz-токены/h1/forecast-band/velocity-rendering) — домен frontend-контрибьютора (#1922-ветка), сначала verify live-поверхности после отката кокпита. Остаётся 1 backend (invest_score flag, p3) + фронт. Держу open под них.
Author
Collaborator

Статус bot-safe backend-пунктов (2026-06-27)

  • units_sold недосчёт — починено в #1927 (смержено+задеплоено): competitors.py _SOLD_COUNT_SQL через objective_lots.contract_date IS NOT NULL + premise_kind='квартира' фильтр, COUNT(DISTINCT objective_lot_id) (fan-out-safe). code-reviewer поймал 2 бага (JOIN fan-out + missing premise filter) до мержа.
  • yandex scrape_runs.total_seen=0 — починено в #1928 (смержено+задеплоено): единый _column_counts() в scrape_runs.py маппит lots_fetched→total_seen, lots_inserted→new_count во ВСЕХ путях (update_heartbeat/mark_done/mark_failed/mark_banned) → покрывает yandex + avito/cian (CitySweepCounters) + newbuilding + domclick одним choke-point'ом. PR #1937 добавил недостающие regression-тесты на avito/cian + mark_failed пути (аудит явно просил «проверить avito/cian»).
  • ⏸️ invest_score advisory-флаг — НЕ сделано (низкий приоритет: бэк уже отдаёт score/score_label/score_max_reference/score_explanation — prose-explanation покрывает суть). Чисто additive score_is_advisory остаётся как nice-to-have; беру если скажешь.

Frontend-пункты (viz-токены / h1 / forecast band / velocity disclosure / ptica isReal) — домен #1922 / frontend-контрибьютора, требуют определения live-поверхности перед правкой (verify-агент путал tradein-mvp vs откат ПТИЦА-кокпита). Не трогаю без подтверждения поверхности.

Итог: все 3 bot-safe backend-«врёт»-бага (units_sold недосчёт, total_seen=0) закрыты и в проде. Остаток #1926 = 1 low-pri additive флаг + frontend-polish (чужой домен). Можно низводить приоритет до p3/закрывать backend-часть.

## Статус bot-safe backend-пунктов (2026-06-27) - ✅ **units_sold недосчёт** — починено в **#1927** (смержено+задеплоено): `competitors.py` `_SOLD_COUNT_SQL` через `objective_lots.contract_date IS NOT NULL` + `premise_kind='квартира'` фильтр, `COUNT(DISTINCT objective_lot_id)` (fan-out-safe). code-reviewer поймал 2 бага (JOIN fan-out + missing premise filter) до мержа. - ✅ **yandex `scrape_runs.total_seen=0`** — починено в **#1928** (смержено+задеплоено): единый `_column_counts()` в `scrape_runs.py` маппит `lots_fetched→total_seen`, `lots_inserted→new_count` во ВСЕХ путях (`update_heartbeat`/`mark_done`/`mark_failed`/`mark_banned`) → покрывает yandex + avito/cian (`CitySweepCounters`) + newbuilding + domclick одним choke-point'ом. **PR #1937** добавил недостающие regression-тесты на avito/cian + `mark_failed` пути (аудит явно просил «проверить avito/cian»). - ⏸️ **invest_score advisory-флаг** — НЕ сделано (низкий приоритет: бэк уже отдаёт `score/score_label/score_max_reference/score_explanation` — prose-explanation покрывает суть). Чисто additive `score_is_advisory` остаётся как nice-to-have; беру если скажешь. **Frontend-пункты** (viz-токены / h1 / forecast band / velocity disclosure / ptica isReal) — домен #1922 / frontend-контрибьютора, требуют определения live-поверхности перед правкой (verify-агент путал tradein-mvp vs откат ПТИЦА-кокпита). Не трогаю без подтверждения поверхности. **Итог:** все 3 bot-safe backend-«врёт»-бага (units_sold недосчёт, total_seen=0) закрыты и в проде. Остаток #1926 = 1 low-pri additive флаг + frontend-polish (чужой домен). Можно низводить приоритет до p3/закрывать backend-часть.
Author
Collaborator

Site-finder часть закрыта (PR #2094 + ранее #2054), deployed + verified

Backend:

  • units_sold align — закрыто ранее в #2054: competitors._SOLD_COUNT_SQL теперь считает flats_sold через objective_lots (is_sold/contract_date), побайтово зеркалит n_sold (комментарий в competitors.py:468-479). domrf_kn_flats.status больше не используется для sold.
  • invest_score advisory-флаг (#2094) — добавлен machine-readable score_is_advisory:true + score_advisory{kind:'poi_density_heuristic',scale_min:0,scale_max:40} в analyze-payload + AnalyzeResponse. Прод-verified: /analyze 66:41:0205010:287 → score_is_advisory:True, score_advisory:{...scale_max:40.0}.

Frontend (#2094, LIVE-поверхность AnalysisPageContent, НЕ откатанный ptica):

  • velocity data-honestyVelocityBlock: бейдж «Источник: Objective» + --warn-soft caveat (role=note) когда объём по прокси/кад.кварталу, а не прямые продажи Objective. + убран хардкод bg-amber-* → viz-токены.
  • <h1> a11y — добавлен во ВСЕ 3 состояния (loading/error/success); раньше был только в success.
  • forecast confidence band / confidenceLevelToValue — удалён неиспользуемый export (latent risk); band не строили (требует дизайна).

code-review APPROVE · CI зелёный (incl. openapi-codegen-check) · deployed + прод-verified.

Остаётся (2 пункта — tradein-стек, не site-finder)

  • yandex scrape_runs.total_seen=0 (observability) — tradein-mvp/.../scrape_pipeline.py маппинг lots_fetched→total_seen.
  • PriceRangeBar viz-токеныfrontend/src/components/trade-in/PriceRangeBar.tsx:33 хардкод #1d4ed8/#bfdbfevar(--viz-*).

Эти два — tradein-домен; site-finder зонтик #1871 по этому тикету закрыт. Предлагаю либо добить эти два отдельным tradein-PR, либо перевесить тикет в tradein-беклог.

## ✅ Site-finder часть закрыта (PR #2094 + ранее #2054), deployed + verified **Backend:** - ✅ **units_sold align** — закрыто ранее в #2054: `competitors._SOLD_COUNT_SQL` теперь считает `flats_sold` через `objective_lots` (is_sold/contract_date), побайтово зеркалит n_sold (комментарий в competitors.py:468-479). domrf_kn_flats.status больше не используется для sold. - ✅ **invest_score advisory-флаг** (#2094) — добавлен machine-readable `score_is_advisory:true` + `score_advisory{kind:'poi_density_heuristic',scale_min:0,scale_max:40}` в analyze-payload + `AnalyzeResponse`. Прод-verified: `/analyze` 66:41:0205010:287 → `score_is_advisory:True, score_advisory:{...scale_max:40.0}`. **Frontend (#2094, LIVE-поверхность `AnalysisPageContent`, НЕ откатанный ptica):** - ✅ **velocity data-honesty** — `VelocityBlock`: бейдж «Источник: Objective» + `--warn-soft` caveat (role=note) когда объём по прокси/кад.кварталу, а не прямые продажи Objective. + убран хардкод `bg-amber-*` → viz-токены. - ✅ **`<h1>` a11y** — добавлен во ВСЕ 3 состояния (loading/error/success); раньше был только в success. - ✅ **forecast confidence band / `confidenceLevelToValue`** — удалён неиспользуемый export (latent risk); band не строили (требует дизайна). code-review APPROVE · CI зелёный (incl. openapi-codegen-check) · deployed + прод-verified. ## Остаётся (2 пункта — tradein-стек, не site-finder) - ⏳ **yandex `scrape_runs.total_seen=0`** (observability) — `tradein-mvp/.../scrape_pipeline.py` маппинг `lots_fetched→total_seen`. - ⏳ **PriceRangeBar viz-токены** — `frontend/src/components/trade-in/PriceRangeBar.tsx:33` хардкод `#1d4ed8/#bfdbfe` → `var(--viz-*)`. Эти два — tradein-домен; site-finder зонтик #1871 по этому тикету закрыт. Предлагаю либо добить эти два отдельным tradein-PR, либо перевесить тикет в tradein-беклог.
Author
Collaborator

Полная проверка остатка 2026-07-04 — все пункты закрыты или разнесены

Два агента перепроверили каждый пункт против текущего main + прод.

Backend

  • units_sold недосчёт — уже закрыт ранее: PR #1927 (align на objective_lots-предикат is_sold OR contract_date OR status='продан' через _SOLD_COUNT_SQL), #1964 (physflat-дедуп ре-листингов 2.91× + honest window, hunt-verify 7082=7082), #2054 (pushdown + covering index, 6896→437 мс). Сознательное отклонение от формулировки аудита: немаппленный конкурент → flats_sold=None → нейтральный stage 0.5, а НЕ fallback на domrf_kn_flats.status (источник заполнен 0.2% — fallback воскресил бы тот самый недосчёт). Принято ревью #1927.
  • invest_score advisory — уже в main: score_is_advisory + структурный score_advisory (parcels.py:3886-3891, schemas/parcel.py:865-866), api-types актуальны.
  • ➡️ yandex total_seen — вынесен в #2369 для tradein-трека (пересекается файлами с активным Group F — не берём параллельно).

Frontend

  • Forecast confidence band — PR #2368 (единственный реально оставшийся пункт): качественный band по уровню уверенности (в payload нет p25/p75 — подписан честно), пунктир прогноза, ECharts stack+stackStrategy:"all". confidenceLevelToValue() в коде уже отсутствовал (мёртвый код убран ранее) — вместо него confidenceBandHalfWidth.
  • velocity proxy-disclosure — уже в main (VelocityBlock.tsx / velocityProvenance: источник, покрытие Objective N%, бейдж прокси).
  • h1 на analysis — уже в main (PageHeading, единственный h1 во всех состояниях).
  • 🚫 PriceRangeBar viz-токены (frontend/src/components/trade-in/) — поверхность МЕРТВА в проде: весь /trade-in* затенён Caddy → отдельный tradein-frontend (см. PR #2365). Живой аналог в tradein-mvp зафиксен ещё #1922. Чинить хардкод в мёртвом компоненте не стали.
  • ptica-adapt isReal — verify подтвердил корректность в live frontend ещё 2026-06-26.

Закрываю после мержа #2368.

## Полная проверка остатка 2026-07-04 — все пункты закрыты или разнесены Два агента перепроверили каждый пункт против текущего main + прод. ### Backend - ✅ **units_sold недосчёт** — уже закрыт ранее: PR #1927 (align на objective_lots-предикат `is_sold OR contract_date OR status='продан'` через `_SOLD_COUNT_SQL`), #1964 (physflat-дедуп ре-листингов 2.91× + honest window, hunt-verify 7082=7082), #2054 (pushdown + covering index, 6896→437 мс). **Сознательное отклонение от формулировки аудита**: немаппленный конкурент → `flats_sold=None` → нейтральный stage 0.5, а НЕ fallback на `domrf_kn_flats.status` (источник заполнен 0.2% — fallback воскресил бы тот самый недосчёт). Принято ревью #1927. - ✅ **invest_score advisory** — уже в main: `score_is_advisory` + структурный `score_advisory` (parcels.py:3886-3891, schemas/parcel.py:865-866), api-types актуальны. - ➡️ **yandex total_seen** — вынесен в **#2369** для tradein-трека (пересекается файлами с активным Group F — не берём параллельно). ### Frontend - ✅ **Forecast confidence band** — PR **#2368** (единственный реально оставшийся пункт): качественный band по уровню уверенности (в payload нет p25/p75 — подписан честно), пунктир прогноза, ECharts stack+`stackStrategy:"all"`. `confidenceLevelToValue()` в коде уже отсутствовал (мёртвый код убран ранее) — вместо него `confidenceBandHalfWidth`. - ✅ **velocity proxy-disclosure** — уже в main (`VelocityBlock.tsx` / `velocityProvenance`: источник, покрытие Objective N%, бейдж прокси). - ✅ **h1 на analysis** — уже в main (`PageHeading`, единственный h1 во всех состояниях). - 🚫 **PriceRangeBar viz-токены** (`frontend/src/components/trade-in/`) — поверхность МЕРТВА в проде: весь `/trade-in*` затенён Caddy → отдельный tradein-frontend (см. PR #2365). Живой аналог в tradein-mvp зафиксен ещё #1922. Чинить хардкод в мёртвом компоненте не стали. - ✅ **ptica-adapt isReal** — verify подтвердил корректность в live frontend ещё 2026-06-26. Закрываю после мержа #2368.
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#1926
No description provided.