Commit graph

3325 commits

Author SHA1 Message Date
64ec4a1aad Merge pull request 'fix(scraper-kit): чекпоинт avito_city_sweep доживает до финализатора — 0/67 прогонов писали done_buckets' (#3330) from fix/3319-citysweep-checkpoint into main
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 / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-09-05 17:36:50 +00:00
8f88841bff Merge pull request 'fix(mera-public): подписи метрик снижения цены и бенчмарк отчёта не называют площадку' (#3345) from fix/mera-public-price-cut-note-imv into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 14s
Deploy Trade-In / test (push) Successful in 4m10s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m18s
Deploy Trade-In / build-backend (push) Successful in 1m7s
Deploy Trade-In / deploy (push) Successful in 2m6s
Deploy Trade-In / deploy-status (push) Successful in 2s
2026-09-04 08:36:46 +00:00
bot-backend
76b24e9f4d fix(mera-public): подписи метрик снижения цены и бенчмарк отчёта не называют площадку
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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 1m12s
CI Trade-In / backend-tests (pull_request) Successful in 5m5s
Замечание юриста (Сайт_МЕРА_v2, п.3): публичный измеримый контент не должен
строиться на названных данных площадок.

- landing_stats.py: note метрик price_cut_share_pct / price_cut_median_pct_per_month
  «Только Домклик (…)» → «Только одна из площадок (…)». Текст персистится в
  landing_stats.note и рендерится на лендинге в блоке «цена ошибки» — после
  деплоя нужен пересчёт (refresh_landing_stats), деплой кода note не перепишет.
- HeroSummary.tsx / IMVBenchmark.tsx: убраны видимые «IMV» и title
  «Information Market Value» / «Индекс Market Value» — бренд-термин оценочной
  модели площадки; подпись — «оценочная модель» + sourcePublicLabel.
- public-copy-no-platform-names.test.ts: «IMV», «Information Market Value»
  добавлены в BANNED (гейт 34/34).
2026-09-04 11:27:32 +03:00
9cb5bf3fa3 Merge pull request 'fix(tradein/pdf): клиентский PDF не называет площадки-источники — «Источник N», как в веб-отчёте (#3341)' (#3344) from fix/3341-pdf-public-source-labels into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 14s
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 4m5s
Deploy Trade-In / build-backend (push) Successful in 1m8s
Deploy Trade-In / deploy (push) Successful in 7m47s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 13s
2026-09-03 09:04:51 +00:00
bot-backend
f2f9ac941b fix(tradein/pdf): клиентский PDF не называет площадки-источники — «Источник N», как в веб-отчёте (#3341)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 11s
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 5m7s
Веб-отчёт и лендинг с #3342 не показывают названия площадок (канон publicLabel в
frontend/src/lib/source-registry.ts), а клиентский PDF по тому же /estimate/{id}
печатал Avito / Циан / «Домклик · Сбер» / Я.Недвижимость / Этажи в пилюлях
источников, «подтверждают Росреестр, ДомКлик…» в советах и «на Циан, Авито,
Я.Недвижимости» в тарифах. Один клиент — два документа с разной нормой, и юр-риск,
ради которого всё делалось, в PDF оставался открытым.

- `_SOURCE_DISPLAY_NAMES` → публичные лейблы 1:1 с реестром фронта: avito → «Источник 1»,
  cian → 2, yandex → 3, domklik → 4, etazhi → 5, rosreestr → «Росреестр»; fallback для
  незнакомого id — «Другой источник», а не `source.title()` (сырой id — та же утечка).
- Алиасы (avito_imv, cian_valuation, yandex_valuation, domclick, etagi) канонизируются
  ДО выбора цвета и лейбла (`_canonical_source`), а списки источников на страницах
  объявлений и сделок дедуплицируются до среза [:5] (`_public_sources`): иначе
  `sources_used` = listing ∪ valuation давал «Источник 1, Источник 1, Источник 2,
  Источник 2, Источник 4» с серой точкой у алиасов и вытеснял yandex.
- Цвета пилюль не тронуты: цвет — опознаватель источника, как на вебе.
- Тексты: «Росреестр, сделки площадок и продажи агентств», «на основных площадках
  объявлений» (двойник offer-rates.ts).
- Гейт `tests/test_pdf_public_source_labels.py`: видимый текст страниц (без тегов и
  атрибутов, href на домены площадок законны) не содержит названий площадок и сырых
  id, по одному кейсу на имя; дедуп алиасов; ветка совета с процентом.

Не тронуто: ссылки на объявления (avito.ru/domclick.ru) — отдельное решение;
`_QUALITY_SOURCE_SLOTS` — только счётчик, имена не рендерит.
2026-09-03 11:58:45 +03:00
34e84ed0ed Merge pull request 'fix(mera-public): публичная копия — сервис не называет себя оценщиком, площадки-источники не названы' (#3342) from fix/mera-public-copy-legal-deplatforming into main
Some checks failed
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 2m12s
Deploy Trade-In / test (push) Failing after 4m8s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / perimeter-smoke (push) Has been skipped
Deploy Trade-In / deploy (push) Has been skipped
Deploy Trade-In / deploy-status (push) Failing after 1s
2026-09-03 06:57:11 +00:00
bot-backend
5541aa9d37 fix(mera-public): публичная копия — сервис не называет себя оценщиком, площадки-источники не названы
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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 1m7s
CI Trade-In / backend-tests (pull_request) Successful in 5m2s
Три юр-правки по документу владельца продукта «Сайт_МЕРА_v2» (31.08.2026), сделаны
локально 31.08, но не были закоммичены — на meraocenka.ru всё оставалось по-старому.

1. «Оценщик» про собственный алгоритм. Витрина сделок (`landing_showcase_deals.py`,
   REJECTION_RULE/NOTE) и сноска статьи «Как оценить квартиру» теперь говорят
   «расчёт МЕРЫ»: самоназвание обесценивало дисклеймер «не официальный отчёт оценщика».
   Комментарии и докстринги не тронуты — посетитель их не видит.
2. «Путь 2»: «стоимость услуг фиксированная и известна заранее» — читалось как
   фиксированная цена квартиры.
3. Названия площадок убраны из видимой копии лендинга и веб-отчёта. Канон —
   `publicLabel` + `sourcePublicLabel()` в `source-registry.ts`: одна площадка = один
   номер «Источник N» и один цвет точки (цвет остаётся опознавателем между блоками),
   Росреестр под своим именем, неизвестный id → «Другой источник» (раньше fallback
   отдавал сырой id). Три параллельные реализации `sourceLabel` сведены к одной;
   `sourceLabel()` с реальными именами живёт для админки. Backend: якорь
   confidence_explanation «по оценке Avito IMV» → «по оценочной модели площадки».
   Подсказки геокодера «Yandex / Nominatim» и «по Яндексу» сняты из клиентских форм.

Гейт `public-copy-no-platform-names.test.ts` сканирует mera-public/** и
components/trade-in/** без комментариев, по Unicode-границе слова; список запретов
регистрозависимый намеренно (строчные id `"avito"` законны), поэтому капс-варианты
и словоформы перечислены явно — «6202 ОБЪЯВЛЕНИЯ ДОМКЛИК» в статье именно так
проходил первую версию гейта.

Не закрыто здесь: клиентский PDF (#3341) и ссылки на объявления на доменах площадок.

Текст витрины хранится в БД (`landing_showcase_runs.rejection_rule`,
`landing_showcase_deals.note`), планировщика у пересчёта нет — после деплоя нужен
ручной пересчёт или UPDATE трёх подстрок на проде.
2026-09-03 09:50:41 +03:00
6883a46ecd Merge pull request 'fix(caddy): периметр МЕРЫ — секрет убран из анонимного пути, чанки закрытого контура не раздаются с meraocenka' (#3340) from fix/3324-mera-perimeter into main
Some checks failed
Deploy Infra Host / sync-infra-host (push) Successful in 7s
Deploy / changes (push) Successful in 11s
perimeter-smoke-mera / smoke (push) Failing after 15s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-frontend (push) Successful in 48s
Deploy / build-backend (push) Successful in 50s
Deploy / build-worker (push) Successful in 51s
Deploy / deploy (push) Successful in 1m17s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 13s
2026-09-02 12:48:51 +00:00
5d9ff13dae fix(caddy): исключить _not-found из фильтра чанков закрытого контура
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 16s
CI Trade-In / backend-tests (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 / openapi-codegen-check (pull_request) Has been skipped
Ревью #3340: `chunks/app/_not-found/` — внутренняя страница Next, а не
маршрут продукта; фильтр её глушил, и клиентская навигация по битому href
падала в error-boundary вместо честного «не найдено». Закрытого кода она
не несёт. Заодно в комментарии матчера явно зафиксировано, что
`static/css/app/<маршрут>/*` оставлен без фильтра сознательно — тот же
класс «имена, а не код», потолок уже задекларирован.
2026-09-02 17:47:28 +05:00
72a17922ee Merge pull request 'fix(mera-public): честный текст покрытия и разные квартиры в раундах игры' (#3337) from fix/3323-coverage-copy-honesty into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 15s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m46s
Deploy Trade-In / deploy (push) Successful in 1m12s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-02 12:46:05 +00:00
ef166eaf21 fix(mera-public): защиты вместо тавтологий в coverage-тексте и выборе раундов
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m9s
Проверка «уверенные + средние + пустые = 100» была верна по определению
средней доли и упасть не могла. Инвариант перенесён на сам замер: уверенные и
пустые обязаны оставлять место третьей доле. Если её всё-таки не останется
(перезамер 85 + 15), текст теперь двухчастный — «Ещё в 0 данных меньше»
отрендериться не может.

`street_name !== null` считал пустую строку названием улицы, и вторая такая
строка вылетала из игры как «повтор». Проверка на пустоту.
2026-09-02 17:43:56 +05:00
177207223b fix(caddy): снять мёртвый internal-секрет с анонимного пути и чанки закрытого контура с публичного домена
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (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 / openapi-codegen-check (pull_request) Has been skipped
#3324, п.3: Caddy на gendsgn.ru инжектил X-Internal-Auth-Secret в каждый
анонимный запрос /trade-in/api/* и /trade-in/* (хоп во фронт). Сравнение
недостижимо по построению: backend читает этот заголовок только в
legacy trusted-header ветке rbac.py, и только после непустого
X-Authenticated-User, который тот же самый хоп строкой ниже удаляет.
Второй потребитель секрета (вебхук GlitchTip) берёт его из query-параметра.
Защита от подделки заголовков внутри gendesign_shared — проверка в rbac.py,
она не тронута; инжект работал в обратную сторону, раздавая секрет наружу.

#3324, п.1: meraocenka.ru раздавал анонимам постраничные чанки App Router
чужих маршрутов (chunks/app/{admin,team,scrapers,...}/) — код закрытого
контура с именами внутренних ручек. Матчер отсекает 404-ом «есть подкаталог,
и он не mera-public»; лэндингу нужны только chunks/app/<файл>.js и
chunks/app/mera-public/* (замер на проде). Карта чанков в webpack-*.js
остаётся — имена файлов по-прежнему читаются, полное разделение = split build.

Регресс-тесты в smoke-mera-perimeter.sh отличают 404 Caddy (пустое тело) от
404 Next и отдельно проверяют, что статика лэндинга жива.
2026-09-02 17:41:38 +05:00
c093212eae fix(mera-public): честный текст покрытия и разные квартиры в раундах игры
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m8s
Остаток «100 − уверенные» в подписи города был однородным только на словах:
внутрь него входят пустые пробы, на которые describeCoverage отвечает «рядом
не продаётся ничего похожего» и расчёт не предлагает. Текст обещал расчёт и
на них — две наши же строки спорили друг с другом. Остаток разложен на долю
«посчитаем, но разброс шире» и долю «считать не станем».

Возраст объявления бэкенд считает percentile_cont(0.5) — это медиана, а
«в среднем» под скошенным распределением называет другую величину.

Раунды игры брались первыми тремя строками витрины, а она отсортирована по
полноте и свежести: на проде это давало три студии с одной улицы. Выбор
жадный по комнатности и улице, детерминированный — страница серверная.
2026-09-02 17:37:13 +05:00
d8b7de2cf6 Merge pull request 'fix(deploy): гейт деплоя ПТИЦЫ — мёртвый worker/beat/frontend больше не уезжает зелёным' (#3334) from fix/3324-ptica-deploy-gate into main
All checks were successful
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 42s
Deploy / build-worker (push) Successful in 43s
Deploy Infra Host / sync-infra-host (push) Successful in 6s
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Successful in 41s
Deploy / deploy (push) Successful in 1m12s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
2026-09-02 12:17:32 +00:00
1b1977efa3 fix(deploy): правки ревью гейта ПТИЦЫ — таймаут сессии, crash-loop, множественный id (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
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) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m35s
command_timeout: 30m — дефолт appleboy/ssh-action 10m короче worst-case гейта
(~17 мин), сессию убило бы посреди диагноза и авария читалась бы обрывом связи.

Деградация «нет health-конфига» требовала лишь running дважды: crash-loop с
временем жизни больше паузы проходил как стабильный (обе проверки видят running,
просто это разные жизни контейнера). Теперь сверяется RestartCount до/после окна
15 с; окно учитывается в счётчике ожидания, иначе таймаут 240 с растянулся бы на
~24 мин и упёрся в command_timeout.

cid(): `ps -aq` возвращает несколько id при залежавшемся exited-контейнере →
docker inspect падает → пустой статус → ложный красный. tail -n1.

worker healthcheck: убран `2>&1` (глушил причину, которую деплой печатает из
.State.Health.Log), retries 3→5 — при interval 60s тройка промахов = 3 минуты,
столько длится обычный флап Redis, а из unhealthy контейнер сам не выходит.
2026-09-02 16:58:40 +05:00
13c4420d1f fix(deploy): гейт ПТИЦЫ читает health worker/beat/frontend, а не только curl backend (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m40s
Деплой ПТИЦЫ проверял ровно один признак — `curl backend /health`. Worker и beat
не имели healthcheck'а в compose вообще, у frontend была TCP-only проба, которую
деплой не читал. Crash-loop воркера, вставший beat и фронт с 500 уезжали зелёным
деплоем: признак «прод жив» отсутствовал в старом состоянии ровно так же, как в
новом.

compose: worker — `celery inspect ping -d celery@$(hostname)` (адресно в ЭТОТ узел,
без -d ответил бы любой воркер на брокере); beat — свежесть shelve-файла
расписания (на inspect ping beat не отвечает; поминутная beat-задача гарантирует
обновление mtime не реже ~3 мин при sync_every=180 с, порог 10 мин = 3× запас).
Фиктивной `true`-пробы нет: она повторяла бы State.Running.

deploy.yml: после подъёма — ожидание healthy для backend/worker/beat/frontend
через docker inspect, HTTP-статус фронта (TCP мало), сверка running-образа с
локально скачанным $IMAGE_TAG (приём #2679 из deploy-tradein). Каждая проверка
при провале печатает контейнер, статус, healthcheck-лог и хвост логов; итог —
явный rc в логе и exit им же. Порядок «миграции до подъёма кода» не тронут,
`up -d --wait` не используется намеренно (подъём разбит на несколько up).
2026-09-02 16:48:50 +05:00
67c1ba043d Merge pull request 'fix(site_finder): Россети — стабильный ключ вместо сессионного fid, дедуп power_supply_centers ×10 → ~481' (#3329) from fix/3322-rosseti-stable-key into main
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 4m53s
Deploy / build-backend (push) Successful in 5m57s
Deploy / deploy (push) Successful in 1m42s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
2026-09-02 10:10:29 +00:00
5cdf2bd74c Merge pull request 'fix(tradein-v2): пустая выборка сделок больше не рисуется нулями — «0,00 млн · −100%» зелёным исчезает' (#3328) from fix/3320-v2-honest-zeros into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 16s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m20s
Deploy Trade-In / deploy (push) Successful in 1m1s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-02 10:00:48 +00:00
41f21c4969 docs(scraper-kit): обоснование #3319 — что именно теряло чекпоинт
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 13s
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 5m13s
Ревью опровергло формулировку «точку терял каждый финализатор»: все четыре
писателя в runs.py мержат jsonb (`counters || :counters`), записанный ключ
переживал mark_done/mark_banned/mark_failed. Правка закрывает выходы РАНЬШЕ
первого end-of-anchor heartbeat (cancel/дрейн на первом якоре, ранний done
#1950 на якоре №1) — комментарий и докстринг переписаны на это.

Замер «0 из 67 за 60 дней» назван тем, чем он является: запись появилась
26.08.2026 (#3074) при такте avito 7 суток, выборка почти вся из эры без
механизма. Плюс тест на прогон без ключей (эра до #3074) — метка дрейна не
меняет вердикт «нечего подхватывать».
2026-09-02 14:53:02 +05:00
6a2d873c70 fix(rosseti): считать координату ключа в SQL тем же double, что в питоне
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m24s
CI / backend-tests (pull_request) Successful in 17m41s
Ревью PR #3329. round(ST_X(geom)::numeric * 100000) округляет по кратчайшему
десятичному представлению float8, а питон — по двоичному double: расхождение на
0.19% реальных координат (761 из 400000), напр. 64.423605 → питон 6442360
(6442360.499999999), numeric-путь 6442361. Каждое расхождение = вечный дубль ЦП,
который сам не зарастёт — миграция применяется один раз (_schema_migrations).
Теперь в SQL sign/floor/abs над float8 без каста в numeric: IEEE754 бит в бит
как math.floor в питоне.

test_coord_e5 брал 60.123455, где двоичное и десятичное округление совпадают —
защита, которая не защищает. Добавлено расходящееся значение 64.423605.

RAISE WARNING при rows_after > 700 заменён на RAISE EXCEPTION: warning не
останавливает прогон, файл помечался бы applied навсегда вместе с дублями.

Refs #3322
2026-09-02 14:51:47 +05:00
35db6608ee fix(tradein-v2): футер 04, падеж сделок и цвет дельты по знаку
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
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 1m31s
Ревью PR #3328:
- HistoryView: «Показано N из M сделок» брало M из street-deals, а строки —
  из sales-vs-listings; после гашения пустого коридора выходило «из —».
  Знаменатель убран, как в 05 РЫНОК;
- обе подписи склоняются через pluralRu («3 фактические сделки», «2 сделки»);
- цвет дельты медианы сделок считается по знаку (mapSources.deltaColor):
  зелёным красились и минус, и прочерк — это второй причинный узел жалобы
  «−100% зелёным»;
- подпись полосы совпала с карточкой 1 буквально («ПО СДЕЛКАМ РОСРЕЕСТРА»);
- тест: убран тавтологичный assert про EmptyTableNote (её рисует пустой
  dealRows, а не guard), добавлены кейсы на футер 04 и на цвет дельты.
2026-09-02 14:50:47 +05:00
24f88988f7 Merge pull request 'fix(mera-public): этаж и состояние переживают переход на /estimate' (#3327) from fix/3321-draft-fields-survive into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 15s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m15s
Deploy Trade-In / deploy (push) Successful in 1m5s
Deploy Trade-In / perimeter-smoke (push) Successful in 18s
Deploy Trade-In / deploy-status (push) Successful in 2s
2026-09-02 09:48:51 +00:00
390a6f05b1 fix(mera-public): сверка адреса у хвоста черновика — в коде, не в комментарии
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
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 1m23s
readDraftExtras требует адрес, под который спрашивают, и на несовпадении
отдаёт null. Гарантия «потребитель обязан сверить» жила только в комментарии,
а takeDraft выходит раньше записи хвоста на пустом и на битом черновике — то
есть хвост от предыдущей квартиры доживал до следующего захода в той же
вкладке и дождался бы потребителя, который сверить забыл.

Плюс два непокрытых кейса: черновик без floor/condition убирает прошлый хвост;
хвост не отдаётся чужому адресу.
2026-09-02 14:46:10 +05:00
0ed934ea09 fix(scraper-kit): чекпоинт avito_city_sweep доживает до финализатора (#3319)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
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 5m30s
0 из 67 прогонов за 60 дней имели done_buckets в counters: точку писала одна
строка внутри цикла якорей, а каждый выход (mark_done — включая ранний #1950
«SERP собран, detail заблокирован», — mark_banned, mark_failed) отдавал голый
counters.to_dict(). Точка держалась только на jsonb-мерже в runs.py, то есть на
свойстве чужого модуля, которого этот файл не проверяет.

- payload любого выхода собирается одной функцией _ckpt() — done_buckets несут
  все 14 записей, а не одна;
- якорь, умерший по таймауту, больше не считается пройденным (тот же инвариант,
  что у generic-except): SERP мог успеть, detail нет, и резюм пропускал такой
  якорь навсегда при штатно завершившемся прогоне;
- SIGTERM-дрейн помечается counters.interrupted=1 и участвует в резюме. Статус
  остаётся 'done' — ни один читатель статуса не меняется; метка та же, что у
  rosreestr_dkp-дрейна. 'done' в _RESUME_STATUSES НЕ добавлен: чистый полный
  обход резюмить нечего. Дрейн перед IMV-фазой помечен отдельно
  (imv_phase_drained) — якоря там пройдены все, подхват собрал бы ноль.
2026-09-02 14:44:38 +05:00
b35f444d50 fix(rosseti): стабильный external_id ЦП вместо сессионного fid GeoServer
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m22s
CI / backend-tests (pull_request) Successful in 17m46s
feature['id'] в WFS-ответе — сессионный fid, новый на каждый GetFeature.
ON CONFLICT (source, external_id) не срабатывал ни разу, каждый weekly-прогон
дописывал полный комплект ~488 фич: 4880 строк на 481 ЦП. Задуманный sha1-фолбэк
по атрибутам был мёртв — fid присутствует всегда.

Ключ теперь считается ТОЛЬКО по стабильным атрибутам (нормализованное имя, класс
напряжения, координаты в 1e-5 градуса), fid игнорируется. Координата квантуется
в целое, а не форматируется как float: ключ обязан совпадать байт-в-байт с
бэкфиллом в SQL. sha256 вместо sha1 — встроен в PG16, pgcrypto не нужен.

Починка разбора старые строки не убирает (ключи не совпадут, ON CONFLICT ничего
не перезапишет) → data/sql/99c_power_supply_centers_dedup.sql: пересчёт ключа
существующих строк + схлопывание копий (победитель — свежайший snapshot,
NULLS LAST явно), с печатью чисел до/после и идемпотентностью.

Refs #3322
2026-09-02 14:43:58 +05:00
08cabe8d98 fix(tradein-v2): пустая выборка сделок больше не рисуется нулями
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m0s
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Бэкенд по контракту не умеет отдать null в street-deals: «сделок на улице
нет» приезжает как count=0 с нулевыми ценами. Витрина сохраняла этот ноль
через ?? и показывала его как данные — «0,00 млн ₽ · −100% к цене
объявления» зелёным и диапазон «0,00 – 0,00».

- usableStreetDeals() схлопывает пустую оболочку в null на границе мапперов
  (mapSources / mapSummary / mapHistory / mapResultPanel) — дальше null уже
  везде означает «нет данных», отдельного состояния заводить не нужно;
- футер таблицы сделок брал N и M из РАЗНЫХ выборок (строки — actual_deals
  оценки, знаменатель — коридор по улице), выходило «Показано 10 из 3»;
  тотала у той же выборки в ответе нет, поэтому знаменатель убран;
- в deals-only ветке (n_analogs=0) полоса диапазона строилась из ДКП-цен,
  но была подписана «В ОБЪЯВЛЕНИЯХ» — подпись согласована с карточкой 1.

Closes #3320
2026-09-02 14:42:49 +05:00
7b61ecb122 fix(mera-public): этаж и состояние переживают переход на /estimate
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
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 / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
Карточка лэндинга требовала этаж и состояние обязательными и клала их в
черновик. Целевая страница на монтировании звала takeDraft, а та стирала
черновик целиком, прочитав только адрес, комнаты, площадь и город: оба
обязательных поля уничтожались непрочитанными. Человек заполнял два поля,
единственным эффектом которых было их же удаление.

takeDraft по-прежнему забирает черновик «на вынос» (адрес не должен
подставляться на следующей неделе в той же вкладке), но перекладывает
непрочитанные floor/condition в отдельный ключ вместе с адресом, к которому
они относятся, — чтобы платный шаг (#2896) не приклеил этаж одной квартиры к
другой. Потребителя у полей пока нет: платного шага нет.

Closes #3321
2026-09-02 14:40:36 +05:00
1c533c8a98 Merge pull request 'v2: бейдж «Мои отчёты» показывает «использовано / доступно» — остаток квоты был не виден нигде' (#3326) from fix/v2-quota-remaining-visible into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Successful in 2m5s
Deploy Trade-In / deploy (push) Successful in 57s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
2026-09-02 07:28:24 +00:00
ee17935d39 Merge pull request 'auth: тестовый аккаунт buyer1 (pilot) для потенциального покупателя' (#3325) from feat/buyer1-trial-account into main
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / deploy-status (push) Successful in 3s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy / build-backend (push) Has been skipped
Deploy / deploy (push) Successful in 1m7s
2026-09-02 07:25:29 +00:00
012d98205f v2: бейдж «Мои отчёты» показывает квоту «использовано / доступно»
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 59s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Владелец выдал покупателю 50 оценок и не смог найти, где виден остаток.
Ответ был: нигде. Бэкенд отдаёт limit/used/remaining/unlimited целиком, а
фронт рендерил голое used с фолбэком на длину истории — «0» у свежего
аккаунта читался как «отчётов нет», а был месячным счётчиком (#3320 п.4:
одна цифра с двумя смыслами).

Теперь: «3 / 50» + title с расшифровкой. Фолбэк на history.length убран —
lifetime-число с потолком 50 строк смыслово другое; нет квоты (unlimited
или не загрузилась) — нет бейджа: лучше ничего, чем не то.

Тест по значению, с открытием меню; фальсификация: возврат голого used
красит «Unable to find 3 / 50». 204 теста (v2 + mera-public) зелёные.
2026-09-02 12:25:14 +05:00
ab3cb4fcb3 auth: тестовый аккаунт buyer1 (pilot) для потенциального покупателя
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m32s
CI Trade-In / backend-tests (pull_request) Successful in 5m13s
CI / backend-tests (pull_request) Successful in 17m21s
По просьбе владельца 02.09.2026. YAML-роль pilot (доступ только /trade-in/**),
DB-роль manager — по образцу praktika/kopylov: самостоятельный внешний аккаунт.
Квота 50 оценок/мес выдаётся через account_quota_overrides.monthly_limit
(дефолт 15), НЕ unlimited.

Строка в roles.yaml обязательна не только для RouteGuard: без неё session-юзер
получает 403 на чтение СВОЕЙ оценки (get_role → KeyError, см. #3316).
2026-09-02 12:05:50 +05:00
4fe0538ea0 Merge pull request 'imv: HTTP 429 больше не приговаривает дом навечно' (#3314) from fix/imv-429-transient into main
All checks were successful
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m2s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 2m7s
2026-09-01 16:27:54 +00:00
dab2b43efc imv: HTTP 429 больше не приговаривает дом навечно
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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 4m50s
Алерты 01.09 (house_imv: RuntimeError('HTTP 429') / ('HTTP 400')) вскрыли
дыру в классификаторе: _raise_for_status_categorized разбирает 401/403 и 5xx,
а ВЕСЬ остальной 4xx проваливается в resp.raise_for_status() и приезжает в
house_imv_backfill голым RuntimeError. Бэкфилл типизирует только
IMV*-исключения — дом получает ТЕРМИНАЛЬНЫЙ imv_status='error' и выпадает из
повторных пакетов навсегда.

429 — канонический ВРЕМЕННЫЙ отказ (rate limit), и механика повтора для таких
существует (transient_error + retry-lane + лимит попыток #2674). Замер на
проде: 19 домов заперты в error с причиной «HTTP 429» — ретраебельный отказ
стал вечным приговором.

429 и 408 теперь IMVTransientError. 400 НАМЕРЕННО оставлен терминальным: тело
безликое {"code":400,"message":"Bad Request"}, оснований считать его
временным нет, а ретраебельный 400 значил бы вечно долбить дома с реально
кривыми параметрами. Тест держит границу С ОБЕИХ СТОРОН — и «429 transient»,
и «400 НЕ transient».

Фальсификация: снятие ветки 408/429 даёт 2 failed по значению
(IMVTransientError не поднят), не ImportError. 9 passed, ruff чисто.

Ремонт уже запертых строк — отдельным шагом после мержа: UPDATE 19 домов
error→transient_error (починка разбора не чинит строки сама).
2026-09-01 21:21:53 +05:00
add974db65 feat(tradein/avito): бан площадки не менял IP — прогон добивался в тот же забаненный адрес (#3283) (#3313)
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 4m10s
Deploy Trade-In / build-backend (push) Successful in 1m7s
Deploy Trade-In / deploy (push) Successful in 7m47s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 13s
2026-09-01 08:05:48 +00:00
7daeaa4b59 feat(tradein/browser): имитация чтения страницы человеком, целиком opt-in (#3283) (#3308)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m51s
Deploy Trade-In / deploy (push) Successful in 1m35s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-09-01 07:34:49 +00:00
879fe57e35 Merge pull request 'fix(tradein/support): один повтор терял каждое одиннадцатое сообщение в поддержку' (#3309) from fix/tgsupport-retry-budget into main
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 4m0s
Deploy Trade-In / build-backend (push) Successful in 3m50s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / deploy (push) Successful in 7m5s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-01 07:00:20 +00:00
bot-backend
d708f15019 fix(tradein/support): один повтор терял каждое одиннадцатое сообщение в поддержку
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 11s
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 4m50s
Замер прода 01.09.2026 из контейнера бота: канал до api.telegram.org рвётся
всплесками, доля отказов на попытку 15-38% (пять проб: 3/8, 15/40, 5/20, 3/20,
1/25), в логе long-polling'а 353 ConnectTimeout за сутки. Транспорт ни при чём —
httpx и сырой сокет отваливаются одинаково (25% против 35% в чередующемся
замере), и прокси не помогает, а мешает: через SCRAPER_PROXY_URL 0 из 20.

Ручка веб-поддержки ходила с max_retries=1, то есть двумя попытками. При 30%
отказов на попытку до пользователя доходило ~9% отказов — каждое одиннадцатое
сообщение возвращало 502 «сервис недоступен».

Два других числа из того же замера задают конструкцию. Успешный запрос отвечает
за 0.13с (максимум из 25 проб — 0.18с), а неудачный НИКОГДА не отваливается
быстро: все отказы упираются в таймаут целиком (10.02с при timeout=10.0). Значит
десятисекундный таймаут не покупал ничего, кроме цены за неудачу, — снижен до 5с,
это ~28-кратный запас к измеренному максимуму. И экспоненциальная пауза 2→4→8с
здесь бессмысленна: отказ — неустановленное соединение, а не троттлинг, пережидать
нечего; она лишь добавляла 14с к ожиданию.

Правка: бюджет ручки — 3 повтора, таймаут 5с, потолок паузы 1с. Худший случай
4 попытки × 5с + 3 паузы × 1с = 23с и требует четырёх отказов подряд; типичный
случай не меняется (0.13с). Расчётная потеря падает с ~9% до ~0.8%.

В TelegramClient добавлен необязательный max_backoff. Воркерная политика НЕ
меняется: без явного потолка откат прежний экспоненциальный до 30с, а retry_after
из 429 уважается целиком — эту границу держит отдельный тест, потому что первая
версия правки её сломала (капала 60с до 30с и для воркера тоже). Потолок на
retry_after применяется только когда его передали явно: интерактивному пути
нельзя ждать Telegram-овские 30-60с, за ним стоит открытый запрос от браузера.

Тесты: 8 новых (потолок на network/429/5xx, неизменность воркерного пути,
арифметика «max_retries=N → N+1 попыток», границы бюджета ручки).
2026-09-01 09:53:21 +03:00
9cbdc5dfbe fix(tradein/browser): снятый PoW-челлендж отдавался с протухшим статусом, и карточка выбрасывалась (#3283) (#3306)
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) Successful in 2m10s
Deploy Trade-In / test (push) Successful in 4m5s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy (push) Successful in 8m32s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-08-31 14:54:00 +00:00
4c29b59041 fix(tradein/proxy): успешный fetch затирал exit_ip и latency_ms в NULL (#3283) (#3305)
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
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) Has been cancelled
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-08-31 14:53:33 +00:00
72cb410b05 fix(tradein/avito): бан-страница площадки уезжала в транспортные сбои и не банила узел (#3283) (#3304)
Some checks are pending
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 / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 13s
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-08-31 14:53:17 +00:00
9219ab6507 feat(tradein/avito): ротация exit-IP по счётчику попыток в доборе карточек (#3302)
All checks were successful
Deploy Trade-In / changes (push) Successful in 14s
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 4m8s
Deploy Trade-In / build-backend (push) Successful in 1m56s
Deploy Trade-In / deploy (push) Successful in 1m43s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-31 12:09:03 +00:00
b7c1c4d9d4 fix(tradein/proxy_pool): истёкший прокси больше не выдаётся в аренду (#3303)
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 4m14s
Deploy Trade-In / build-backend (push) Successful in 1m16s
Deploy Trade-In / deploy (push) Successful in 7m32s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-08-31 11:54:54 +00:00
ea8943e00e Merge pull request 'chore(tradein/scheduler): добор карточек Яндекса и Циана шёл раз в сутки и простаивал' (#3301) from chore/cadence-yandex-cian-detail into main
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 4m17s
Deploy Trade-In / build-backend (push) Successful in 1m48s
Deploy Trade-In / deploy (push) Successful in 1m40s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-08-31 11:36:24 +00:00
dbb8ca4ec1 Merge pull request 'fix(tradein/avito): browser-путь добора не смотрел на HTTP-статус — отказ площадки уходил в ошибку парсинга (#3297)' (#3300) from fix/3297-avito-browser-status-check into main
Some checks failed
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
2026-08-31 11:36:19 +00:00
bot-backend
4ddb3c7196 chore(tradein/scheduler): добор карточек Яндекса и Циана шёл раз в сутки и простаивал
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 12s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-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 4m58s
compute_next_run_at держит суточную гранулярность (interval_days, минимум 1),
подчасовой такт делается хуком reschedule_after_minutes как post_claim. У
avito_detail_backfill он есть (180 мин), у yandex_detail_backfill и
cian_detail_backfill не было — отсюда один прогон в сутки.

Цена простоя по замеру прода 31.08:

  Яндекс: 375-450 карточек за прогон, блоков НОЛЬ за неделю, очередь 11110
          → 25 суток при нынешнем такте
  Циан:   блоков ноль, очередь 20501

Такты разные, и это не произвол:

  yandex — 180 мин (8 прогонов/сутки). Ходит через resolve_proxy_url: берёт
           URL узла, но НЕ лизует его, поэтому чужие прогоны не блокирует.
  cian   — 360 мин (4 прогона/сутки). Ходит через BrowserFetcher и ДЕРЖИТ
           lease весь прогон, то есть отнимает узел у Авито и Домклика. Пул
           дефицитен (#2638), поэтому осторожнее.

Асимметрия зафиксирована комментарием у обоих хендлеров и в докстринге
миграции — иначе следующий читатель выровняет интервалы и сожжёт пул. Тест
test_cian_default_interval_is_360_minutes_not_180 ассертит именно неравенство,
чтобы выравнивание без замера покраснело.

283_scrape_schedules_cadence_yandex_cian_detail.sql — идемпотентный
UPDATE ... SET default_params = default_params || jsonb, остальные ключи
параметров не трогает.

Значения 180/360 — консервативная отправная точка по аналогии с Авито, а не
найденный оптимум: двигать вниз только по замеру нескольких суток, глядя и на
свипы тоже (тот же довод, что в комментарии у avito_detail_backfill).

Тесты (5): наличие post_claim у обоих, дефолтные интервалы, переопределение
через params. Прогон: 397 passed, 1 skipped, ruff чист.
2026-08-31 14:17:12 +03:00
bot-backend
5c10aee4c2 fix(tradein/avito): browser-путь добора не смотрел на HTTP-статус — отказ площадки уходил в ошибку парсинга (#3297)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m4s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Замер прода 31.08.2026 (24 попытки через 3 узла) поймал три отказа, которые
доезжали до parse_detail_html и падали ValueError("Cannot extract item_id"):
страница 8172 байта, статус 439, title «Доска объявлений от частных лиц и
компаний на Авито». Отказ ПЛОЩАДКИ записывался generic-ошибкой разбора, узел
не ротировался и не банился, брейкер по доле его не видел.

Две независимые дыры, обе в browser-ветке fetch_detail:

1. last_response_status не читался вовсе. Curl-ветка того же файла статус
   проверяет (`if sc in (403, 439) or is_firewall`), browser-ветка смотрела
   только на HTML. Прод ходит именно browser-путём.

2. Маркер витрины-заглушки протух: искали «объявления на сайте авито», а
   фактический title — «доска объявлений от частных лиц и компаний на авито»,
   подстрока в нём не встречается.

Правка:

- новые константы _AVITO_DETAIL_BROWSER_BLOCK_STATUSES = {403, 439} и
  _AVITO_DETAIL_BROWSER_RATELIMIT_STATUS = 429, источник каждого статуса
  назван комментарием;
- проверка стоит ПОСЛЕ _is_detail_not_found (404 остаётся
  AvitoListingGoneError) и ДО parse_detail_html;
- статус None (сайдкар старой версии, goto без статуса) отказом НЕ считается —
  поведение прежнее, фолбэк на html-эвристики;
- 429 разведён с блокирующими статусами и поднимает AvitoRateLimitedError.
  Разница не косметическая: на AvitoBlockedError оркестратор один раз за прогон
  зовёт request_context_reset (#3251) и выбрасывает пройденный QRATOR-PoW. При
  rate-limit контекст цел, сбрасывать его — значит проходить проверку заново с
  того же IP. Зеркалит curl-ветку, где 429 тоже не блок;
- старый title-маркер не удалён, а дополнен снятым вживую: площадка может
  отдавать обе формы.

Тесты (9): каждый статус по отдельности, None-статус не ломает разбор и не
подавляет html-эвристики, 404 побеждает блокирующий статус (порядок проверок),
оба title-маркера опознаются, 429 не является AvitoBlockedError.

Фальсификация: без правки detail.py 5 из 9 новых тестов падают.
Прогон: 397 passed, 1 skipped (-k "avito or cadence or scheduler"), ruff чист.

Замер намеренно жёстче прода (без прогрева сессии и органического перехода из
выдачи), поэтому доля таких отказов в проде из него НЕ следует — её покажет
счётчик после правки.
2026-08-31 14:16:47 +03:00
a256077211 feat(tradein/proxy_rotation): ротация IP через mobileproxy.space (aproxy.site) (#3298)
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 4m11s
Deploy Trade-In / build-backend (push) Successful in 1m5s
Deploy Trade-In / deploy (push) Successful in 7m37s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 13s
2026-08-31 11:01:41 +00:00
c4fcedda9a Merge pull request 'лендинг: типографский ритм и мобильные отступы — подача без новых обещаний' (#3292) from feat/mera-landing-attractiveness into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m4s
Deploy Trade-In / deploy (push) Successful in 59s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-31 09:32:59 +00:00
2274c82b7d Merge branch 'main' into feat/mera-landing-attractiveness
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
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 1m9s
2026-08-31 09:30:58 +00:00
41d0d84075 Merge pull request 'первый экран: оговорки вынесены из блока преимуществ' (#3293) from feat/mera-hero-separate-caveats into main
Some checks failed
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 18s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been cancelled
2026-08-31 09:30:51 +00:00