Commit graph

3499 commits

Author SHA1 Message Date
bot-backend
671fef758e feat(mera): Метрика и GA4 на публичном контуре + открытие сайта для индексации
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / 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 / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m19s
CI / openapi-codegen-check (pull_request) Successful in 2m6s
CI / backend-tests (pull_request) Successful in 17m40s
ЗАЧЕМ. Статьи МЕРЫ публикуются с UTM-метками, но посмотреть, приходил ли по
ним кто-нибудь, было физически нечем: веб-аналитики на публичном контуре не
было вовсе. Заодно вскрылось, что «толкнуть в выдаче» тоже нельзя — всё
дерево mera-public отдавало `robots: noindex, nofollow`.

СЧЁТЧИКИ. Яндекс.Метрика и GA4 подключаются ТОЛЬКО в `mera-public/layout.tsx`
и никогда в корневом `app/layout.tsx` — иначе счётчик уехал бы в закрытый
контур (/v2, /admin, /scrapers, /history), где анонимных посетителей нет, а
приватные маршруты сотрудников есть. Идентификаторы приходят build-time
(`NEXT_PUBLIC_YM_ID` / `NEXT_PUBLIC_GA_ID`) — канон Dockerfile'а этого
проекта: Next инлайнит NEXT_PUBLIC_* на сборке, runtime env их не подхватит.
Пустое значение = тег не рендерится вовсе, никаких `ym(undefined)`. Оба
build-arg'а прописаны в ОБОИХ блоках CI, включая retry-сборку без кеша.

Вебвизор выключен намеренно. Он пишет ввод в поля, а на `/estimate` человек
вводит адрес своей квартиры; раздел 9 политики этого не раскрывает. Включать
следует одним заходом с правкой политики и маскировкой полей — в коде рядом
записано, что именно понадобится.

ЦЕЛИ ВОРОНКИ. Десять целей: клик по CTA, начало ввода адреса, адрес выбран,
результат с разбивкой по вердикту (ok/thin/none), ошибка расчёта, ошибка
валидации, отказ подсказок, показ платного тизера. Кнопок «Проверить
квартиру» восемь штук в разных компонентах, все — обычные `<a>` через
PublicLink, поэтому вместо восьми копий onClick один делегированный
слушатель на document: девятая кнопка подключится сама. Цель «оплата
успешна» НЕ заведена — вызова checkout во фронте нет вовсе, PAYMENTS_ENABLED
выключен, страницы возврата не существует; вешать её пока не на что.

ИНДЕКСАЦИЯ. Снят noindex со всех публичных страниц, добавлены `app/robots.ts`
и `app/mera-public/sitemap.ts`, metadataBase, canonical на КОРОТКИЕ адреса,
openGraph и JSON-LD Article на главной статье. robots.txt и sitemap.xml
разведены по двум разным handle в Caddy не от хорошей жизни: у Next
robots.txt — конвенция корня app/, а sitemap живёт в сегменте маршрута, и
формы путей не совпадают.

152-ФЗ. Раздел 9 «Файлы cookie и веб-аналитика» в политике (обработчики
названы поимённо — этого требует ч. 3 ст. 6) + уведомляющий, не блокирующий
баннер. Гейт «названий площадок в публичной копии быть не должно» получил
узкое исключение ровно на аналитические словосочетания в политике; голое
«Яндекс» как площадка остаётся запрещённым и там.

ПОПУТНЫЙ БАГ (замер на живом проде 10.09.2026). `meraocenka.ru/articles/`
с UTM-метками отдавал 301 на адрес БЕЗ query — матчер @meraShortSlash
собирал цель из regex-захвата пути и терял параметры. Код ответа при этом
оставался 301, поэтому смоук проблему не видел. Мессенджеры и
автолинкификаторы дописывают слэш сами, то есть атрибуция терялась именно на
трафике по опубликованной ссылке. Починено тем же приёмом, что у соседних
матчеров; в смоук добавлена проверка буквального Location.

ГЕЙТЫ. `isPublicPath` и периметр-тест узнали про новые машинные адреса;
noindex-гейт развёрнут (падает, если флаг вернулся) и расширен на строковую
форму `robots: "noindex"`; заведена проверка, что корневых `handle` в
site-блоке не появляется без объявления — раньше эту дверь гейт не видел.

Проверено: tsc и eslint чисто, `npm run build` проходит, robots.txt и
sitemap.xml отдаются по нужным адресам, при пустых ID в HTML нет ни одного
обращения к mc.yandex.ru и googletagmanager, `caddy validate` валиден,
изоляция mera-public от B2B не нарушена. Два теста LoginPage падают и на
нетронутом дереве — не наши.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ
2026-09-10 16:52:31 +03:00
455766dcc4 Merge pull request 'ЕЭСК: бинд-параметр в SQL-комментарии молча ронял все 71 UPDATE две недели' (#3317) from fix/eesk-bind-in-comment into main
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 1m53s
Deploy / build-worker (push) Successful in 2m57s
Deploy / deploy (push) Successful in 1m18s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
2026-09-09 22:05:38 +00:00
60bb7cf54e Merge pull request '[SECURITY] Caddy: голый IP по HTTP спрашивал пароль пилота открытым текстом' (#3318) from fix/caddy-bare-ip-plaintext-auth into main
All checks were successful
Deploy Infra Host / sync-infra-host (push) Successful in 7s
Deploy / changes (push) Successful in 10s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 42s
Deploy / build-backend (push) Successful in 50s
Deploy / build-frontend (push) Successful in 4m33s
Deploy / deploy (push) Successful in 1m13s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
2026-09-09 21:49:09 +00:00
ff9ee721a7 Merge branch 'main' into fix/caddy-bare-ip-plaintext-auth
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
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 1m18s
2026-09-09 21:47:08 +00:00
6eb1441508 Merge branch 'main' into fix/eesk-bind-in-comment
All checks were successful
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
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 17m40s
2026-09-09 21:47:07 +00:00
e5f31d4dc7 Merge pull request 'fix(msk-collector): ретрай заливки батча на обрыве ssh' (#3435) from fix/msk-collector-ssh-retry into main 2026-09-09 07:25:16 +00:00
bot-backend
ae70e7a39b fix(msk-collector): ретрай заливки батча на обрыве ssh
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
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 10s
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
Прогон длится часами, и ssh рвётся: живьём поймано Connection reset by peer
(ssh 255) прямо посреди заливки — умирал весь прогон, несброшенный батч
(до 1000 карточек) терялся, resume перечитывал страницы заново.

Ретрай безопасен: SQL идемпотентен целиком — batch через ON CONFLICT DO
NOTHING, карточки через ON CONFLICT (source_id,batch_id,kind) DO NOTHING.
Ретраится ТОЛЬКО транспорт (код 255); ошибка самого psql под ON_ERROR_STOP
поднимается сразу — это дефект данных или SQL, повтор его не лечит.
2026-09-09 10:13:44 +03:00
62b9d1d4a7 Merge pull request 'fix(msk-collector): страница выдачи — 50 карточек, не 60' (#3434) from fix/msk-collector-page-size into main 2026-09-09 07:00:14 +00:00
bot-backend
88f422c07b fix(msk-collector): страница выдачи — 50 карточек, не 60
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
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 / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
PAGE_SIZE=60 был взят из потолка 30x60=1800, а не измерен. Реальная выдача
Москва+МО отдаёт 50 на страницу, поэтому planned_pages = ceil(count/60)
не запрашивал последние ~17% каждого коридора: по закрытым коридорам счётчик
Авито обещал 34 753, собрано 28 352.

Это ровно тот молчаливый недобор, который читается как «покрыто всё»: коридор
помечался done, дойдя до расчётной страницы, хотя выдача не кончилась. С 50
потолок пагинации даёт 1500 на запрос — совпадает с дефолтным --target-count,
так что коридоры бисекции по-прежнему добираются целиком.
2026-09-09 09:48:43 +03:00
3107712893 Merge pull request 'fix(msk-collector): пересоздавать свою вкладку каждые 75 загрузок' (#3433) from fix/msk-collector-tab-recycle into main
Reviewed-on: #3433
2026-09-09 06:45:24 +00:00
bot-backend
68b7c16d9d fix(msk-collector): пересоздавать свою вкладку каждые 75 загрузок
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 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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Рендерер Chrome копит память по всем навигациям вкладки, а московский проход —
под тысячу страниц в одной. Живьём поймано: вкладка падает с «Опаньки… Код
ошибки: Out of Memory» при 34 ГБ свободных в системе — упирается рендерер, а не
машина. Свежая вкладка стоит одну навигацию и обнуляет счёт.

Закрывается только своя вкладка; контекст и вкладки владельца не трогаются.
2026-09-09 09:44:02 +03:00
6de69c4d05 Merge pull request 'feat(tradein/geocoder): регион-параметризация геокодера — region_code в geocode()/known_city_hint, --region-code у скрипта сделок, region_code у geocode-missing (#3051)' (#3432) from feat/3051-geocoder-region-param into main
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 4m18s
Deploy Trade-In / build-backend (push) Successful in 1m5s
Deploy Trade-In / deploy (push) Successful in 2m11s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 23:58:57 +00:00
bot-backend
9898b6bc02 feat(tradein/geocoder): регион-параметризация геокодера — region_code в geocode()/known_city_hint, --region-code у скрипта сделок, region_code у admin geocode-missing (#3051)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-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 / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m6s
Геокодер был жёстко привязан к Свердловской области: viewbox 66 +
bounded=1, accept только при state ~ 'свердловск' и точке в bbox 66,
known_city_hint знал лишь города области → для 212 937 московских сделок
(address 'Москва, <улица>') геокод давал None либо ложный хит по
одноимённой улице области, а cache-ключ без города смешивал регионы.
Теперь регион приходит от вызывающего (deals.region_code): viewbox и
bbox из REGIONS[code], state-маркер per region ('свердловск'/'москва'),
ЕКБ-тиры (geoportal/cadastral/local houses) только при 66, city-хинт
через словарь региона → cache-ключ '|city=москва'. Дефолт 66 везде —
для существующих вызовов поведение байт-идентично (ревью двумя
линзами). Побочно: geocode-missing по умолчанию больше не берёт
listings с region_code NULL (16 930 неактивных чужих городов, которые
и раньше геокодились впустую).
2026-09-09 02:51:28 +03:00
12476df7a2 Merge pull request 'feat(tradein/estimator): deal_city_price_bands по ключу (region_code, city) — миграция 298, refresh per-region (#3051 sub-PR B)' (#3431) from feat/3051-bands-region-key into main
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 4m14s
Deploy Trade-In / build-backend (push) Successful in 1m4s
Deploy Trade-In / deploy (push) Successful in 1m54s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 23:32:46 +00:00
db7eb00759 Merge pull request 'test(tradein): тесты трека «Москва» — импорт ДКП по 77, чекпоинт per-source, wildcard-хендлер, регион-скоуп коридора, миграция 289, deploy-скрипт (#3051)' (#3430) from test/3051-moscow-track-tests into main
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 11s
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
2026-09-08 23:30:21 +00:00
bot-backend
1d9adb4a24 feat(tradein/estimator): deal_city_price_bands по ключу (region_code, city) — миграция 298, refresh per-region, потребители (#3051 sub-PR B)
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 10s
CI / openapi-codegen-check (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 5m10s
После импорта 212 937 московских ДКП (region_code=77) region_stats
refresh'а (p1 для tier region_fallback) считался бы по пулу 66+77 и
поднял бы floor ~250 малым городам области. Ключ bands становится
(region_code, city): миграция 298 (колонка DEFAULT 66, смена PK через
DO-guard, re-seed per-region, ЕКБ-исключение только для 66, doc_type=ДКП),
тот же SQL в refresh-джобе, estimator/backtest читают bands по паре.
Для 66 набор строк байт-идентичен прежнему (ревью двумя линзами).
2026-09-09 02:25:47 +03:00
bot-backend
beb487c12e test(#3051): тесты трека «Москва» — импорт ДКП по 77, per-source чекпоинт, wildcard-хендлер, регион-скоуп ДКП-коридора, миграция 289, валидация deploy-скрипта
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
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 / 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 5m2s
Тесты пишутся ПОСЛЕ живой проверки функционала (правило от 08.09):
импорт по 77 — run 6460 (212 937 строк), регион-фильтр коридора — smoke
до/после импорта. 14 тестов: SQL-текст через inspect (regex \s+ для
многострочных клозов), чистые функции, мок db по образцу 2846, subprocess
bash с урезанным PATH (падение от валидации, не от отсутствия docker).
2026-09-09 02:23:47 +03:00
e0bef636e6 feat(tradein): bulk-дампы открытых данных ФНС по юрлицам + lookup по ИНН (#3429)
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 4m17s
Deploy Trade-In / build-backend (push) Successful in 1m3s
Deploy Trade-In / deploy (push) Successful in 7m11s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 22:29:10 +00:00
50f0674977 feat(tradein): слой ДТП из dtp-stat.ru в PostGIS + радиусные агрегаты (#3428)
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 4m9s
Deploy Trade-In / build-backend (push) Successful in 1m54s
Deploy Trade-In / deploy (push) Successful in 1m51s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 22:10:56 +00:00
3bd30aab87 Merge pull request 'feat(mera): макро-ряды ЦБ РФ — ипотека по субъектам и ключевая ставка' (#3427) from feat/mera-cbr-macro into main
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 4m13s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 1m32s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 21:48:59 +00:00
bot-backend
1f3566c466 merge main в feat/mera-cbr-macro — оба handler'а (frt_mkd_load + cbr_macro_pull) в реестре
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
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 / 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 5m12s
2026-09-09 00:43:13 +03:00
e7ef6bc4a2 Merge pull request 'feat(mera): загрузчик реестра МКД АИС ФРТ → обогащение houses' (#3426) from feat/mera-frt-mkd-loader into main
Some checks failed
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 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 4m24s
Deploy Trade-In / build-backend (push) Successful in 1m7s
Deploy Trade-In / deploy (push) Has been cancelled
2026-09-08 21:41:24 +00:00
bot-backend
2abab8218e feat(mera): макро-ряды ЦБ РФ — ипотека по субъектам и ключевая ставка
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (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 / backend-tests (pull_request) Successful in 5m16s
Три XLSX ЦБ (выдачи, ставка, задолженность) в разрезе субъектов, помесячно
с 01.2019, + ключевая ставка через SOAP DailyInfo.asmx (метод KeyRate; GET
на нём не работает, только POST). Всё анонимно, без ключа.

Таблицы: cbr_mortgage_series (region, period_month, series) и cbr_key_rate.
fetched_at НЕ обновляется в DO UPDATE — по уроку #2846 колонка значит
«когда мы ВПЕРВЫЕ увидели период» и по ней меряется такт публикации источника.

Раскладка листов у ЦБ РАЗНАЯ, и это ловушка: в 02_11/02_13 шапка периодов в
строке 3 текстом («Январь 2019»), а в 02_14 (задолженность) — уже в строке 2 и
настоящими datetime. С захардкоженным индексом строки серия debt_rub молча
давала НОЛЬ строк при зелёных тестах. Теперь строка шапки ищется динамически:
берётся строка с наибольшим числом распознанных периодов среди первых шести.

Проверено на живых файлах: каждая из трёх серий даёт 8 736 точек
(96 территорий × 91 месяц, 01.2019–07.2026); по Свердловской области 91 месяц,
последняя ставка 11.49.

estimator.py не тронут: как ипотечная ставка войдёт в оценку — отдельное
продуктовое решение, этот PR только про данные.

Миграции 292 (таблицы) и 293 (сид расписания, enabled=false, interval_days 7).
Новая зависимость: openpyxl.
2026-09-09 00:33:12 +03:00
bot-backend
8aca820a46 feat(mera): загрузчик реестра МКД АИС ФРТ → обогащение houses
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
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 / 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 5m14s
Открытые данные АИС ППК «ФРТ» (бывш. Реформа ЖКХ), node 110 = реестр МКД
региона 66: 41 790 строк, houseguid (ФИАС GUID) заполнен на 100% → join к
houses.gar_house_guid без канонизации адреса. Анонимный GET, без ЕСИА.

Заполняет ТОЛЬКО NULL-поля houses: year_built, material_walls, material_floors,
total_floors, entrances, is_emergency, flat_count, heat_supply_type,
gas_supply_type, hot_water + новые area_land, foundation_type, elevators_total.

Замер по ЕКБ: wall_material 92.5% (было 75% от ДОМ.РФ), built_year 92.0%
(было 86%), area_land 86.8% и is_emergency — признаков, которых не давал
ни один из текущих источников.

Осознанно НЕ мапится:
- project_type → series_name: свободный текст и фактически дубль материала стен
  (пусто у 10 635 строк, «кирпичный» 1754, «нет данных» 1496);
- energy_efficiency: решение миграции 284 + реальный класс лишь у ~10% домов
  (у 25 595 из 41 790 значение «Не присвоен»);
- elevators_count → passenger_elevators: в источнике это ОБЩЕЕ число лифтов,
  в houses раздельно пассажирские и грузовые → отдельная колонка;
- playground/sportsground: в источнике id справочника (498/499/500), не флаг.

Дубликаты houseguid реальны и взаимодополняющи: 912 guid'ов, 968 лишних строк,
у одной строки пары заполнен area_total, у парной нет. Строки СЛИВАЮТСЯ по
полям (первое непустое побеждает), иначе бэкфилл терял бы данные ~2% домов.

estimator.py не тронут: аналоги подбираются FROM listings без JOIN к houses,
встраивание признаков в подбор когорты — отдельная задача.

Миграции 290 (staging frt_mkd + колонки houses) и 291 (сид расписания,
enabled=false, interval_days 30). robots.txt источника требует Crawl-delay 10.
2026-09-09 00:33:09 +03:00
1441e189c8 Merge pull request 'fix(msk-collector): ограниченный ретрай goto на таймауте навигации' (#3425) from fix/msk-collector-nav-timeout into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / deploy (push) Successful in 55s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-frontend (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 / perimeter-smoke (push) Successful in 12s
2026-09-08 20:53:34 +00:00
bot-backend
f820c3eac5 fix(msk-collector): ограниченный ретрай goto на таймауте навигации
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / 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 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
Авито изредка держит соединение до упора, goto падает по 90-секундному
таймауту и прогон умирает целиком. В наблюдавшемся случае это был не отказ:
вкладка показывала нормальную выдачу, маркеров фаервола и PoW не было.

Но тихий отказ выглядит так же, поэтому ретрай не слепой: перед каждым
повтором читаем то, что есть в документе, и прогоняем через _guard — реальный
блок останавливает прогон со своей причиной. Исчерпали 3 попытки — жёсткий
стоп с причиной nav_timeout, а не бесконечный повтор.
2026-09-08 23:50:36 +03:00
1e96a6f12d Merge pull request 'fix(msk-collector): узкий ретрай page.content() на гонке перенавигации' (#3424) from fix/msk-collector-nav-race into main
All checks were successful
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 54s
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-08 20:48:18 +00:00
bot-backend
3daf09c7e4 fix(msk-collector): узкий ретрай page.content() на гонке перенавигации
All checks were successful
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 / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Авито дорисовывает выдачу после domcontentloaded, и content() иногда попадает
ровно в смену документа — Playwright бросает "page is navigating and changing
the content". Это не отказ площадки, но прогон падал целиком: на 1110-й
карточке потеряно 100 несброшенных строк.

Ретрай узкий — только на этот текст ошибки и только 4 попытки; любая другая
ошибка поднимается как есть, гварды блокировки не ослаблены. runs/ (планы и
CSV прогонов) в .gitignore.
2026-09-08 23:44:09 +03:00
e2045582ab Merge pull request 'feat(tradein/rosreestr): импорт ДКП по Москве (77) — canonical_city, raw_payload, wildcard-расписание rosreestr_dkp_import_*, per-source чекпоинт (#3051)' (#3422) from feat/3051-rosreestr-import-region-param into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-browser (push) Successful in 39s
Deploy Trade-In / build-frontend (push) Successful in 2m13s
Deploy Trade-In / test (push) Successful in 4m22s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m23s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-08 20:28:20 +00:00
c6321c767d Merge pull request 'feat(tradein/estimator): регион-скоуп ДКП-коридора — фильтр по deals.region_code (#3051 PR-A)' (#3423) from feat/3051-estimator-dkp-region-filter into main
Some checks failed
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 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 4m14s
Deploy Trade-In / build-backend (push) Has been cancelled
2026-09-08 20:23:21 +00:00
bot-backend
fcf5887225 merge(#3051): main (#3421) в ветку импорта по региону — московская дельта поверх region_code/doc_type
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
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 / 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 5m12s
#3421 въехал в main параллельно с той же миграцией 288 (deals.doc_type,
параметры region_code/doc_types). Разрешение: 288 — целиком версия main;
наша дельта (FDW-колонки okato/quarter_cad_number/district, выключенный seed
rosreestr_dkp_import_77) переехала в 289. scheduler.py — doc_types из main +
canonical_city-маппинг/raw_payload/per-source чекпоинт. deploy-скрипт —
валидация REGION_CODE и DOC_TYPE (интерполируются в SQL текстом).
2026-09-08 23:19:47 +03:00
bot-backend
fbe85fcc75 feat(tradein/estimator): регион-скоуп ДКП-коридора — фильтр по d.region_code (#3051 PR-A)
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 9s
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 5m9s
target_city резолвится ТОЛЬКО для городов Свердловской области
(_resolve_target_city матчит SVERDLOVSK_OBLAST_CITIES) — для Москвы/любого
нового региона city=None, и street ILIKE оставался единственным скоупом
сделки: одноимённая улица чужого региона утекала в коридор. Добавлен
d.region_code = CAST(:region_code AS int) в оба ДКП-запроса
(_fetch_dkp_corridor) + region_code передаётся из обоих вызывающих (POST
/estimate через geo.lat/lon, GET-rehydrate через row.lat/lon) с гарантией
«не резолвится → DEFAULT_REGION_CODE (66)», не NULL (NULL в SQL-параметре
обнулил бы фильтр целиком). Дефолт региона и джойн deal_city_price_bands не
трогаются — следующие PR (B, F).

Regression: 5622 passed, 37 skipped (полный прогон tests/).
2026-09-08 23:12:12 +03:00
bot-backend
1f24f12af9 chore(#3051): снять новые тесты до живой проверки; валидация REGION_CODE в deploy-скрипте
All checks were successful
CI Trade-In / 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 / 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 / backend-tests (pull_request) Successful in 5m13s
Тесты трека «Москва» пишутся отдельным заходом после живого прогона импорта по 77 —
правило проекта с 2026-09-08. Правки существующих тестов (bind-параметр вместо
литерала 66, канонический source rosreestr_dkp_import_77) остаются.
REGION_CODE в import-rosreestr.sh подставляется в SQL текстом — допускаем только целое.
2026-09-08 23:05:35 +03:00
541c4ff28d Merge pull request 'feat(#3051): deals.doc_type + регион и тип документа как параметры импорта Росреестра' (#3421) from feat/3051-deals-doc-type-region-param into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 41s
Deploy Trade-In / build-frontend (push) Successful in 2m22s
Deploy Trade-In / test (push) Successful in 4m24s
Deploy Trade-In / build-backend (push) Successful in 1m5s
Deploy Trade-In / deploy (push) Successful in 6m35s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-08 20:04:29 +00:00
76d018b5a0 Merge pull request 'feat(#2989): локальный ручной сборщик SERP Авито по Москве и МО' (#3420) from feat/2989-msk-avito-local-collector 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 / 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 / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-09-08 20:04:25 +00:00
bot-backend
e9370818ea fix(#3051): lock_timeout в миграции 288 — гейт CI #2752
All checks were successful
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
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 Trade-In / backend-tests (pull_request) Successful in 5m11s
ALTER TABLE deals ADD COLUMN без SET LOCAL lock_timeout встаёт в очередь за
чужой сессией и уводит за собой запросы приложения. Лучше упасть по таймауту
и повторить деплой.
2026-09-08 22:58:38 +03:00
bot-backend
84ee8e5990 feat(tradein/rosreestr): параметризовать импорт ДКП по региону, deals.doc_type (#3051)
Трек 2 подготовки Mera к Москве. import_rosreestr_dkp принимает region_code из
params (default 66 — байт-в-байт прежнее поведение), валидирует его через
app.services.regions.REGIONS. Регион с canonical_city (77 — Москва, Росреестр
отдаёт округ/поселение вместо города) подставляет city/address через одну
SQL-ветку на bind-параметре :canonical_city, а не Python if/else на код региона;
city IS NOT NULL не фильтруется для такого региона (иначе теряется ~10% строк),
исходные city/okato/quarter_cad_number/district уходят в raw_payload.

Чекпоинт курсора (_resume_dkp_cursor) стал per-region: source для поиска
предыдущего прогона строится через _dkp_source_for_region (66 сохраняет
легаси-имя 'rosreestr_dkp_import', остальные — суффикс кода) — иначе прогон по
77 либо никогда не резюмился бы (source-литерал не матчил), либо, при более
наивном фиксе, унёс бы курсор чужого региона.

product_handlers регистрирует wildcard rosreestr_dkp_import_* (по образцу
deactivate_stale_*/avito_city_sweep_*), deploy/import-rosreestr.sh получил
REGION_CODE env (bash-путь не region-generic — city-override только в Python).

Migration 288: deals.doc_type + backfill 'ДКП' для source=rosreestr, foreign
table gendesign_rosreestr_deals расширена okato/quarter_cad_number/district
(проверено live на прод-БД), выключенный seed rosreestr_dkp_import_77.
2026-09-08 22:58:26 +03:00
bot-backend
bde38cdce7 fix(msk-collector): --resume берёт base_url из сохранённого плана
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / 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 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
Коридоры считаются под конкретный URL выдачи. При --resume код брал свежий
args.base_url, поэтому запуск без повтора --base-url молча качал другую
выдачу под тем же batch_id. Теперь URL из плана, расхождение с аргументом —
явная ошибка вместо тихого выбора одного из двух.
2026-09-08 22:54:44 +03:00
bot-backend
476fb9b77a chore(#3051): снять новые тесты — до живой проверки функционала тесты не пишем
Some checks failed
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Failing after 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 / 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
В существующем test_rosreestr_dedup_key оставлена только правка, без которой
он падает после параметризации (литералы region_code=66 / doc_type='ДКП'
ушли из SQL живого импорта). Новый тест-файл и добавленная функция сняты:
поведение ещё не проверено на живом импорте, тест зафиксировал бы догадку.
2026-09-08 22:53:20 +03:00
bot-backend
7b33ed9a45 feat(#2989): локальный ручной сборщик SERP Авито по Москве и МО
Трек 1 эпика: нужен разовый корпус вторички Москвы+МО в msk_raw, а прод-скрейпер
для этого не подходит — его расписания, прокси-пул и сайдкар держат ЕКБ и трогать
их ради ручного замера нельзя.

Поэтому скрипт: браузер — уже открытый Chrome владельца с залогиненным техаккаунтом
(connect_over_cdp, своя вкладка, чужие вкладки/контекст/браузер не трогаем и не
закрываем; своего профиля не поднимаем); парсер — импорт _parse_html /
_extract_total_count / _is_firewall_page из scraper-kit, а не копия (копия разъедется
с прод-парсером на первом же DOM-drift); заливка — поток в psql через ssh, потому что
прямого доступа к прод-Postgres с локалки нет, а одиночный psql -c ломается на
квотинге.

avito_serp_ekb_only=False обязателен: с True парсер выбрасывает всё, где в URL нет
/ekaterinburg/ — из московской выдачи не осталось бы ни одной карточки.

Потолок Авито 30x60=1800 на запрос, поэтому план ценовых коридоров с бисекцией по
ГЕОМЕТРИЧЕСКОЙ середине: цены логнормальны, арифметическая середина 1млн..100млн
даёт вырожденно-пустую верхнюю половину. Коридор, который не влезает в 1800 даже
на минимальной ширине, помечается truncated и недобор пишется в batches.notes —
молчаливое усечение читалось бы как полный охват.

Стоп на первом признаке блока (403/439, 429, firewall, PoW, 0 карточек при ненулевом
счётчике) без ретраев: ретрай по забаненному техаккаунту только углубляет бан.
Дефолт — --measure 100, полный проход только по явному --full.
2026-09-08 22:48:37 +03:00
bot-backend
cfe529fd14 feat(#3051): deals.doc_type + регион и тип документа как параметры импорта Росреестра
ПОЧЕМУ: расширение на Москву упирается в два литерала. В источнике за 2024 по региону 77
лежат 30 627 ДДУ с медианой 112 743 против 107 005 ДКП с медианой 256 250 — это цены
котлована, и без различимого признака в deals они развалят любую оценку. При этом тип
сделки терялся при загрузке вовсе (в deals колонки не было), а фильтры region_code = 66
и doc_type = 'ДКП' стояли литералами в scheduler.import_rosreestr_dkp и в двойнике
deploy/import-rosreestr.sh — сменить регион было нельзя, не правя код.

ЧТО:
- миграция 288: deals.doc_type text (idempotent) + бэкфилл 'ДКП' для source='rosreestr'
  (корректен, а не эвристика: всё загруженное прошло фильтр ДКП — и в импорте, и в 077)
  + явный region_code=66 в default_params расписания rosreestr_dkp_import вместо неявного
  дефолта в коде. Индекс НЕ добавлен: 2-3 значения, живые выборки идут по
  region_code/deal_date/geom — заведём частичный, когда появится режущий запрос;
- import_rosreestr_dkp: region_code (default 66) и doc_types (default ['ДКП']) из params,
  фильтры через bind-параметры CAST(:region_code AS int) / ANY(CAST(:doc_types AS text[])),
  doc_type едет из SELECT в INSERT и в ON CONFLICT DO UPDATE. Дефолты сохраняют текущее
  прод-поведение байт-в-байт;
- dedup_hash оставлен как 'ros:dkp:' || id: id уникален в источнике независимо от типа
  документа, а смена формы ключа осиротила бы уже загруженные строки (ровно то, что
  разгребала миграция 077);
- deploy/import-rosreestr.sh: REGION_CODE / DOC_TYPE как env со старыми дефолтами,
  doc_type протащен через staging в deals; шапка про «ЕКБ квартиры» переписана честно —
  city-фильтр снят давно, скоуп = весь регион;
- тесты: test_rosreestr_dedup_key переведён с ассертов на литералы на проверку
  «параметр + дефолт = скоуп 077»; новый test_3051_* проверяет bind-параметры реальным
  вызовом с моком Session, дефолты 66/['ДКП'], doc_type в колонках INSERT и текст 288.
2026-09-08 22:45:55 +03:00
278f8055a4 Merge pull request 'chore(tradein): последние обходы пула прокси — admin cian-login/domclick-debug через фабрику; снят костыль _PoolCurlConfig (#3197, #3386)' (#3409) from chore/backend-pool-wiring-tails into main
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 4m16s
Deploy Trade-In / build-backend (push) Successful in 1m42s
Deploy Trade-In / deploy (push) Successful in 1m53s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-07 11:04:07 +00:00
f4d174283a chore(#3197): cian-login — без холостой аренды и ложного отказа (/login сайдкара override не берёт); честные докстринги
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
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 / 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
Code-review хвоста #3197: на `cian-login` аренда из пула не доходила до сайдкара.
`BrowserFetcher._post_login` не кладёт `payload["proxy"]` (это делают только
`fetch`/`fetch_json`), а на приёме `login_handler` (browser/server.py:2814-2817)
зовёт `_no_live_proxy(provider, None)` и `_ensure_browser(provider)` без override —
`/login` proxy-override не принимает вовсе. Lease брался в `__aenter__` и
освобождался в `__aexit__` без пользы и без health-вердикта по узлу.

Хуже холостого хода: при пустом пуле в production `_acquire_lease` поднимает
`NoProxyAvailableError` ДО POST, `cian_auto_login` ловит любое `Exception` →
`502 Browser login failed`. То есть единственная ручка ВОССТАНОВЛЕНИЯ cian-сессии
отказывала ровно во время инцидента с пулом. Комментарий в admin.py при этом
утверждал, что фикс закрывает InvalidIP на логине — неправда.

Убран `proxy_provider=` (фабрика остаётся ради endpoint/environment из одного
места). `use_pool` без провайдера фетчер игнорирует сам — `_acquire_lease`:
`use_pool AND provider is not None` — поэтому ни аренды, ни прод-отказа.
`domclick-detail-debug` не тронут: он ходит через `fetch`, где override реально
кладётся в тело и читается сайдкаром — там #3197 остаётся настоящим фиксом.

Тесты для cian обратные по значению и падают на HEAD ветки:
`test_cian_auto_login_does_not_lease_from_pool` (провайдер не передан, `acquire`
не вызван) и `test_cian_auto_login_survives_empty_pool_in_production` (пустой пул
на проде не отдаёт 502). Стаб cian — подкласс настоящего `BrowserFetcher`, чтобы
второе утверждение шло через реальный `_acquire_lease`, а не через заглушку.

Follow-up (отдельной задачей): сайдкар `/login` не принимает proxy override →
логин cian всегда с env-узла (сейчас выключенный узел 9).
2026-09-06 17:48:29 +05:00
8761602e9b chore(tradein/proxy): последние две ручки admin.py — через фабрику фетчера; снят форс pool-режима у cian-history (#3197, #3386)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
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 / 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 5m5s
Два хвоста одной темы — проводка пула прокси в контейнере backend.

#3197: `cian-login` и `domclick-detail-debug` были последними прямыми
конструкциями `BrowserFetcher(source=, endpoint=)` мимо `build_browser_fetcher`.
Без `proxy_provider`/`use_pool`/`environment` сайдкар брал свой env-узел
`SCRAPER_PROXY_URL` (на проде выключенный узел 9: 407 → camoufox `InvalidIP`), а
прод-отказ «пул пуст» (#2616) на этих путях был мёртв — он смотрит на
`environment`, который до конструктора не доезжал. Соседи по эпику уже переведены
(#3382 cian, #3389 yandex). Прямых конструкций без провайдера вне тестов больше
не осталось: остальные (backfill-задачи, pipeline) пул получают своими kwargs,
а `endpoint=None`-ветки providers — это документированный `config=None` для
офлайн-тестов.

#3386: `_PoolCurlConfig` в `cian_price_history` форсил `use_proxy_pool_curl=True`,
потому что у контейнера `backend` не было переменной. #3387 задал
`USE_PROXY_POOL_CURL: "true"` сервису `backend` в compose — зашитая константа
стала лишней и делала рубильник неотключаемым ровно на этом пути (докстринг при
этом описывал уже неверную причину). Теперь `RealScraperConfig()` напрямую.

Тесты меряют значения, а не наличие kwarg'а: на откате исходников красные
4 параметризации нового `test_3197_admin_debug_browser_pool_wiring`
(`assert None is not None` — провайдер не передан) и
`test_price_history_honours_flag_off` (`assert ['cian'] == []` — пул дёргался при
выключенном флаге).
2026-09-06 17:22:46 +05:00
f52a39e3ad Merge pull request 'fix(tradein/proxy): операции пула прокси уходят с event loop публичного /estimate — acurl_proxy_url через to_thread (#3398 п.1)' (#3407) from fix/3398-pool-ops-off-event-loop into main
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 4m18s
Deploy Trade-In / build-backend (push) Successful in 1m48s
Deploy Trade-In / deploy (push) Successful in 1m16s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
2026-09-06 11:55:10 +00:00
7d3c0eab51 fix(#3398): acurl_proxy_url — вторая отмена не бросает lease; тест меряет наш release, а не teardown; потолок бюджета в докстринге
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (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 / backend-tests (pull_request) Successful in 5m11s
2026-09-06 16:49:12 +05:00
e4feadefe6 fix(tradein/proxy): операции пула прокси уходят с event loop публичного /estimate (#3398)
All checks were successful
CI Trade-In / 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 / 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 / backend-tests (pull_request) Successful in 5m8s
Вход в `providers/_proxy.py::curl_proxy_url` синхронный и стоял ДО первого await во
всех трёх async-сайтах `/estimate` (avito/imv, yandex/valuation, cian/valuation).
`RealProxyProvider.acquire/mark_health/release` ходят в БД синхронно (своя SessionLocal
на операцию), а публичный backend крутится на ОДНОМ воркере uvicorn (#3083): на
cache-miss это 3 источника x (acquire + mark_health + release) блокирующих вызовов
прямо на loop'е. `_with_budget(asyncio.wait_for)` синхронный вход прервать не может, а
при исчерпанном пуле коннектов (5+10) checkout ждёт до 30 с — весь инстанс молчит.

`acurl_proxy_url` — async-обёртка над тем же синхронным контекстом: вход и выход через
`asyncio.to_thread`, выход тоже (иначе mark_health/release держали бы loop на выходе).
Семантика прежняя: `NoProxyAvailableError` до запроса, BaseException-ветка (#3397:
отмена → health=False), release в finally. `to_thread` копирует contextvars, поэтому
`current_run_id` (#3404) виден в потоке как раньше.

Новое по сравнению с sync-путём: отмена может прийти ВО ВРЕМЯ acquire (раньше это было
невозможно по построению). Вход держится через `asyncio.shield` и добирается в except —
иначе выданная в потоке аренда висела бы до reap_stale_leases.

Переключены только три сайта `/estimate`. Sync-вызывающие и async-сайты под scheduler
(cian/detail.py::fetch_detail, cian/newbuilding.py::resolve_cian_zhk_url_via_search)
остаются на `curl_proxy_url` — там loop не обслуживает публичные запросы.
2026-09-06 16:05:50 +05:00
75b0931fad Merge pull request 'fix(cian): капча видна в отчёте прогона (ban_kinds.platform по типу) и банит узел на curl-пути (#3402 follow-up)' (#3406) from fix/3402-followups-ban-kinds-curl into main
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 4m10s
Deploy Trade-In / build-backend (push) Successful in 1m41s
Deploy Trade-In / deploy (push) Successful in 1m50s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-06 10:40:11 +00:00
224f91e8fa fix(cian): капча видна в отчёте прогона и банит узел на curl-пути (#3402)
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 5m11s
Два follow-up из ревью #3403.

1. Капча-волна была невидима в счётчиках. `_note_refusal` ключевался только по
   HTTP-статусу, а капча приходит с 200 (свой детект по <title>) или без статуса
   (сайдкар) → рос один `listings_failed_fetch`, `ban_kinds` оставался пустым, и
   волна отказа площадки читалась как дрейф нашей разметки. Теперь диагноз берётся
   сперва по ТИПУ исключения (`ban_kind_of_exception`), статус — фолбэк. Инвариант
   #3196 сохранён: 'unknown' по типу И None по статусу по-прежнему ничего не пишут.

   `ban_kind_of_exception` расширен с `AvitoBlockedError` до `ProxyBanError` — это
   ровно тот mixin, по которому generic-прокси-слой уже снимает узел с выдачи
   источнику. Для Авито поведение не меняется (AvitoBlockedError его наследует),
   Cian/DomClick перестают приезжать как 'unknown'.

2. curl-путь детектил капчу, но не банил: общий parse-путь лежит ЗА границей
   `with curl_proxy_url(...)`, и `CianBlockedError` поднимался уже после
   `mark_health(ok=True)` — узел, которому Циан показывает капчу, оставался в
   выдаче Циану (дефект #2700, только на HTTP 200). Проверка перенесена ВНУТРЬ
   блока, `finally` хелпера сам делает `mark_banned(source='cian')`.

Тесты по значению (оба красные на main): батч с капчей → ban_kinds == {platform: 1};
curl-путь + HTML капчи → mark_banned == [(1, 'cian')], mark_health(ok=True) нет.
2026-09-06 15:30:12 +05:00
72e3bc9e24 Merge pull request 'feat(tradein/proxy): прогон знает свой узел, а снятый бан перестаёт стирать историю (#3404)' (#3405) from feat/3404-proxy-run-attribution into main
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 4m14s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy (push) Successful in 1m53s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-09-06 10:18:14 +00:00