Commit graph

3299 commits

Author SHA1 Message Date
7b61ecb122 fix(mera-public): этаж и состояние переживают переход на /estimate
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
Карточка лэндинга требовала этаж и состояние обязательными и клала их в
черновик. Целевая страница на монтировании звала takeDraft, а та стирала
черновик целиком, прочитав только адрес, комнаты, площадь и город: оба
обязательных поля уничтожались непрочитанными. Человек заполнял два поля,
единственным эффектом которых было их же удаление.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Правка:

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

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

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

Замер намеренно жёстче прода (без прогрева сессии и органического перехода из
выдачи), поэтому доля таких отказов в проде из него НЕ следует — её покажет
счётчик после правки.
2026-08-31 14:16:47 +03:00
a256077211 feat(tradein/proxy_rotation): ротация IP через mobileproxy.space (aproxy.site) (#3298)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m11s
Deploy Trade-In / build-backend (push) Successful in 1m5s
Deploy Trade-In / deploy (push) Successful in 7m37s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 13s
2026-08-31 11:01:41 +00:00
c4fcedda9a Merge pull request 'лендинг: типографский ритм и мобильные отступы — подача без новых обещаний' (#3292) from feat/mera-landing-attractiveness into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m4s
Deploy Trade-In / deploy (push) Successful in 59s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-31 09:32:59 +00:00
2274c82b7d Merge branch 'main' into feat/mera-landing-attractiveness
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m9s
2026-08-31 09:30:58 +00:00
41d0d84075 Merge pull request 'первый экран: оговорки вынесены из блока преимуществ' (#3293) from feat/mera-hero-separate-caveats into main
Some checks failed
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 18s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been cancelled
2026-08-31 09:30:51 +00:00
d3b0d0bea5 Merge branch 'main' into feat/mera-hero-separate-caveats
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m29s
2026-08-31 09:28:32 +00:00
eded14c3d6 Merge branch 'main' into feat/mera-landing-attractiveness
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / 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 1m30s
2026-08-31 09:28:24 +00:00
1b7d10179f Merge pull request 'липкая панель прячется, пока герой в кадре' (#3296) from fix/mera-sticky-after-hero into main
Some checks failed
Deploy Trade-In / deploy-status (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 / changes (push) Successful in 17s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been cancelled
2026-08-31 09:28:15 +00:00
e94a9792ce Merge branch 'main' into fix/mera-sticky-after-hero
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m7s
2026-08-31 09:26:41 +00:00
86e6956257 Merge pull request 'лендинг: пятая плитка падала на вторую строку, столбец «Расхождение» читался пустым' (#3290) from fix/mera-kpi-grid-and-table 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 / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been cancelled
2026-08-31 09:26:34 +00:00
98a1944a86 Merge pull request 'числа лендинга: покрытие 88 % было выше всего диапазона представительных пересборок' (#3295) from fix/mera-honest-coverage-number into main
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-08-31 09:26:28 +00:00
b277b1f678 Merge branch 'main' into fix/mera-sticky-after-hero
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 11s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m7s
2026-08-31 09:24:43 +00:00
3f57171f63 Merge branch 'main' into fix/mera-kpi-grid-and-table
All checks were successful
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m8s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / frontend-tests (pull_request) Has been skipped
2026-08-31 09:24:37 +00:00
44e7d4b63a Merge branch 'main' into fix/mera-honest-coverage-number
All checks were successful
CI Trade-In / changes (pull_request) Successful in 17s
CI / changes (pull_request) Successful in 16s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / 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 1m26s
2026-08-31 09:15:06 +00:00
f7d428c0c5 Merge pull request 'витрина: строка без улицы больше не открывает страницу и игру' (#3291) from fix/mera-showcase-order-street into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 22s
Deploy Trade-In / build-browser (push) Successful in 41s
Deploy Trade-In / build-frontend (push) Successful in 2m41s
Deploy Trade-In / test (push) Successful in 4m22s
Deploy Trade-In / build-backend (push) Successful in 1m7s
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-08-31 09:14:48 +00:00
d20b844561 Merge pull request 'бэктест: выборка вразброс — ORDER BY id DESC садится на несколько улиц' (#3289) from feat/backtest-scattered-sampling into main
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
2026-08-31 09:14:40 +00:00
078f102841 Merge pull request '«Для бизнеса» вела на форму входа — заведена страница /business' (#3294) from feat/mera-business-page into main
Some checks failed
Deploy Trade-In / test (push) Blocked by required conditions
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / build-frontend (push) Blocked by required conditions
Deploy Trade-In / build-browser (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / perimeter-smoke (push) Blocked by required conditions
Deploy Trade-In / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Has been cancelled
Deploy Infra Host / sync-infra-host (push) Successful in 10s
Deploy / changes (push) Successful in 14s
perimeter-smoke-mera / smoke (push) Failing after 16s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 52s
Deploy / build-frontend (push) Successful in 53s
Deploy / build-backend (push) Successful in 54s
Deploy / deploy (push) Successful in 1m23s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Failing after 13s
2026-08-31 09:14:34 +00:00
47df1a9020 липкая панель прячется, пока герой в кадре
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / 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 1m37s
Владелец увидел на первом экране, что полоса «Проверить квартиру» лежит поверх
абзаца «Мы называем реальную цену вашей квартиры».

.barSpacer это НЕ лечит и не может: он добавляет высоту в КОНЕЦ документа, а
.barRoot — fixed, то есть накрывает нижние ~67px вьюпорта при ЛЮБОЙ прокрутке,
включая scrollTop=0. Разобравший это агент так и написал и не стал чинить молча.

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

ПО УМОЛЧАНИЮ ПАНЕЛЬ ВИДИМА. Скрытие включает только клиентский наблюдатель;
если скрипт не выполнился, поведение остаётся ровно сегодняшним. Обратный
порядок (скрыта, показывает скрипт) в тех же условиях убрал бы призыв со
страницы совсем — то есть чинил бы вид ценой работы. Нет секции героя —
наблюдать нечего, панель остаётся видимой, а не пропадает.

Убирается transform-ом, а не display:none: transform идёт на композиторе и не
вызывает перекладку. Страница и так дорога в отрисовке на мобильном (1824 мс в
Style & Layout), и лечение перекрытия не должно стоить пересчёта макета на
каждом пересечении границы героя.

Убранная панель уходит и из дерева доступности (aria-hidden + inert): иначе Tab
уводит в кнопку, которой на экране нет.

Тест проверяет ПОВЕДЕНИЕ, а не наличие класса в файле: наблюдателю скармливается
пересечение. Фальсификация обеих сторон — панель не прячется («expected false to
be true») и панель скрыта по умолчанию («до срабатывания панель видима»).
150 passed, eslint чисто.
2026-08-31 14:13:36 +05:00
da6a6b6e82 Merge pull request 'P1: лендинг отдавался без витрины — имя сервиса двоится между продуктами' (#3282) from fix/mera-ambiguous-service-names 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 14s
Deploy Trade-In / build-browser (push) Successful in 43s
Deploy Trade-In / build-frontend (push) Successful in 2m29s
Deploy Trade-In / test (push) Has been cancelled
2026-08-31 09:10:09 +00:00
c4beee29ff для бизнеса: убраны обещания, которых нет в контуре; гейт чисел накрыл подстраницы
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 10s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
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 Trade-In / frontend-checks (pull_request) Successful in 1m3s
Снимает два замечания ревью.

1. КОПИЯ ОБЕЩАЛА НЕСУЩЕСТВУЮЩЕЕ. «Расчёты по потоку адресов», «не для одной
сделки, а потоком», «по вашему потоку адресов» — всё это подразумевает
массовую загрузку, которой в контуре нет: ни CSV, ни импорта, ни пакетной
ручки. Проверено grep-ом по src/app/v2.

Что там ЕСТЬ на самом деле и теперь названо: учётные записи команды
(app/api/v1/team.py), история своих расчётов (useEstimateHistory, оверлей
«Предыдущие оценки»), месячный лимит на аккаунт (account_quota_overrides,
monthly_limit/unlimited), выдача доступа вручную.

Это ровно тот дефект, который на этом лендинге выкорчёвывали весь день:
величина или возможность, которой никто не мерил и не делал, в продуктовом
голосе. Новая страница не имеет права начинать заново.

2. ГЕЙТ ЧИСЕЛ НЕ ВИДЕЛ НОВУЮ СТРАНИЦУ. Сканер знал два места: корневой
page.tsx и каталог _components/v3. Публичная /business под него не попадала —
правило держалось на памяти автора. Теперь подстраницы находятся ОБХОДОМ
каталога, а не списком: список — снимок, следующую страницу в него снова
забудут вписать.

Юридические страницы (оферта, политика, возврат, документы) исключены явно и
с обоснованием: они состоят из ссылок на законы («Закон РФ от 07.02.1992
№ 2300-1», «152-ФЗ», «ст. 18.1»), а по форме это те же числа, что и замеры.
Отличить регулярным выражением нельзя, и правило гейта к ним не относится —
там числа цитируются из нормативных актов, а не утверждаются о рынке.
Расширение проверено: включение этих страниц давало 4 ложных срабатывания.

Фальсификация: вписанные в /business «42 700 квартир» роняют гейт по значению
с указанием файла. 150 passed, tsc и eslint чисто.
2026-08-31 13:02:45 +05:00
79ae87019a бэктест: подписи окна приведены к замеру, уверенность — к тому же прогону
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
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 1m21s
Снимает три блокера ревью на этой же ветке.

1. ПОДПИСЬ ОКНА. Числа перевели на представительную выборку, а подпись
осталась от прежнего прогона: «сделки II квартала 2026 года». Автор честно
записал, что квартальный состав пересборки не смотрел, и оставил критерий
снятия — посчитать deal_date по выборке. Посчитал:

    III кв 2025 — 106 (26,5 %)      I кв 2026 — 88 (22,0 %)
    IV кв 2025  — 110 (27,5 %)     II кв 2026 — 96 (24,0 %)

Пересборка покрывает ЧЕТЫРЕ квартала, II квартал 2026 — четверть её. Прежняя
подпись для этих чисел была бы прямым враньём.

2. ЗНАМЕНАТЕЛЬ. Был 5 954 — сделки только II квартала. Для выборки по четырём
кварталам это завышало долю вчетверо: 5,5 % вместо 1,3 %. Замер тем же
фильтром, что у выборки: в окне 24 333, из них II квартал 2026 — 5 954. Оба
числа одним запросом.

3. УВЕРЕННОСТЬ. Плитка «325 из 327» была из прогона 29.08, а лид секции печатал
«замер 31.08» — старому числу приписывалась новая дата, и 325 в лиде совпадало
с 325 в плитке, читаясь как одна выборка. Заменено на «400 из 400» из ТОЙ ЖЕ
пересборки. Знаменатель 400 — запрошенные сделки, а не 325 сматчившихся:
уверенность считается на каждой оценке.

4. Оговорка уверенности не выводилась вовсе: AccuracyV3 собирал оговорки двух
величин из трёх. Плитка стояла без единого слова о том, почему уверенность
низкая. Добавлена третья.

Тесты проверены фальсификацией: возврат знаменатели одного квартала даёт
«expected 5954 to be 24333» и «expected 5.458 to be less than 2»; снятие
оговорки из вывода — «оговорка не выводится». 157 passed.
2026-08-31 12:59:27 +05:00
2864d80854 fix(mera-public): честные числа бэктеста — медиана трёх представительных пересборок
Покрытие 88 % и расхождение 14,5 % получались только на кластеризованной
выборке (ORDER BY id DESC: 20 строк на четыре улицы). Замер 31.08.2026 тремя
представительными пересборками (engine=full, ЕКБ, три соли) дал покрытие
82,77/84,31/86,02 % и MAPE 14,66/15,28/15,66 %: опубликованные 88 % лежат ВЫШЕ
всего диапазона, то есть недостижимы, а 14,5 % — чуть ниже, тот же
благоприятный край.

- BACKTEST.coverage 88 % -> 84,3 %, priceError 14,5 % -> 15,3 % (медианы),
  sampleN 327 -> 325 (медианная пересборка);
- в оговорки добавлен сам диапазон пересборок (82,8-86,0 % и 14,7-15,7 %):
  точечное значение без него обещает точность, которой у замера нет;
- BACKTEST_SOURCE переписан — представительная выборка, а не последние
  загруженные строки; дата замера 29.08 -> 31.08.2026;
- confidenceLow («325 из 327») на пересборках НЕ пересчитывался: у него
  отдельный источник с прежней датой и оговорка про свой знаменатель;
- квартальный состав представительной выборки не проверен — записан
  дословно, с критерием снятия, рядом с BACKTEST_PERIOD_LABEL.

Тест: backtest-freshness проверяет наличие диапазона в оговорках (+контроль на
инструмент) и что AccuracyV3/HeroV3 не содержат самих величин; render-тест
перестал сверяться с вписанным «14,5 %» и берёт значение из landing-facts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 12:41:32 +05:00
4d51d19f7c feat(mera-public): публичная страница /business вместо ссылки на форму входа
Кнопка «Для бизнеса» в шапке и «МЕРА для бизнеса» в подвале вели на B2B_URL.
Реальная цепочка на проде: 308 → /trade-in → 307 → /trade-in/v2 → клиентский
редирект → /trade-in/login. Посетитель публичного сайта упирался в форму
«Логин / Пароль» от аккаунта, которого у него нет.

Заведена страница /business: кому полезен бизнес-контур, чем он отличается от
бесплатной проверки, и как связаться (почта и телеграм — те же константы, что
в подвале и в документах, не выдуманные для страницы). Ни цен, ни сроков, ни
объёмов: этих величин никто не мерил. Ссылка на вход оставлена внутри страницы
второстепенной — «уже работаете с нами».

Периметр согласован во всех четырёх местах: PUBLIC_ROUTES, три матчера Caddy
(@meraPages, @meraShortSlash, @meraLongPages), PUBLIC_SHORT_PATHS в RouteGuard,
плюс строка в smoke-mera-perimeter.sh. Гейт периметра проверен фальсификацией:
удаление /business из @meraShortSlash роняет public-perimeter.test.ts.

noindex — тем же способом, что у /docs (metadata.robots).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 12:40:59 +05:00
c9251d3ed5 первый экран: оговорки вынесены из блока преимуществ
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m0s
Владелец попросил сделать лендинг привлекательнее. Один скриншот мобильной
страницы показал причину, по которой первый экран читается стеной текста:
шесть однотипных пунктов подряд, из которых ДВА про обратное.

«Пример по рынку Екатеринбурга, а не расчёт по вашему адресу» и «По всем
расчётам МЕРЫ, а не только по Екатеринбургу» лежали ВНУТРИ .heroPerks — в том
же контейнере, тем же кеглем, с маркером в том же ряду, что «Бесплатно» и
«За 150 ₽». То есть дисклеймер выглядел как ещё один пункт оффера.

Вынесены в собственный блок за разделителем: тише, мельче, отдельно.

Слова НЕ ТРОНУТЫ. Порядок чтения не тронут: оговорки по-прежнему выше формы,
человек прочитает их до ввода адреса — ровно то требование, ради которого их
туда и ставили. Ширина колонки та же, контраст держит AA. Убирать или прятать
оговорку нельзя: замаскированная оговорка хуже показанной, и вся эта страница
построена на обратном принципе.

Тесты, проверяющие СОДЕРЖИМОЕ обеих строк (что экспозиция и аналоги подписаны
разными границами и не слиты в одну), держатся за parentElement строки, а не
за контейнер — переезд их не задел. 147 passed.
2026-08-31 11:35:02 +05:00
884a6b9cfe бэктест: режим выборки вразброс — ORDER BY id DESC садится на несколько улиц
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / 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
ORDER BY id DESC берёт последние ВСТАВЛЕННЫЕ строки, а Росреестр грузится
пачками по домам: соседние id это один дом и одна улица. Замер на проде
31.08.2026 по Екатеринбургу, выборка 200:

    последние 200 по id : 38 разных улиц, до 22 сделок с ОДНОЙ улицы
    случайные 200       : 129 разных улиц, максимум 6 с одной

На этой выборке стоит витрина лэндинга: 20 строк с ЧЕТЫРЁХ улиц, 16 из них с
двух. Владелец заметил это как «почти все сделки из одного района».

ЧТО ЗАМЕР ПОКАЗАЛ, а что нет. Пять прогонов по 200 сделок:

    recent            MAPE 15.18%  покрытие 88.75%
    scattered mera    MAPE 14.96%  покрытие 83.13%
    scattered alpha   MAPE 15.97%  покрытие 88.34%
    scattered bravo   MAPE 13.69%  покрытие 83.85%
    scattered charlie MAPE 14.36%  покрытие 86.23%

Заголовочная точность УСТОЯЛА — опубликованные 14,5% лежат внутри разброса
представительной выборки. Кластеризация её не раздувала.

А покрытие коридором опубликовано как 88% — это верх диапазона: при пересборке
выборки величина гуляет 83-88 при медиане около 86. Публикуется лучший прогон
из пяти как единственный. Правка самих чисел лэндинга — отдельным заходом.

УМОЛЧАНИЕ НЕ ТРОНУТО. Докстринг _sample_sql обещает дефолтному пути побайтово
тот же SQL ради замороженного регресс-гейта; смена умолчания молча обнулила бы
сравнимость всей истории замеров. Режим выбирается флагом --spread.

Порядок по md5(id||seed), а не random(): нужен ВОСПРОИЗВОДИМЫЙ порядок, иначе
два прогона отличаются и из-за правки, и из-за состава выборки, и разделить
вклады нечем.

Тест держит обе стороны и проверен фальсификацией: сделать вразброс умолчанием
— падает identity-проверка (сравнение текстов её бы пропустило), заменить md5
на random() — падает проверка воспроизводимости.
2026-08-31 11:28:11 +05:00
2527068c45 подача лендинга: мобильный ритм, ровные заголовки, табличные цифры
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 15s
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 1m15s
Только оформление: ни одного утверждения, числа или секции не тронуто.

- text-wrap: balance на заголовках секций — на 375px они уходят в 2-3 строки
  и оставляли висячее слово.
- artTitle подтянут к общему полу 26px (был 24px — последняя секция читалась
  мельче остальных).
- внутренние отступы карточек стали responsive (clamp): на 375px под текст
  оставалось 279px из 375, четверть экрана уходила в поля. На десктопе
  значения прежние.
- font-variant-numeric: tabular-nums на крупных значениях (плитки сверки,
  плитки цены ошибки, отклонение в строке) — разряды встают в колонку.
- «Все статьи» имела высоту нажатия 23px; паддинг гасится отрицательным
  полем, макет не сдвигается. Ховер переведён с нижней границы на
  подчёркивание, иначе линия легла бы по краю паддинга.
2026-08-31 11:24:52 +05:00
28bf3a82d7 fix(mera-public): плитки точности в один ряд, «Расхождение» по одному краю
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / 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 1m21s
Пятая плитка (тёмная, из /stats) не влезала в грид: контейнер 1168px,
minmax(220px) + gap 20px требуют 1180px — плитка падала на вторую строку
одна и узкая, читалось как обрыв вёрстки. Заменил грид на flex-wrap с общим
базисом 200px: пять плиток встают в ряд, четыре — тоже, а на узких ширинах
остаток растягивается на всю строку вместо огрызка слева.

Значения столбца «Расхождение» стояли по левому краю ячейки, а заголовок —
по правому, из-за чего столбец читался пустым. Выровнял значения вправо,
по краю заголовка; на мобильном брейке text-align по-прежнему сбрасывается
в left (карточки с data-label не тронуты).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 11:24:11 +05:00
e45b05845e fix(mera-landing): наличие схемы улицы входит в полноту строки витрины
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 13s
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
Первой строкой витрины и первым раундом игры «Проверьте себя» стояла
единственная из двадцати сделка БЕЗ улицы: у неё вместо схемы улиц
рисовался полигон района. Причина — `completeness()` считала район, этаж
и этажность, но не улицу, хотя именно она решает, будет ли у строки карта.

Схема улицы — такое же ВИДИМОЕ поле, как район: строка, которой нечем
нарисовать карту, полнее строки с картой быть не может. Признак берётся
из уже загружаемого индекса улиц (`StreetIndex.lookup`, поиск в памяти);
индекс поднят выше отбора, дорогие пространственные запросы остались в
`_schemes_for` и по-прежнему считаются только для показанных строк.

Отбор по ВЕЛИЧИНЕ ОШИБКИ не введён и введён быть не может: строка
с отклонением +75,7% остаётся в витрине, просто больше не открывает её.
Прежнее правило «наличие схемы на отбор не влияет» в докстринге
`_schemes_for` заменено с разбором, почему оно давало этот дефект.

Тест двусторонний: при прочих равных строка со схемой выше строки без
неё, а строка без улицы остаётся в витрине. Фальсифицирован — снятие
`row.has_street` из `completeness` даёт красное ПО ЗНАЧЕНИЮ ([9, 8]
вместо [8, 9]), не по исключению.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 11:22:39 +05:00
189ae63e53 Merge pull request 'fix(tradein/domclick): бан сайдкара наследуется от httpx-ошибки и уезжал в транспортные сбои (#3283)' (#3287) from fix/3283b-sidecar-ban-is-httpx-subclass into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 4m5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m6s
Deploy Trade-In / build-backend (push) Successful in 1m3s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-30 20:25:23 +00:00
bot-backend
1e02badbe9 fix(tradein/domclick): бан сайдкара наследуется от httpx-ошибки и уезжал в транспортные сбои (#3283)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m5s
Правка #3286 разделила отказы площадки и сбои нашей стороны по природе
исключения: httpx-ошибка в цепочке причин = транспорт. Прод показал дыру
на первом же прогоне (5406):

  СБОЙ #1/400 (подряд=1/10, http=401, kind=platform, не блок площадки):
  DomClick detail: sidecar detected platform refusal

Заголовок «не блок площадки» стоит над записью, где kind=platform и код 401,
то есть над самым что ни на есть отказом площадки. Причина в иерархии:
SidecarBanPageError объявлен как подкласс httpx.HTTPStatusError, поэтому
проверка на httpx, стоявшая первой, забирала генуинный бан себе.

Порядок проверок перевёрнут: бан сайдкара отсекается ДО общей httpx-ветки.
Остальное поведение #3286 не тронуто — обычная 500 от сайдкара по-прежнему
транспортный сбой.

Тесты: +4, включая закрепление самой предпосылки (issubclass проверяется
явно, чтобы смена иерархии не отключила проверку молча) и страховку, что
обычная 500 осталась сбоем. Проверено мутацией: снятие порядка роняет тест
и печатает самопротиворечивый лог «10 сбоев подряд без единого отказа
площадки, диагнозы: {'platform': 10}». Набор целиком — 5210 passed.
2026-08-30 17:02:46 +03:00
03d04f13b5 Merge pull request 'fix(tradein/domclick): таймаут сайдкара и пустой пул считались отказами площадки (#3283)' (#3286) from fix/3283-non-blocks-counted-as-blocks into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 14s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 7m33s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / test (push) Successful in 4m5s
Deploy Trade-In / build-backend (push) Successful in 1m4s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-30 13:42:10 +00:00