Commit graph

2560 commits

Author SHA1 Message Date
bot-backend
881730bf20 fix(tradein/privacy): не удалять B2B-строки в purge + находить телефон в другом формате при erasure (#2547)
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / 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 3m6s
Deep-review HIGH: purge_expired_trade_in_data удалял trade_in_estimates по
expires_at без разбора B2B/B2C -- эта колонка TTL ссылки/PDF, а не срок
хранения строки, и её единообразно проставляет каждой оценке estimator.py.
Прод-аудит: 1040/1057 строк просрочены, 911 из них у пилотов (admin,
kopylov, brusnika, praktika, pilottest, admintest, user1). DELETE теперь
ограничен created_by IS NULL -- ровно анонимная B2C-популяция (129 строк).
Докстринг миграции 231 переписан: явные цифры аудита, необратимость,
чек-лист (свежий SELECT count + один supervised прогон) перед enable.

Deep-review MEDIUM: erase_person_data сравнивал phone точным =, а lead.py
сохраняет номер как прислали (без нормализации, намеренно) -- разное
форматирование одного и того же номера не находилось, 0 строк удалялось,
но ответ всё равно был 200 "данные удалены". Сравнение переведено на
regexp_replace(x, '\D', '', 'g') с обеих сторон.

Оба фикса проверены живьём (throwaway Postgres 16 в docker, вне обычного
mock-only CI-лейна): без гварда пилотская строка удалялась вместе с
анонимной; без нормализации разноформатный телефон не находился. С
фиксами -- находит/не находит ровно как задумано. Добавлены self-skipping
live-DB тесты (паттерн test_house_dedup_merge.py::_live_session) плюс
статические SQL-guard тесты.
2026-08-06 19:49:13 +03:00
bot-backend
3ee99efaa4 chore(tradein/privacy): перенумерация 231 и merge main - коллизия префикса (#2547)
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 3m21s
2026-08-06 19:10:27 +03:00
bot-backend
b93bee5393 Merge remote-tracking branch 'forgejo/main' into pr2547-privacy-work 2026-08-06 19:02:18 +03:00
c86a5378ef feat(tradein/houses): журнал слияний домов — слияние стало обратимым (#2740)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m6s
Deploy Trade-In / build-backend (push) Successful in 1m2s
Deploy Trade-In / deploy (push) Successful in 1m31s
2026-08-06 15:41:20 +00:00
0dc6f12630 fix(tradein/avito): серия отказов обрывается и называет причину, а не выедает бюджет (#2674) (#2739)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m3s
Deploy Trade-In / build-backend (push) Successful in 57s
Deploy Trade-In / deploy (push) Successful in 1m31s
2026-08-06 15:29:48 +00:00
4aec49f7fb fix(tradein/yandex): в очередь обогащения не берём то, что парсер отвергает до сети (#2674) (#2738)
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 3m7s
Deploy Trade-In / build-backend (push) Successful in 59s
Deploy Trade-In / deploy (push) Successful in 1m13s
2026-08-06 15:22:21 +00:00
f0968c8513 test(tradein/auth): правило про синхронную сверку — сторожем, а не комментарием (#2715) (#2735)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
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 3m0s
Deploy Trade-In / build-backend (push) Successful in 32s
Deploy Trade-In / deploy (push) Successful in 1m24s
2026-08-06 14:35:32 +00:00
90e328df66 fix(tradein/auth): отказ по насыщению — до выборки из БД и с агрегированным следом (#2715) (#2734)
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 3m0s
Deploy Trade-In / build-backend (push) Successful in 58s
Deploy Trade-In / deploy (push) Successful in 1m23s
2026-08-06 14:27:26 +00:00
bot-backend
dccd2d4272 chore(tradein/privacy): перенумерация миграций и merge main - разблокировка PR (#2547)
192/193 -> 229/230: main занял 192_tradein_users_auth.sql и
193_tradein_users_seed.sql за время простоя PR. 228 зарезервирован
открытым PR #2732 (228_payments.sql) - следующие реально свободные
229/230, порядок consent_proof -> retention сохранён.

Правки ссылок на старые имена/префиксы: docstring-заголовки самих
SQL-файлов, перекрёстная ссылка 229 -> 230 в комментарии-докстринге,
комментарии migration 192/193 в lead.py / config.py / schemas/trade_in.py
/ purge_expired_trade_in_data.py, переменные и имена тестов в
test_estimate_consent_gate.py / test_purge_expired_trade_in_data.py.
(Оставлены нетронутыми ссылки на migration 192/193 в auth_session.py и
test_team_api.py - это про другие, уже существующие на main миграции
192_tradein_users_auth.sql / 193_tradein_users_seed.sql, не про эту
пару.)
2026-08-06 15:56:02 +03:00
64a7975549 fix(tradein/proxy): проба узла ходит браузерным трактом, вердикт живёт отдельно от HTTP-пробы (#2723) (#2736)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m2s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 1m54s
2026-08-06 12:50:24 +00:00
bot-backend
6820337da0 Merge remote-tracking branch 'forgejo/main' into pr2547-privacy-work
# Conflicts:
#	tradein-mvp/backend/app/services/estimator.py
#	tradein-mvp/backend/app/services/product_handlers.py
2026-08-06 15:47:03 +03:00
a32ccabd0d feat(tradein/payments): подпись Token, клиент Т-Банка и сборка чека (#2733)
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 3m3s
Deploy Trade-In / build-backend (push) Successful in 1m9s
Deploy Trade-In / deploy (push) Successful in 1m24s
PR-C платёжного контура: token.py (sign + verify_notification_token, оба эталонных вектора Т-Банка перепроверены независимо), receipt.py (54-ФЗ ФФД 1.05, целые копейки), tbank_client.py (Init/GetState/CheckOrder/Confirm/Cancel, таймаут 15с, 4xx не ретраится). Слой инертный: 0 импортёров, роутеров нет.
Co-authored-by: bot-backend <bot-backend@gendsgn.local>
Co-committed-by: bot-backend <bot-backend@gendsgn.local>
2026-08-06 12:36:57 +00:00
2a1577738a chore(tradein/db): DROP listings_snapshots.position_in_serp — шаг 2 из 2 (#2697) (#2728)
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 2m59s
Deploy Trade-In / build-backend (push) Successful in 32s
Deploy Trade-In / deploy (push) Successful in 1m22s
2026-08-06 11:41:39 +00:00
5f71fc670f fix(tradein/scraper): сигнал живости из середины батча — живые прогоны перестают числиться зависшими (#2725) (#2727)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
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 2m59s
Deploy Trade-In / build-backend (push) Successful in 56s
Deploy Trade-In / deploy (push) Successful in 1m36s
2026-08-06 11:30:52 +00:00
02b256288d fix(tradein): молча задеселекченный тест починен, deselect снят (#2722) (#2729)
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-08-06 11:30:42 +00:00
3527d2b608 ci(tradein): тесты браузерного сайдкара наконец бегут в пайплайне (#2722) (#2726) 2026-08-06 11:19:50 +00:00
3231233b73 fix(tradein/browser): четыре теста сайдкара догоняют код — тест отстал, не код сломан (#2722) (#2724)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
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 29s
Deploy Trade-In / deploy (push) Successful in 58s
2026-08-06 11:13:17 +00:00
6cf9172d96 fix(tradein/auth): доля слотов сверки пароля на адрес — потолок перестаёт бить по своим (#2714) (#2717)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m0s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m9s
2026-08-06 10:42:39 +00:00
a52b9d07cf fix(tradein/browser): повтор evaluate на той же странице — гонка случается ПОСЛЕ load (#2676) (#2721)
All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m8s
Deploy Trade-In / deploy (push) Successful in 1m33s
2026-08-06 10:18:14 +00:00
b8afe13ae4 fix(tradein/scraper): длинная серия неудач перестаёт замолкать, оборванный прогон — называться успехом (#2670) (#2720)
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 2m57s
Deploy Trade-In / build-backend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 1m40s
2026-08-06 10:06:58 +00:00
0144092856 fix(tradein/houses): camelCase-типы домов приводятся к канону у источника (#2678) (#2719)
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 2m58s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 2m5s
2026-08-06 09:55:54 +00:00
663a831775 fix(tradein/scraper): отметки времени прогона перестают замерзать в его же транзакции (#2702) (#2718)
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-08-06 09:55:50 +00:00
1ba1a55770 fix(tradein/browser): гонка «execution context destroyed» — восстановимая, ретрай без relaunch (#2676) (#2716)
All checks were successful
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-backend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m8s
Deploy Trade-In / deploy (push) Successful in 1m35s
2026-08-06 09:44:23 +00:00
9d8114158b fix(tradein/auth): bcrypt вне событийного цикла + настоящий потолок темпа логинов (#2665) (#2712)
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 3m4s
Deploy Trade-In / build-backend (push) Successful in 1m2s
Deploy Trade-In / deploy (push) Successful in 1m15s
2026-08-06 09:02:10 +00:00
627e163103 fix(tradein): TTL-деактивация не исполняется, пока сбор по источнику лежит (#2659) (#2710)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m56s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m16s
2026-08-06 08:40:41 +00:00
396d8a74b6 fix(tradein/scraper): сторож «ноль результатов» перестаёт читать «не измерено» как ноль (#2703) (#2713)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m56s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 1m49s
2026-08-06 08:28:59 +00:00
9e5e9fca08 fix(tradein/scraper): статус «забанен» перестаёт выдавать наш сбой за чужой (#2686) (#2711)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m58s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 1m44s
2026-08-06 08:18:26 +00:00
cb64796b44 fix(tradein/scraper): дневной снимок узнаёт свой прогон (#2701) (#2707)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m55s
Deploy Trade-In / build-backend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 1m21s
2026-08-06 07:00:17 +00:00
c66e18cd13 fix(tradein/imv): домовая оценка ходит в сайдкар через пул, а не через чужой env-прокси (#2698) (#2708)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m0s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 1m38s
2026-08-06 06:52:15 +00:00
90c193f898 fix(tradein): «медианный торг» гаснет на псевдорепликах и на неправдоподобном минусе (#2672) (#2706)
Some checks failed
Deploy Trade-In / deploy (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 2m58s
Deploy Trade-In / build-backend (push) Has been cancelled
2026-08-06 06:48:47 +00:00
bc5233de15 fix(tradein/cian): просмотры перестают теряться на int() по фразе (#2669) (#2705)
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 / 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-08-06 06:46:18 +00:00
58dcd76d2e fix(tradein/domclick): sale_type перестаёт быть третьим словарём (#2674) (#2696)
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 2m51s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 1m44s
2026-08-06 06:11:45 +00:00
44470f7310 fix(tradein/scraper): detail-backfill с нулём обогащений перестаёт называться успехом (#2674) (#2695)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m57s
Deploy Trade-In / build-backend (push) Successful in 59s
Deploy Trade-In / deploy (push) Successful in 1m44s
2026-08-06 05:59:37 +00:00
27272762ef fix(tradein): такт в сохранении расписания, position_in_serp невыразим (#2674)
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 2m57s
Deploy Trade-In / build-backend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 1m27s
2026-08-06 05:49:48 +00:00
a7ca0e9ee8 fix(tradein): вернуть фотографии подсказок Avito IMV задним числом (#2674) (#2693)
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 2m54s
Deploy Trade-In / build-backend (push) Successful in 28s
Deploy Trade-In / deploy (push) Successful in 1m7s
2026-08-06 05:40:02 +00:00
e1c26c212a chore(tradein): догнать _manifest_applied.txt до факта прода (31 имя) (#2692)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m54s
Deploy Trade-In / build-backend (push) Successful in 28s
Deploy Trade-In / deploy (push) Successful in 1m6s
2026-08-06 00:54:41 +00:00
fb5ec56a54 Merge pull request 'chore(tradein): разбор мёртвого кода — подключить, удалить или задокументировать (#2674)' (#2689) from chore/2674-dead-code-sweep 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) Successful in 2m10s
Deploy Trade-In / test (push) Successful in 3m1s
Deploy Trade-In / build-backend (push) Successful in 1m36s
Deploy Trade-In / deploy (push) Successful in 2m19s
2026-08-06 00:37:05 +00:00
3d38d589d0 fix(tradein): панорама для страниц без истории, точные счётчики, окно без гонки (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m0s
Правки по ревью PR #2689.

Признак панорамы был недостижим примерно для десятой части страниц. Вызов стоял
после раннего возврата по пустой истории размещений, поэтому идеально отрисованная
страница без единого объявления до записи не доходила: на проде 1519 оценок против
1360 домов с историей. Резолв дома и запись панорамы подняты выше возврата — гейт
честности не тронут. Цена: match_or_create_house теперь вызывается и для таких
страниц (может создать дом), но это тот же вызов с тем же адресом, который уже
отрабатывает на остальных 90%.

Числа в комментариях к схеме были оценками планировщика, а не точным счётом:
listings 142 569 против реальных 93 408 (раздув мёртвыми кортежами на 53%),
house_sources 46 813 против 49 502. На безопасность удаления это не влияло — нули
там точные, — но оценка уезжала в постоянный комментарий к схеме, в PR, тезис
которого «каждое утверждение несёт число с прода». Пересчитано точным count(*).

Окно расписания ДОМ.РФ 03:00-04:00 совпадало с refresh_search_matview — то есть
ровно с тем заданием, которое переносит year_built в поиск. Планировщик берёт
случайный момент внутри окна и гоняет источники параллельно, так что порядок был
подбрасыванием монеты. Перенесено на 01:00-02:00; в комментарии честно сказано, что
гарантии всё равно нет и при аномально долгом прогоне возможно отставание на цикл.

Тесты, адресовавшие вызовы по позиции (db.execute.call_args_list[0]), переведены на
фильтр по SQL — это и была причина, по которой добавление второго execute ломало
шесть чужих тестов разом. То же для side_effect в тесте отката батча: исключение
доставалось бы записи панорамы, которая свои ошибки глотает, и тест молча проверял
бы не тот путь. В test_save_history_items_inserts_each возвращено утверждение о
числе коммитов (было удалено вместо обновления).

Refs #2674
2026-08-06 05:32:14 +05:00
f76485781b Merge pull request 'fix(tradein/matching): честность тиров сопоставления домов (#2674)' (#2688) from fix/2674-matching-tiers-honesty into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m50s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 1m45s
2026-08-06 00:18:06 +00:00
bot-backend
1a577fe748 fix(tradein/matching): снять слияние по ГАР-GUID, починить приёмник кадастра и keeper (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m54s
Ревью PR #2688 нашло, что расширение ключа дедупа было неверным. Снимаю его
полностью и добавляю три правки, которых не хватало.

СНЯТО: слияние 781 дома по COALESCE(house_fias_id, gar_house_guid).
Аргумент «общий UUID здания есть независимая идентичность» оказался круговым.
gar_flats_loader проставляет gar_house_guid предикатом
  WHERE tradein_canon_addr(COALESCE(h.short_address, h.full_address, h.address)) = gp.canon
— левая часть побайтово равна ключу канон-прохода, то есть guid является
детерминированной функцией канон-адреса, а не вторым наблюдением. Проход шёл
с выключенным гео-стражем, значит #2187 обходился боковой дверью: канон-проход
отказывается слить два дома в 6 км, а этот сливал их же за «общий UUID»,
выданный за тот же адрес. Плюс gar_pick берёт DISTINCT ON (canon) — одна
ГАР-строка на канон, а 20.3% канонов накрывают несколько зданий, и ЕКБ-фильтр
стоит только на стороне ГАР. Кедровка/Советская 17 уехала бы в ЕКБ. Нужен
ключ, независимый от канона, либо включённый гео-страж — это другая задача.

Приёмник кадастра сужен до кадастра ЗДАНИЯ. Параметр cadastral_number (кадастр
КВАРТИРЫ) убран из match_or_create_house, Protocol HouseMatcher,
RealMatcherAdapter и обоих вызывающих; `cad` больше не падает на него фолбэком.
Мина была отложенной: начни Циан отдавать offer["cadastralNumber"], который
парсер уже читает, — у каждой квартиры свой номер, Tier 0 не сматчил бы
никогда, New-house INSERT записал бы номер квартиры в houses.cadastral_number
и попутно снял P1-страж «безномерный адрес без кадастра не создаём». Две
квартиры одного дома дали бы два дома — то самое дробление. В listings оба
поля пишутся как раньше.

Keeper: listing_cnt DESC NULLS LAST. Счётчик приходит из LEFT JOIN, у дома без
объявлений он NULL, а DESC в Postgres — NULLS FIRST, поэтому пустая запись
обгоняла запись со 192 объявлениями вопреки задокументированному правилу.
Дефект предсуществующий и живой для канон-прохода.

Сторож границы вызова для живого ФИАС-тира. Прежние проверки были
структурными — видели имя параметра в сигнатуре. Уберут аргумент на настоящей
границе (estimator.estimate_quality -> match_house_readonly) — сигнатура цела,
тесты зелёные, тир снова мёртв. Новый тест смотрит на сам вызов. Заявление
«тест ловит неуловимый класс» из прошлого описания снято как преувеличение:
структурная проверка ловит подслучай, и building_cadastral_number её проходит
при нуле срабатываний из 49 502.

Остаётся из первого захода: снятый фильтр поиска has_kadastr, разделение
ФИАС-тира (удалён в пути создания, оставлен в read-only), поправка ложного
утверждения в шапке cadastral_geo_match.py.

Refs #2674
2026-08-06 05:11:47 +05:00
f5b39e6fc9 chore(tradein): разбор мёртвого кода — подключить, удалить или задокументировать (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m57s
Восемь находок «написано, покрыто тестами, ни разу не сработало» разведены на три
разных диагноза. Две из восьми оказались не мёртвым кодом, а оборванной проводкой.

ПОДКЛЮЧЕНО

Загрузчик ДОМ.РФ. Loader и CLI существуют с #2013, а Handler'а в product_handlers
и строки в scrape_schedules не было — вызвать его было нечем. На проде 29 978 строк
staging с ОДНИМ loaded_at (2026-07-12), то есть ровно один ручной запуск, 24 дня
без обновления. Отсюда кормятся houses.year_built/material_walls/total_floors и
дальше listings.year_built — когортный фильтр эстиматора. Недельный такт, окно
03:00-04:00 UTC (до импорта ДКП и дневных агрегатов).

filters_hash. Парсер читал estimation.sale.data.filtersHash, а Циан кладёт ключ
уровнем выше — estimation.sale.filtersHash. Колонка пуста 0/1658, при том что в
сохранённых сырых ответах хеш есть у 139/139 и все значения различны. Путь исправлен,
139 строк восстановлены бэкфиллом из raw_payload.

has_panorama. Разбирался парсером, лежал в карте приоритетов, обещан публичным
контрактом market.v_houses — и не попадал в houses ни одной строкой кода (0 из 9366).
Пишется там, где yandex_valuation уже держит и house_id, и мету. Гейт честности:
парсер отдаёт bool, а не bool|None, поэтому false пишем только при подтверждённо
отрисованной странице (есть год или этажность) — иначе NULL, а не выдуманный false.

УДАЛЕНО

Дедуп-обёртки эстиматора _phys_dedup_key / _extract_street_token: 25 ссылок, все из
тестов. Хуже, чем просто мёртвые — _phys_dedup_key утверждала правило «ключ = кадастр
ИЛИ улица», которого в боевом дедупе нет (_union_find_phys_dedup держит оба композита
и сливает по любому совпадению). Тесты переведены на живые функции.

Тиерные коэффициенты выкупа asking_to_sold_ratios_tiered + asking_to_sold_tier_bounds:
ноль читателей и писателей, флага tier_aware_ratio_enabled не существует. Посчитаны
один раз при накатке 098 (computed_at 2026-06-27) — тогда как живая
asking_to_sold_ratios обновляется ежедневно (2026-08-05). Методика сохранена в 098.

Колонки без писателя: listings.merged_into (113 уже называла её мёртвой) и
house_sources.raw_payload вместе с GIN-индексом по всегда-NULL колонке.

v_data_quality.price_disagreements_count: у всех 89 699 объявлений ровно один
источник, показатель структурно не мог быть ненулевым, а ноль читался как
«расхождений нет».

ЗАДОКУМЕНТИРОВАНО

BROWSER_BLOCK_RESOURCES выставлен во всех трёх прод-контейнерах, а код перестал его
читать в #1812. Блокировка при этом не ослабла (image глушит camoufox block_images,
font/media — дефолт списка типов), мёртв только выключатель. Сервис теперь говорит
об этом на старте: молча игнорируемая ручка опаснее отсутствующей.

v_price_divergence / v_cross_source_health оставлены как задел, но в COMMENT написано,
почему они пусты структурно: боевой путь загрузки зовёт upsert_listing_source
напрямую и не зовёт match_or_create_listing.

house_sources.ext_url пуст 46 813/46 813, но входит в публичный контракт
market.v_house_sources — оставлен и подписан.

Refs #2674
2026-08-06 05:00:36 +05:00
bot-backend
3fd6550a16 fix(tradein/matching): честность тиров сопоставления домов (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
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 2m54s
Три находки эпика #2674 про верхние тиры матчинга домов. Замеры — прод
tradein-postgres, 2026-08-05/06.

Кадастр от площадок не приходит вообще. listings.cadastral_number (кадастр
КВАРТИРЫ) — 0 из 93 408; единственный писатель, парсер Циана, читает
offer["cadastralNumber"], которого в ответе нет. Все 28 504 заполненных
building_cadastral_number на 100% пришли из локального гео-зеркала ЕГРН
(tasks/cadastral_geo_match.py, KNN <=50 м) — проверено джойном к
cad_buildings_local. Поэтому снят фильтр поиска has_kadastr: предикат
`cadastral_number IS NOT NULL` мог вернуть только пустую выдачу. Колонка и
писатель оставлены — заработают сами, если площадка начнёт отдавать кадастр.

Tier 0 cadastr_exact оставлен, но не подключён к гео-кадастру. Он достижим по
построению (ScrapedLot -> адаптер -> матчер), просто данных нет; подать туда
KNN-заполнение НЕЛЬЗЯ: как ключ здания оно не инъективно — 656 из 3 260
значений накрывают >1 здание ГАР (20.1%), 751 из 2 864 зданий получают >1
значение (26.2%). Это был бы over-merge с confidence 1.0. Заодно исправлено
ложное утверждение в шапке cadastral_geo_match.py, будто Tier 0 трактует эту
колонку как подсказку.

Tier 0.5 fias_exact удалён из match_or_create_house. Параметра house_fias_id
не было ни в Protocol scraper_kit.contracts.HouseMatcher, ни в
RealMatcherAdapter, ни у двух прямых вызывающих — передать его было некому.
В match_house_readonly тир оставлен: у estimate-пути источник ФИАС есть
(payload.target_fias_id / DaData).

Что чинит сопоставление на самом деле: ключ идентичности в house_dedup_merge
расширен с house_fias_id до COALESCE(house_fias_id, gar_house_guid). Это один
и тот же UUID здания в ГАР (3 666 совпадений из 3 667 домов, где заполнены оба),
но заполняют его разные источники, и половина в проход не входила. Read-only
прогон отрендеренного mapping-SQL на проде: старый ключ — 0 пар, новый — 781
(8.3% таблицы houses, 6 389 объявлений на них). Канон-проход эти дома узнаёт
(900 пар из 919 имеют один канон-адрес), но блокирует гео-стражем: 356 пар
с NULL geom, 457 дальше 250 м (максимум 5 065 км — битый геокод). Ровно
аргумент #2187: общий UUID здания старше близости.

Качество сопоставления сейчас: 0 из 49 502 строк house_sources сматчены
верхними тирами; fingerprint 58.97%, new 22.65%, geo_proximity 18.36%.

Тесты: новый tests/test_matching_tier_reachability_2674.py сверяет параметры
матчера с границей вызова (Protocol + адаптер) — ловит класс «ветка есть,
передать некому», который обычный тест не видит, потому что зовёт функцию
напрямую. Удалены два теста мёртвого fias-тира: они были зелёными ровно
потому, что обходили границу вызова.

Refs #2674
2026-08-06 04:37:12 +05:00
5e92810d72 Merge pull request 'fix(tradein/avito): окно ретроспективы под недельный такт + диагноз полного обхода (#2674)' (#2685) from fix/2674-avito-full-load-coverage into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m49s
Deploy Trade-In / build-backend (push) Successful in 1m32s
Deploy Trade-In / deploy (push) Successful in 1m39s
2026-08-05 23:13:39 +00:00
6d76328168 fix(tradein/avito): верное объяснение границы окна и критерий приёмки по глубине (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
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 2m52s
Разбор ревью PR #2685. Выбор окна в 7 суток подтверждён непредвзятым замером по
одному прогону (run 2990, exhaustive 02.08, 2184 датированных строки): W=2 -> 147,
W=6 -> 446, W=7 -> 1275, W=12 -> 1278. Шестёрка теряет две трети семёрки, а 7..12 —
плато в +3 лота, то есть семёрка стоит на самой дешёвой его точке. Потеря окна 2
занижена мной в первом заходе: не 3.26x, а 8.7x.

Механизм объяснён неверно. Пик на возрасте ровно 7 — не недельный авто-подъём Авито,
а квантование нашего же парсера относительных дат: «неделю назад» -> ровно today-7,
«две недели назад» -> today-14, возрасты 8..13 по этому пути недостижимы. То самое
плато (3 лота из 2184) это и доказывает: при реальном подъёме полоса 8..13 была бы
заполнена. Вывод от этого только крепнет — шестёрка режет не по пику распределения,
а по границе квантования и теряет бакет «неделю назад» целиком, а внутри него
реальный возраст от 7 до 13 суток.

51% не воспроизводится: 1212 из 3714 датированных наблюдений — 32.6%. Пятьдесят один
получается только на знаменателе, урезанном возрастами 0-13.

Цена по запросам описана неверно и в опасную сторону. Рост не пропорционален лотам:
стоимость бакета — ceil(свежих/50) страниц с полом 1-2, при окне 7 на бакет выходит
~15-20 свежих (1275 на 77 бакетов), то есть меньше страницы. Большинство бакетов как
стояло на 1-2 страницах, так и останется. Верхняя граница честная и продом пережитая:
полный обход без отсечки — 6 ч 59 мин (run 295) и 2 ч 34 мин (run 2990).

Отсюда же переписан критерий приёмки: ждать «9-10 тысяч собранных лотов» нельзя, это
уведёт в ложный вывод. Прогон с окном 2 уже собирал 2804 лота, потому что первые
страницы всё равно полные — объём почти не сдвинется, сдвинется глубина. Считать надо
лоты с listing_date в полосе [D-7, D-3] и число страниц из лог-строки paginated=.

Впечатана мина на случай отката такта: ни миграция (GREATEST только расширяет), ни
планировщик (расширяет до такта, не сужает) окно не сузят, поэтому interval_days
7 -> 1 при окне 7 даст восьмикратный охват каждый день. Такт и окно менять вместе.

int(params.get("interval_days", 1)) падал на значении null в jsonb — соседний параметр
строкой выше обрабатывался через явную проверку на None, этот нет.
2026-08-06 04:09:05 +05:00
0ed0f97ae2 Merge pull request 'fix(tradein/admin): убрать показатели, которые не могут быть ненулевыми, и брать список источников из данных (#2674)' (#2684) from fix/2674-admin-metrics-honesty into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m2s
Deploy Trade-In / test (push) Successful in 2m55s
Deploy Trade-In / build-backend (push) Successful in 1m32s
Deploy Trade-In / deploy (push) Successful in 2m5s
2026-08-05 23:01:30 +00:00
3c5f535e6c fix(tradein/admin): гейт отмены по источнику, честный комментарий view, лимит 50 (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 8s
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 1m2s
CI Trade-In / backend-tests (pull_request) Successful in 2m58s
Ревью PR #2684 — четыре MINOR.

1. Починка фильтра открыла кнопку отмены на все 53 источника. Раньше таблица была
   пуста на каждой вкладке, поэтому кнопка не рендерилась НИ РАЗУ и дыра не
   проявлялась: ручки отмены source не проверяют вовсе. Оператор на вкладке Авито
   мог бы «отменить» refresh_search_matview — задача продолжила бы работать под
   статусом 'cancelled' (ещё один врущий статус ровно в тот день, когда их
   вычищаем), а has_running_run перестал бы держать single-run guard, который
   существует из-за инцидента с двойным свипом и баном (2026-05-31).

   Гейт поставлен на общем узле всех пяти ручек — scrape_runs.honors_cancel +
   отказ в mark_cancelled, — а не в UI: иначе ручной POST по-прежнему снимал бы
   guard. Флаг cancellable отдаётся в строке, UI по нему прячет кнопку.
   Состав набора выведен из call-site'ов runs.is_cancelled: city-sweep'ы (все
   площадки и города), full-load'ы, avito_newbuilding_sweep, rosreestr_dkp_import.
   Правило НЕ «любой *_sweep»: yandex_newbuilding_sweep отмену не опрашивает.

2. Комментарий пересозданного v_data_quality утверждал, что его обновляет
   /api/v1/admin/data-quality. Читателей у view нет ни одного — живая ручка строит
   свой запрос. PR с тезисом «ложный показатель хуже отсутствующего» не имеет права
   переносить в прод ложное утверждение о читателе.

3. Лимит выдачи 20 → 50: первые 20 строк по started_at на три четверти —
   сердцебиение proxy_healthcheck (1631 из 3245), часовой сбор мог не поместиться.
   Привязка к вкладке НЕ возвращается.

4. Тест «действующее определение view» искал маркер подстрокой с OR REPLACE —
   миграция с обычным CREATE VIEW или парой DROP+CREATE была бы невидима, и тест
   проверял бы 214, пока показатель уже вернулся. Заменено регуляркой на обе формы.

Фальсификация трёх новых тестов патч-методом — все три красные. Полный прогон
3490 passed / 9 skipped, tsc --noEmit чистый.
2026-08-06 03:57:46 +05:00
e4ac0365cf fix(tradein/avito): окно ретроспективы под недельный такт + диагноз полного обхода (#2674)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m53s
Окно и такт разъехались. Каденс avito_full_load задаёт default_params.interval_days,
глубину обхода — incremental_days (since = today - N, ранняя остановка по listing_date).
Это два независимых литерала, обязанных совпадать: 129 поставил окно 2 при ежедневном
такте (перекрытие было), 206 перевёл такт на 7 суток и окно не тронул. Прогон видит
[D-2, D] = 3 суток из 7; дни D+1..D+4 не попадают ни в один прогон.

Числа с прода (tradein, read-only 2026-08-06). 2026-06-21 — единственный день, когда
оба обхода отработали: инкрементальный run 297 — 2804 unique, exhaustive run 295 —
9992 unique, то есть окно в 2 суток достаёт 28.1% инвентаря. Когорта run 295, не
виденная после 23.06 (listing_date заморожен): полоса [D-2, D] — 189 лотов, полоса
[D-7, D-3] — ещё 427, расширение окна берёт в 3.26 раза больше. Гистограмма
(last_seen_at::date - listing_date) за 20 суток: возраст 0-2 — 594, возраст ровно 7 —
1212 (51% датированных наблюдений) — у Avito недельный авто-подъём, sortTimeStamp
сдвигается кратно 7. Структурный минимум бездырочного покрытия — 6, но 6 режет ровно
по этому пику; 7 = такт, полосы соседних прогонов смыкаются с суточным перехлёстом
под дрейф расписания (замер: last_run 03.08 13:37 -> next_run_at 10.08 14:16).

Цена: страниц примерно втрое больше на прогон, но прогон недельный. До 206 система
платила ~150-370 страниц семь раз в неделю; после правки — ~500-1200 в неделю, всё
ещё примерно вдвое дешевле, чем до 206.

Чиню в двух местах: миграция 215 выводит окно из фактического interval_days строки
(GREATEST — не сужает окно шире такта), scheduler расширяет его на лету и пишет
warning, чтобы расхождение не вернулось следующей правкой каденса.

Полный обход: причина не в площадке. Все пять banned-прогонов
avito_full_load_exhaustive (05.07-02.08) несут один текст —
"browser-sidecar error: browser unavailable (proxy may be down)", то есть 503 от
своего же сайдкара, у которого не поднялся камуфокс. В те же дни avito_city_sweep
(20 done), avito_newbuilding_sweep (21) и avito_detail_backfill (63) работали.
Корень — мёртвый BROWSER_PROXY_AVITO (ard.mobileproxy.space) в env сайдкара, куда
полный обход проваливался, потому что строил свой BrowserFetcher без пула; починено
не здесь, а #2637 (02.08, пул для браузерного пути Авито) и #2616 шаг 2 (05.08,
снос мёртвых env). Прод подтверждает: 0 лотов 05/12/19/26.07, 2334 и 362 в двух
прогонах после 02.08.

Остаток, который чинится кодом, здесь: 503 сайдкара классифицировался как soft-ban и
уходил в бюджет IP-ротации, а ротация снята (#2616 шаг 2, max_rot=0) — условие
rot_done < max_rot ложно всегда, а бюджет коротких backoff-retry стоял в else и был
для soft-ban недостижим. Ни одного ретрая на самую частую ошибку: один блип сайдкара
стоил бакета, четыре подряд — всего прогона. Бюджет backoff теперь общий для обеих
причин; сайдкар на таком 503 сам поднимает фоновый retry launch'а, поэтому повтор
через пару секунд обычно проходит.

Статус banned на такой ошибке остаётся ложью (площадка не банила) — это же чтение
легло в основание 206. Здесь не трогаю: набор status ограничен CHECK-констрейнтом,
а mark_banned в отличие от mark_failed сохраняет чекпоинт done_buckets.
2026-08-06 03:39:37 +05:00
4d0795ae7a fix(tradein/admin): убрать показатели, которые не могут быть ненулевыми, и брать список источников из данных (#2674)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / changes (pull_request) Successful in 8s
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 1m1s
CI Trade-In / backend-tests (pull_request) Successful in 2m57s
Четыре находки одного класса: админка показывает числа, которые никогда не
бывают ненулевыми, и подаёт это как результат. Ноль читается оператором как
«всё чисто», а не как «мы это не считаем» — такой показатель хуже отсутствующего.

1. «Помечено выбросов» (v_data_quality.outliers_flagged) — УБРАН вместе с
   колонкой listings.is_outlier. Механизм не «не доделан»: «выброс» у эстиматора
   вычисляется Tukey-фильтром по КОНКРЕТНОЙ подборке аналогов и живёт один
   запрос — один и тот же лот выброс для одной оценки и нормальный аналог для
   соседней. Persist-флаг на объявлении такое отношение выразить не может,
   реализовать пометку нечем.

2. http_requests / http_errors / returning_count / disappeared_count — УБРАНЫ.
   HTTP-запросы не считает ни один фетчер (заполнить нечем без сквозной
   инструментации). Ошибки и «пропало/вернулось» уже считает тот, кто их знает,
   и кладёт в counters jsonb: errors_count у pipeline, deactivated/revived у
   deactivate_stale_*. Отдельные колонки были бы вторым определением того же.

3. run_type — УБРАН из API, из таблицы админки и из схемы. Ни одно место кода
   его не задавало; DEFAULT из 051 подписывал 'city_sweep' даже proxy_healthcheck.
   Колонка «Тип» в UI заменена на «Источник» — там осмысленное значение.

4. Фильтр источников — теперь из данных (GET /scrape/runs/sources, SELECT
   DISTINCT source). Захардкоженная тройка не просто была неполной: сравнение
   точное, а строк с source='avito'/'cian'/'yandex' в таблице нет вообще, то
   есть каждый пункт фильтра давал пустую выдачу, и пустой выбор («Все») тоже —
   он молча подставлял source вкладки. Новый источник появляется в списке сам.

Числа с прода (tradein-postgres, 2026-08-06): is_outlier=true у 0 из 93 408
listings (NULL у 0 — только DEFAULT); четыре счётчика = 0 во всех 3244 прогонах
с миграции 015; run_type — одно значение на 3244 строки; 53 реальных источника,
2466 прогонов (76%) вне трёх площадок, включая весь Домклик.

Миграция 214 идемпотентна; v_data_quality пересоздан тем же DDL минус
outliers_flagged (порядок DROP VIEW → DROP COLUMN → CREATE как в 095).
2026-08-06 03:34:18 +05:00
673c02e5d6 Merge pull request 'fix(tradein): писатели наконец пишут то, что обещает схема — фото подсказок, статус «снято», события объявлений (#2674)' (#2682) from fix/2674-writers-honor-schema into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m50s
Deploy Trade-In / build-backend (push) Successful in 1m32s
Deploy Trade-In / deploy (push) Successful in 6m36s
2026-08-05 22:12:29 +00:00
77336d351c chore(tradein): перенумеровать миграцию 212 -> 213 (коллизия с #2681)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m55s
PR #2681 смержен, пока ветка была в работе, и принёс
212_sber_index_pull_weekly.sql. Номер 212 занят — беру 213 (свободен,
проверено git ls-tree по origin/main после fetch).

Почему локальный гейт молчал: test_new_files_do_not_reuse_prefix сравнивает
префиксы файлов В ОДНОМ ДЕРЕВЕ, а смерженный 212_sber в этой ветке
отсутствует. Проверено симуляцией (копия data/sql + stub 212_sber):
с моим 212 тест КРАСНЫЙ («212 уже у нового 212_sber»), с 213 — зелёный.

Кросс-ветковым реестром занятых номеров служит _manifest_applied.txt, но он
отстал на 27 имён (171, 187-188, 189-211, 213), поэтому префикс 212 нигде не
числился занятым. Про долг — отдельно, в этом PR манифест не трогаю.

Apply after в шапке обновлён на 212_sber_index_pull_weekly.sql.
2026-08-06 03:07:09 +05:00