Commit graph

1129 commits

Author SHA1 Message Date
bot-backend
cb79c67bfc fix(tradein/deactivate): make TTL-cap multiplier configurable per source
Review of 3a1e29a7 found CAP_MULT=2 is uniform across sources with wildly
different ttl_days, so it produces a different ABSOLUTE ceiling per source:
cian/yandex (ttl=30) -> 60d, avito (ttl=10) -> 20d, domklik (ttl=14) -> 28d.
That breaks exactly where the crawl's revisit tail doesn't scale with
ttl_days: avito's measured p99 revisit gap is 42.1d (_REVISIT_TAIL) --
above its own default cap of 20d -- so a legitimately slow-but-alive avito
crawl cycle would get its floor cut below the very tail the floor exists
to protect (the false-kill scenario #2659 was filed for). cian/yandex/
domklik aren't affected: their default ceilings (60/60/28) already sit
comfortably above their own measured tails (26.6/43.0/3.1).

Fix: cap_mult is now a function parameter (same pattern as
revisit_floor_quantile/min_confirmations) with the module constant CAP_MULT
as its default, wired through product_handlers via default_params["cap_mult"]
so a schedule can override it without touching the shared default. Also
closes the ttl_days<=0 edge case flagged in the same review: before the cap,
max(ttl_days, floor) tolerated a misconfigured ttl_days<=0 as long as the
floor was positive; with the cap, min(floor, ttl_days*cap_mult<=0) would
silently defeat that protection and match nearly the whole active pool.
ttl_days<=0 now raises ValueError before any SQL, same contract as the
existing staleness_column whitelist check.

Also verified (read-only, postgres-tradein) the review's core "no-op"
claim: false. scrape_runs.counters for the 6 days since the revisit-floor
went live (08-10..08-15) show the cap DID bind on 3 of 6 runs for avito
(floor 52 vs cap 20) and 3 of 6 for yandex (floor 75 vs cap 60) -- the
reviewer's "no source hits the cap" read a single-day trough right after
a natural recovery, not the whole observation window. See PR discussion
for the full counter history and refutation detail.

Refs #2659
2026-08-15 18:47:49 +03:00
bot-backend
3f5f099392 fix(health): HEAD /health на верном бэкенде (Site Finder) + честные заголовки
Some checks failed
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Failing after 2m34s
CI Trade-In / backend-tests (pull_request) Successful in 5m4s
CI / backend-tests (pull_request) Successful in 16m49s
Review-разбор ветки fix/tradein-uptime-honest-green:

1. [HIGH] Прод-симптом `HEAD gendsgn.ru/health -> 405` обслуживает Site
   Finder (Caddyfile:60 `handle /health { reverse_proxy backend:8000 }`),
   а предыдущий коммит правил только tradein-mvp/backend, чей /health наружу
   не проксируется вообще. Добавлен @app.head("/health") в backend/app/main.py
   рядом с существующим @app.get — эмпирически подтверждено (uv run pytest):
   HEAD было 405, стало 200. tradein-mvp фикс не откачен (безвреден, годится
   для будущего internal-caller), но обвязан комментарием, что реальный
   прод-путь чинится не там.

2. [LOW] Response(status_code=200) без media_type отдавал HEAD без
   Content-Type, тогда как GET отдаёт application/json — расходится с
   заявленным в комментарии RFC 9110 §9.3.2. Добавлен media_type в обоих
   бэкендах; Content-Length сознательно не подгоняем под байты GET-ответа
   (payload header field, RFC разрешает опускать для HEAD) — не дублируем
   сборку payload ради байт-в-байт соответствия.

Тесты: test_health_head_ok_no_body добавлен в backend/tests/test_health.py
(Site Finder) — RED-check (git stash app/main.py) воспроизводит прод-баг
1:1: assert 405 == 200. tradein-mvp/backend/tests/test_health_endpoint.py
дополнен проверкой Content-Type. uv run pytest — все зелёные.
2026-08-15 18:44:31 +03:00
bot-backend
c75206c348 fix(tradein/geocoder): local houses fallback + downgrade DaData CLEAN-disabled noise
28/1084 прод-оценок имели lat IS NULL — гарантированный ноль аналогов, клиент
не получал оценку вовсе. Дом уже был в houses (скрейпленные листинги), но не
резолвился ни geoportal/cad_buildings, ни Nominatim: разговорное/усечённое имя
улицы («Онуфриева» вместо ГАР-каноничного «Начдива Онуфриева») или отсутствующий
в вводе корпус («49» вместо реального «49к1»). Добавлен последний тир geocode()
с двумя defensive-допущениями (суффиксный матч улицы + опциональная догадка
«номер+к1») — при любой неоднозначности возвращает None, а не гадает; проверено
живыми прод-адресами (Онуфриева/Хрустальногорская резолвятся, Крестинского
корректно остаётся неоднозначным — два разных дома в houses под одним номером).

Отдельно: HTTP 403 «услуга CLEAN выключена на аккаунте» логировался как ERROR
на каждый /estimate (164 события) — это статичная конфигурация аккаунта, а не
сбой; понижено до WARNING (первый раз за процесс) + DEBUG на повторы, чтобы
ERROR продолжал значить настоящую проблему.
2026-08-15 18:08:09 +03:00
bot-backend
8fcec9f12e fix(tradein/observability): stop basic_auth 401 and RetryError GlitchTip noise
83% of tracker issues (7460 total) were pure noise drowning real signal:
- basic_auth 401 (3738 issues, 2019 distinct titles) — ops/glitchtip-auth-
  forwarder sent EVERY 401 from bots scanning gendsgn.ru (GET /wp-admin/
  install.php etc.) as an individual GlitchTip event, remote_ip baked into
  message/tags inflated cardinality. Not an application error — expected
  bot-scan traffic against a basic_auth-protected site.
- RetryError (2462 issues) — geocoder.py's three tenacity @retry-wrapped
  Nominatim helpers (lookup/suggest/reverse) raised tenacity.RetryError on
  exhaustion without reraise=True; RetryError.__str__() embeds a Future
  repr() with a memory address that differs every call, so GlitchTip
  grouped each exhausted retry as a distinct issue instead of one.

Fix at the source, not post-hoc issue cleanup:
- forwarder.py: before_send drops events tagged event_type in
  {basic_auth_failed, basic_auth_storm}; forwarder's own capture_exception
  (real script bugs) carries no such tag and passes through untouched.
- geocoder.py: reraise=True on all three @retry decorators — propagates
  the real underlying exception (stable type + stacktrace) instead of the
  unstable RetryError wrapper.
- sentry_scrub.stabilize_retry_error_fingerprint: belt-and-suspenders
  before_send hook, composed into both app/main.py and scheduler_main.py
  (geocoder runs in both processes — FastAPI request path and the
  overnight geocode_missing_listings batch). Collapses any RetryError that
  still slips through into one persistent issue per cause-exception type
  name only — never IP/address/listing-id.

Content-ful categories (OperationalError, city-sweep, harvest_quarter,
cian/avito/yandex sweep failures, scrape_freshness_check — ~700 issues)
are untouched: filters key off event_type tag / exception type name only.
2026-08-15 18:08:04 +03:00
bot-backend
885031420e fix(tradein/scrapers): honest run status — стоп 'done' поверх провала и нуля
Три прод-факта, где status='done' врал о реальном исходе прогона:

- avito_detail_backfill 15.08: {"attempted":64,"failed":57,"enriched":6,"blocked":1}
  -> 'done'. mark_backfill_finished звал mark_done, потому что produced=6 (>0);
  ни _sweep_run_did_nothing (нет anchors_total/errors_count у backfill'ов), ни
  _phase_totally_failed (голые "attempted"/"failed" без фазового префикса) эту
  форму counters не ловили. Новый _failed_ratio_too_high внутри mark_done:
  failed/attempted >= 0.5 -> 'failed', >= 0.15 -> тоже 'failed' (другая
  формулировка причины в error-тексте) — 'partial' статусом не заведён: это
  потребовало бы DROP+ADD CHECK constraint (051_scrape_runs_extend.sql) и
  дообучения ещё 4 мест (Literal-фильтр admin API, статусы фронта, оба
  IN-списка сторожей) — тот же класс проводки, что и у ban_kind (#2686/#2764),
  который сознательно не стал новым статусом.

- yandex_newbuilding_sweep 26.07-10.08: десять прогонов подряд 'done' при
  processed=5 succeeded=0 rows_inserted=0 failed_resolve=4-5 — сторож нулевого
  результата (_alert_if_consecutive_zero_results) не видел ни один результатный
  ключ этого sweep'а и молчал навсегда. _RESULT_COUNTER_KEYS дополнен
  rows_inserted/processed (именно в этом порядке — rows_inserted это результат,
  processed это попытки; иначе "5 обработано, 0 записано" замаскировалось бы
  под measured-5).

- admin-витрина показывала new_count=0 у трёх подряд cian_full_load при реально
  сохранённых saved_inserted=482/214/239 — full-load'ы не пишут ни 'new_count',
  ни 'lots_inserted'. _column_counts дополнен saved_inserted/rows_inserted.

Правки продублированы в scraper_kit/orchestration/runs.py (byte-эквивалент
app.services.scrape_runs, см. докстринг модуля) для параллели: единственный
текущий писатель "attempted"/"failed" (mark_backfill_finished) живёт только в
app-копии, но приоритет ключей/константы держим синхронными на будущее.

Не тронуто: сознательно пустые sweep'ы (errors_count=0, honest empty) и малые
батчи (attempted < 3) — доля отказов на них не считается диагнозом.

Tests: tests/test_honest_run_status_failed_ratio.py (41 кейс, оба модуля,
включая точные прод-числа из трёх фактов выше) + regression-прогон 609 тестов
по всем файлам, трогающим scrape_runs/orchestration.runs — 0 регрессий.
2026-08-15 18:06:33 +03:00
bot-backend
24b70e5c58 fix(tradein): HEAD /health отвечает 200 вместо 405
@app.get("/health") в FastAPI/Starlette не добавляет HEAD-обработчик
автоматически (в отличие от низкоуровневого Route(methods=["GET"])) —
внешний uptime-monитор (GlitchTip PING-тип шлёт HEAD) получал 405 и не
мог отличить "жив" от "мёртв" по статусу. Добавлен явный
@app.head("/health") — 200 без тела (RFC 9110 §9.3.2), GET не тронут.

Тест test_health_endpoint.py фиксирует оба метода; RED до фикса
(HEAD → 405), GREEN после (проверено git stash + повторный прогон).
2026-08-15 17:58:36 +03:00
bot-backend
3a1e29a7da fix(tradein/deactivate): cap effective TTL floor at 2x configured value
Revisit-floor (#2659) raises effective TTL via max(ttl_days, floor) with no
upper bound -- a positive feedback loop confirmed on prod: slow crawl raises
the floor, a high floor keeps stale listings marked active longer than a
fresh sweep needs to return, the "active" pool bloats with rot, and the next
floor measurement on that bloated pool comes out even higher. Yandex counters
sat at ttl_days_effective=75/75/75/39/52/54 for six runs straight with
deactivated=0; 23,687/44,744 "active" avito listings hadn't been confirmed in
>7 days, cian 10,572/19,514 and yandex 7,178/15,790 were >30 days stale, the
oldest "active" row hadn't been seen in 86 days.

CAP_MULT=2 caps the floor's upward push without disabling it -- the floor
still protects against premature deactivation during genuinely slow (but
alive) crawl cycles, it just can no longer grow unbounded. Beyond 2x, a
persistently low crawl rate is better handled by the existing health gate
(min_confirmations), which disables deactivation outright instead of
stretching TTL forever.

When the cap binds, counters gain ttl_floor_capped=1 + ttl_days_floor_raw
(the uncapped value) so it's visible in the run-history dashboard, not just
logs -- counters are stored as-is in scrape_runs.counters.

Single fix point: all four sources (avito/yandex/cian/domklik) route through
this one deactivate_stale_listings() via the product_handlers wildcard
"deactivate_stale_*" handler, so no other task file needed the change.
2026-08-15 17:56:12 +03:00
f3205d3ba1 fix(tradein/cian): вернуть сбор вторички — sweep выбрасывал всё найденное (#2880)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m43s
Deploy Trade-In / build-backend (push) Successful in 39s
Deploy Trade-In / deploy (push) Successful in 1m57s
2026-08-15 13:44:22 +00:00
0f0a3bbd79 fix(mera): кликабельная ссылка на политику ПДн в чекбоксе согласия + синхронизация версии (блоки 2, 5.4) (#2886)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m22s
Deploy Trade-In / test (push) Successful in 3m46s
Deploy Trade-In / build-backend (push) Successful in 2m7s
Deploy Trade-In / deploy (push) Successful in 1m50s
2026-08-14 12:48:33 +00:00
bot-backend
74e7969ef9 fix(tradein/lead): кликабельная ссылка на политику ПДн в чекбоксе согласия
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m6s
CI Trade-In / backend-tests (pull_request) Successful in 4m31s
РКН/владелец: рядом с чекбоксом согласия должна быть ссылка на сам документ
политики обработки ПДн, а не упоминание закона. Чекбокс в LeadForm.tsx
(v2, живой /trade-in/v2) теперь линкует "Политикой обработки персональных
данных" на /mera-public/privacy (target=_blank, чтобы не терять заполненную
форму). Путь вынесен в новый src/lib/legal-copy.ts (модуль без импортов) —
content.ts ре-экспортирует оттуда, чтобы B2B-виджет не тянул B2C-лэндинг-модуль
целиком.

_CONSENT_TEXT_SNAPSHOT/_CONSENT_POLICY_VERSION в lead.py обновлены под новый
плоский текст и дату утверждения политики (PRIVACY_APPROVAL: 2026-08-13).
test_consent_text_frontend_sync.py: экстрактор теперь снимает JSX-теги/{" "}
спейсеры перед сравнением (иначе сломался бы на разметке ссылки) + новый тест
держит _CONSENT_POLICY_VERSION в синхроне с PRIVACY_APPROVAL из content.ts,
чтобы версия не расходилась молча с редакцией документа.

Легаси-дубль в HeroTransparency.tsx (недостижим с живого роута) — текст
приведён в соответствие без ссылки: компонент не смонтирован нигде, и нет
теста, который держал бы там ссылку в актуальном состоянии.
2026-08-14 15:29:49 +03:00
bot-backend
eb0f082790 fix(tradein/pdf): 135-ФЗ дисклеймер в футере каждой страницы (Блок 4.2)
Some checks failed
CI / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 4m42s
Running @bottom-center margin-box печатал только мета/wordmark — юр-требование
(индикативный расчёт, не отчёт об оценке по 135-ФЗ) отсутствовало на всех 4
страницах. Бюджет высоты подвала = margin-bottom (19mm≈53.9pt) уже был
заполнен почти впритык (~51pt) после 42a50cf8 (ровно 4 страницы без пустых).

Сжат существующий HUD-хром внутри _page_footer (margin-top 6→4pt, padding-top
8→6pt, line-height мета/wordmark 1.35→1.15, разделитель margin 6pt 0→3pt 0,
экономия ~13pt) + добавлен текст дисклеймера отдельным блоком (5pt/line-height
1.15, ~3 строки ≈17pt). Экономии внутри подвала не хватило без деградации до
нечитаемого — минимально поднят @page margin-bottom 19mm→21mm (+2mm).

Реальный WeasyPrint-рендер (native Pango/cairo) недоступен на Windows-деве —
пагинация (риск отката к 5-й пустой странице из-за margin-bottom на всех 4
страницах) не подтверждена локально, арифметика в docstring _page_footer.
2026-08-14 15:25:00 +03:00
01b5e73ea4 feat(tradein): вся Свердловская область — 40 городов в city-sweep (#2879)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m33s
Deploy Trade-In / build-backend (push) Successful in 1m41s
Deploy Trade-In / deploy (push) Successful in 1m38s
2026-08-13 19:08:44 +00:00
a4d6cbba25 fix(tradein/proxy): учитывать историю банов при выборе egress-узла (#2877)
All checks were successful
Deploy Trade-In / changes (push) Successful in 21s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m1s
Deploy Trade-In / build-backend (push) Successful in 1m16s
Deploy Trade-In / deploy (push) Successful in 1m24s
2026-08-13 17:47:01 +00:00
d7017ebfee Merge remote-tracking branch 'origin/main' into HEAD
Some checks failed
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 4m21s
2026-08-13 12:36:56 +05:00
d6c000eddb docs(tradein): снять устаревшее обоснование обрыва прогона Домклика (#2854) (#2859)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m30s
Deploy Trade-In / build-backend (push) Successful in 1m7s
Deploy Trade-In / deploy (push) Successful in 1m36s
2026-08-13 07:33:59 +00:00
05d2126c5a chore(tradein/db): убрать три колонки-заглушки витрины поиска (#2857) (#2858)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m32s
Deploy Trade-In / build-backend (push) Successful in 34s
Deploy Trade-In / deploy (push) Successful in 1m50s
2026-08-13 07:25:43 +00:00
06c322c167 fix(tradein/proxy): проба Домклика спрашивает защищённый путь, а не robots.txt (#2855) (#2856)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m31s
Deploy Trade-In / build-backend (push) Successful in 1m40s
Deploy Trade-In / deploy (push) Successful in 2m9s
2026-08-13 06:02:38 +00:00
4b527751b9 fix(tradein/domclick): распознанный QRATOR-блок свипа — ban_kind='platform', не 'unknown' (#2687) (#2832)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m27s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 2m49s
2026-08-12 20:55:05 +00:00
17d558b18c fix(tradein/yandex): снести признак «панорама» — его нет на площадке (#2851)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m27s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 2m12s
2026-08-12 20:44:11 +00:00
3eabbd0186 fix(tradein/quality): витрина перестаёт рапортовать «доля с кадастром» (#2674) (#2850)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m31s
Deploy Trade-In / build-backend (push) Successful in 33s
Deploy Trade-In / deploy (push) Successful in 1m26s
2026-08-12 20:37:16 +00:00
bcc4f2c29e docs(tradein/estimator): докстринг домового IMV-якоря говорит правду о таблице (#2674) (#2852)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m28s
Deploy Trade-In / build-backend (push) Successful in 1m2s
Deploy Trade-In / deploy (push) Successful in 1m19s
2026-08-12 20:29:28 +00:00
13ce1045bb fix(tradein/estimate): домовой IMV-якорь приводится к базису ремонта квартиры (#2677) (#2816)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m30s
Deploy Trade-In / build-backend (push) Successful in 1m0s
Deploy Trade-In / deploy (push) Successful in 1m52s
2026-08-12 20:17:10 +00:00
3cd7e0a9c4 fix(tradein/sber): сторож мерит отставание загрузки, а не календарь (#2846) (#2849)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m32s
Deploy Trade-In / build-backend (push) Successful in 1m2s
Deploy Trade-In / deploy (push) Successful in 2m3s
2026-08-12 19:36:20 +00:00
e59a102b16 fix(tradein/dkp): «ФАКТИЧЕСКИЕ СДЕЛКИ» называют свой возраст, а не окно поиска (#2847)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m13s
Deploy Trade-In / test (push) Successful in 3m33s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy (push) Successful in 7m1s
2026-08-12 18:51:10 +00:00
1ff6699b95 feat(tradein/scheduler): планировщик подхватывает чекпоинт оборванного прогона (#930) (#2845)
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-08-12 18:51:02 +00:00
bot-backend
2c8dc46205 Merge remote-tracking branch 'origin/main' into fix/2683-manifest-drift
Some checks failed
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 54s
CI / openapi-codegen-check (pull_request) Successful in 2m54s
CI / backend-tests (pull_request) Failing after 1m14s
CI Trade-In / frontend-checks (pull_request) Successful in 1m19s
CI Trade-In / backend-tests (pull_request) Successful in 4m44s
# Conflicts:
#	tradein-mvp/backend/data/sql/_manifest_applied.txt
#	tradein-mvp/backend/tests/skip_allowlist.txt
2026-08-12 23:06:41 +05:00
081319c290 fix(tradein/ci): объявить пропуски живых тестов повтора IMV — деплой-лэйн без БД (#2844)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m32s
Deploy Trade-In / build-backend (push) Successful in 1m10s
Deploy Trade-In / deploy (push) Successful in 7m56s
2026-08-12 16:38:02 +00:00
c927b77777 fix(tradein/imv): «временная» ошибка снова временная — 1390 домов возвращаются в очередь (#2843)
Some checks failed
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Failing after 3m29s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Has been skipped
2026-08-12 16:06:24 +00:00
9a89c34971 fix(tradein/estimate): не подставлять квартал соседа вместо квартала цели (#2839)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m13s
Deploy Trade-In / build-backend (push) Successful in 1m2s
Deploy Trade-In / deploy (push) Successful in 1m30s
2026-08-12 15:55:24 +00:00
b5f3366577 fix(tradein/yandex): 3535 source_url ведут на сайт застройщика — переписываем из offerId (#2840)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m17s
Deploy Trade-In / build-backend (push) Successful in 30s
Deploy Trade-In / deploy (push) Successful in 2m14s
2026-08-12 15:33:31 +00:00
447fbbd3a5 fix(tradein/yandex): «непригодных» 3535 не было — адресуем их по offerId (#2838)
Some checks failed
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m9s
Deploy Trade-In / build-backend (push) Failing after 1m7s
Deploy Trade-In / deploy (push) Has been skipped
2026-08-12 15:04:53 +00:00
677fcb749c fix(tradein/geocoder): инвариант «чужой топоним + результат внутри ЕКБ = недостоверно» (#2590) (#2837)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m11s
Deploy Trade-In / build-backend (push) Successful in 1m3s
Deploy Trade-In / deploy (push) Successful in 2m9s
2026-08-12 14:26:58 +00:00
dbe3e05fb1 fix(tradein/estimate): нет индекса у аналогов — нет квартальной поправки (#2583 C3)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m59s
price_index нормирован на медиану Екатеринбурга (99a_quarter_price_index.sql),
поэтому фолбэк `avg_analog_index = ... else 1.0` подставлял в знаменатель
gap-коррекции не «нейтраль», а уровень ЕКБ. Для цели вне ЕКБ (индексы области
0.28–0.82) это превращало поправку в безусловную скидку: factor = target_qi,
после клампа до −40%, с подписью «Учтена локация квартала» — то есть догадка
выдавалась пользователю за методику.

Нет данных → нет поправки. Ровно тот же factor=1.0 получается из avg := target_qi,
и это лучшая оценка неизвестного avg на живых данных: медиана |ошибки| 0.116
против 0.161 у 1.0, p90 0.337 против 0.517 (400 лотов, 2026-08-12).

Проверка направления на сделках Росреестра (12 мес, медианы ₽/м² по городам):
без поправки ошибка +0…+14%, с текущей поправкой −32…−40%. Правка поднимает
цену и одновременно уводит её к правде, а не просто вверх.

MV и FDW не трогаются намеренно: строки basis='district'/'city_fallback' имеют
n_deals 3–4, а эстиматор требует n_deals >= 10 — второй 1.0 (city_fallback в
99a) до него структурно не доходит (прод: 0 из 1894 строк видимы).

Тесты: два прежних кейса задавали уровень аналогов отсутствием кадастра,
то есть опирались на сам дефект — переведены на явную карту analog_indexes.

Refs #2583
2026-08-12 19:18:01 +05:00
bot-backend
bbed743773 Merge remote-tracking branch 'origin/main' into HEAD
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m57s
2026-08-12 18:06:20 +05:00
bot-backend
3f87873d7b Merge remote-tracking branch 'origin/main' into fix/2656-anchor-ratio-freshness
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m6s
Ветка отстала от main на 151 коммит. Текстовых конфликтов нет, семантический — один.

`test_imv_card_survives_when_headline_suppressed_and_anchor_absent` добывал нулевой
headline тонкой выборкой (n=3 < HEADLINE_LISTINGS_MIN_N) — гейт достаточности его
обнулял. #oblast-F (#2823, смержен 2026-08-09) это поведение СНЯЛ: тонкая выборка
больше не обнуляет headline, только помечает низкую надёжность. Тест падал на
собственной предпосылке, а не на щели, которую стережёт. Нулевой headline берётся
отсутствием аналогов (n=0) — единственное оставшееся нулевое состояние; сама щель
(«тир добыт, якоря нет, headline нулевой → карточка IMV не должна исчезнуть») от
этого не изменилась. Фальсификация: возврат старого условия display-блока
(`anchor_tier is not None and ...`) снова роняет тест.

Прогон полного набора на смерженном дереве: 4248 passed, 18 skipped.
2026-08-12 17:49:48 +05:00
ad190be233 fix(tradein/estimate): отдавать фактический радиус подбора, а не выбор пользователя (#2835)
All checks were successful
Deploy Trade-In / changes (push) Successful in 18s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m23s
Deploy Trade-In / build-backend (push) Successful in 1m6s
Deploy Trade-In / deploy (push) Successful in 1m47s
2026-08-12 12:48:18 +00:00
4d31a0ee82 fix(tradein/scraper): пустой пул прокси перестаёт стирать чекпоинт полного обхода (#2834)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m6s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 1m23s
2026-08-12 12:18:03 +00:00
e17687aed7 fix(tradein/scrapers): убрать оставшиеся обходы пула прокси (#2830) (#2833)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m13s
Deploy Trade-In / build-backend (push) Successful in 1m46s
Deploy Trade-In / deploy (push) Successful in 1m48s
2026-08-12 10:53:36 +00:00
f2cbd76ae0 fix(tradein/scrapers): egress по источнику из пула с учётом банов, а не статичный env (#2831)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m12s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m27s
2026-08-11 06:29:48 +00:00
29db137375 fix(tradein/admin): развести исходы проверки кук — бан не равен «плохим кукам» (#2829)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m9s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m31s
2026-08-11 06:08:16 +00:00
55c25416f6 fix(tradein/estimate): не перетирать дату обращения при оживлении оценки (#2828)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m10s
Deploy Trade-In / build-backend (push) Successful in 1m3s
Deploy Trade-In / deploy (push) Successful in 1m48s
2026-08-11 05:52:05 +00:00
82d8db9f42 fix(tradein/estimate): оживлять мёртвые сохранённые оценки при открытии (#2826)
All checks were successful
Deploy Trade-In / changes (push) Successful in 15s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 3m11s
Deploy Trade-In / test (push) Successful in 3m42s
Deploy Trade-In / build-backend (push) Successful in 1m8s
Deploy Trade-In / deploy (push) Successful in 6m59s
2026-08-11 04:15:40 +00:00
0b84f0b661 fix(tradein/estimate): уверенность не может противоречить надёжности выборки (#2825)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m6s
Deploy Trade-In / build-backend (push) Successful in 1m5s
Deploy Trade-In / deploy (push) Successful in 2m1s
2026-08-10 16:21:31 +00:00
8423af5dd5 feat(tradein): версионирование продукта — единый источник, подвал, PDF, /versions (#2824)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 36s
Deploy Trade-In / build-frontend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m14s
Deploy Trade-In / build-backend (push) Successful in 4m19s
Deploy Trade-In / deploy (push) Successful in 1m44s
2026-08-10 16:00:54 +00:00
9d9457f67d fix(tradein/estimate): не блокировать оценку — расширять подбор и честно предупреждать (#2823)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m30s
Deploy Trade-In / test (push) Successful in 3m14s
Deploy Trade-In / build-backend (push) Successful in 7m24s
Deploy Trade-In / deploy (push) Successful in 1m25s
2026-08-10 15:42:22 +00:00
74344f7b8b docs(tradein/scraper): 42 прогона, а не 52 — в замере считались пары «прогон × фаза» (#2700) (#2822)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m5s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 1m36s
2026-08-10 11:49:27 +00:00
20ec6a5d33 fix(tradein/cian): 403 снимает узел с выдачи, а не гаснет в return None (#2700) (#2821)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m10s
Deploy Trade-In / build-backend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 2m4s
2026-08-10 11:29:19 +00:00
0ed0140c9e fix(tradein/dedup): остаток схлопывания домов становится измеряемым числом, а не оценкой (#2690) (#2820)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m11s
Deploy Trade-In / build-backend (push) Successful in 1m4s
Deploy Trade-In / deploy (push) Successful in 1m57s
2026-08-10 11:18:19 +00:00
405d2f2eec fix(tradein/db): разовая чистка 1123 адресов Авито с приклеенным рейтингом (#2814) (#2818)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m9s
Deploy Trade-In / build-backend (push) Successful in 31s
Deploy Trade-In / deploy (push) Successful in 1m42s
2026-08-10 10:22:33 +00:00
5ee4126ed0 fix(tradein/avito): рейтинг дома перестаёт уезжать в адрес объявления (#2814) (#2815)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m9s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 1m21s
2026-08-10 09:47:15 +00:00