Commit graph

176 commits

Author SHA1 Message Date
d953899d92 fix(mera): подпись тонкого рынка доезжает до карточки оценочной модели
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 17s
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 2m3s
Карточка печатает тот же recommended_price, что и hero, но берёт его из
своего endpoint и подписи не имела — рядом с итогом стояла неподписанная
копия величины, исключённой из расчёта (ровно то, что #3339 чинил в hero).

Проп note + проброс thinMarketNote(estimate.avito_imv) со страницы; подпись
тем же классом .bench-chip__sub, ему же дан max-width, чтобы длинная строка
не растягивала чип.

Тест падает при снятии рендера подписи (проверено: 1 failed / 5 passed).
2026-09-05 23:05:12 +05:00
bot-backend
76b24e9f4d fix(mera-public): подписи метрик снижения цены и бенчмарк отчёта не называют площадку
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m12s
CI Trade-In / backend-tests (pull_request) Successful in 5m5s
Замечание юриста (Сайт_МЕРА_v2, п.3): публичный измеримый контент не должен
строиться на названных данных площадок.

- landing_stats.py: note метрик price_cut_share_pct / price_cut_median_pct_per_month
  «Только Домклик (…)» → «Только одна из площадок (…)». Текст персистится в
  landing_stats.note и рендерится на лендинге в блоке «цена ошибки» — после
  деплоя нужен пересчёт (refresh_landing_stats), деплой кода note не перепишет.
- HeroSummary.tsx / IMVBenchmark.tsx: убраны видимые «IMV» и title
  «Information Market Value» / «Индекс Market Value» — бренд-термин оценочной
  модели площадки; подпись — «оценочная модель» + sourcePublicLabel.
- public-copy-no-platform-names.test.ts: «IMV», «Information Market Value»
  добавлены в BANNED (гейт 34/34).
2026-09-04 11:27:32 +03:00
bot-backend
5541aa9d37 fix(mera-public): публичная копия — сервис не называет себя оценщиком, площадки-источники не названы
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m7s
CI Trade-In / backend-tests (pull_request) Successful in 5m2s
Три юр-правки по документу владельца продукта «Сайт_МЕРА_v2» (31.08.2026), сделаны
локально 31.08, но не были закоммичены — на meraocenka.ru всё оставалось по-старому.

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

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

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

Текст витрины хранится в БД (`landing_showcase_runs.rejection_rule`,
`landing_showcase_deals.note`), планировщика у пересчёта нет — после деплоя нужен
ручной пересчёт или UPDATE трёх подстрок на проде.
2026-09-03 09:50:41 +03:00
ef166eaf21 fix(mera-public): защиты вместо тавтологий в coverage-тексте и выборе раундов
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m9s
Проверка «уверенные + средние + пустые = 100» была верна по определению
средней доли и упасть не могла. Инвариант перенесён на сам замер: уверенные и
пустые обязаны оставлять место третьей доле. Если её всё-таки не останется
(перезамер 85 + 15), текст теперь двухчастный — «Ещё в 0 данных меньше»
отрендериться не может.

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

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

Раунды игры брались первыми тремя строками витрины, а она отсортирована по
полноте и свежести: на проде это давало три студии с одной улицы. Выбор
жадный по комнатности и улице, детерминированный — страница серверная.
2026-09-02 17:37:13 +05:00
390a6f05b1 fix(mera-public): сверка адреса у хвоста черновика — в коде, не в комментарии
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m23s
readDraftExtras требует адрес, под который спрашивают, и на несовпадении
отдаёт null. Гарантия «потребитель обязан сверить» жила только в комментарии,
а takeDraft выходит раньше записи хвоста на пустом и на битом черновике — то
есть хвост от предыдущей квартиры доживал до следующего захода в той же
вкладке и дождался бы потребителя, который сверить забыл.

Плюс два непокрытых кейса: черновик без floor/condition убирает прошлый хвост;
хвост не отдаётся чужому адресу.
2026-09-02 14:46:10 +05:00
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
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
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
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
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
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
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
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
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
3b09368669 контраст: два оставшихся нарушения на живой странице + сторож по секциям
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / 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
Проверка axe-правилом по ОТРЕНДЕРЕННЫМ узлам прода дала 32 → 2. Обе
оставшиеся показательны, и одну из них завёл мой же вчерашний батч.

.tickerNote (3.06) — подпись под тикером. Её добавила одна ветка, токены
чинила другая; каждая по отдельности была права, вместе дали дефект: подпись
взяла --b2c-muted (токен для СВЕТЛОГО фона) и легла на тёмную плашку #121A1D,
где шкала перевёрнута. Красится --b2c-muted-dark, 6.20.

.stepsLabelAccent (4.48) — «ШАГ 3 · 150 ₽» на карточке #EAF5F7. Сторож знал
эту поверхность, но проверял accentDeep только как ЗАЛИВКУ (белый по нему), а
CSS использует его ещё и как ЦВЕТ ТЕКСТА. accentDeep #0C7B8B → #0B7382:
минимальный шаг, проходит текстом на всех трёх светлых поверхностях
(4.99/5.15/5.54) и заодно улучшает главную кнопку — белый по нему 4.97 → 5.54.

Две правки сторожа, обе про механизм, а не про случай:
1. accentDeep проверяется и как текст: токен, используемый двумя способами,
   обязан проверяться обоими.
2. Новый тест «в тёмных секциях не используются светлофоновые токены». Он
   ловит промах в ВЫБОРЕ токена, а не в его значении — тот класс, который и
   прошёл мимо. Список тёмных секций явный, но это снимок СЕКЦИЙ, а не
   нарушений: новое правило внутри известной секции ловится без правки списка.

Фальсификация обеих: возврат accentDeep даёт «4.4775 >= 4.5», возврат
.tickerNote на muted даёт «.tickerNote → --b2c-muted». Первую попытку
фальсификации засчитывать было нельзя — подмена не сработала из-за
добавленного комментария, и тест остался зелёным по построению.
2026-08-30 15:51:25 +05:00
f3e05e1907 Merge branch 'main' into fix/mera-keep-floor-in-draft
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 16s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 2m21s
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
2026-08-30 10:23:10 +00:00
619e38b1e7 Merge branch 'main' into fix/mera-ticker-context
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 1m15s
2026-08-30 10:15:23 +00:00
be59ca4452 Merge branch 'main' into fix/mera-public-contrast
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
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 1m20s
2026-08-30 10:13:25 +00:00
df53ff5a5f лендинг: этаж перестал теряться на переходе к проверке
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 12s
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 1m5s
Этаж — обязательное поле карточки: без него форма не отправляется. При этом
saveDraft его не клал, а на целевой странице поля этажа нет вовсе. Комментарий
объяснял это тем, что «повторно набирать одно короткое поле дешевле, чем
хранить лишнее» — довод неверен: переспросить негде, и обязательное поле
работало чистой помехой.

Схема бэкенда этаж принимает (TradeInEstimateInput.floor), и на цену он влияет
(первый и последний этаж). Хранится теперь по тому же доводу, что и состояние:
выбросить уже полученный ответ и спросить второй раз хуже, чем донести.

Тест — через САМУ ФОРМУ, и это не формальность. Первая попытка защитить правку
звала saveDraft({floor}) напрямую: она проверяла round-trip хранилища и
оставалась зелёной, когда из FreeCheckCard убирали передачу этажа, то есть
ровно при возврате чинимого дефекта. Сторож, который не сторожит. Теперь форма
заполняется и отправляется целиком; снятие проводки даёт «expected undefined to
be 7/16» — красное по значению.

Плюс три случая на само хранилище: старый черновик без поля переживает выкатку,
нестроковое значение выпадает не унося остальное.
2026-08-30 15:11:40 +05:00
8aac93ee9b fix(mera-public): контраст WCAG AA на публичном лендинге
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 17s
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 2m3s
axe 30.08 нашёл 32 узла color-contrast (все serious). Причина одна на все:
пары «цвет × фон» подбирались глазами, а числа в комментариях писались по
памяти — шапка b2c-tokens.ts утверждала «onAccent на accentDeep 4.6:1», тогда
как реальный замер 4.41, то есть главная кнопка призыва не проходила норму, а
документация уверяла, что проходит.

Затемнены четыре токена до запаса на худшем светлом фоне (surfaceSoft, он
темнее pageBg): accentDeep 4.41→4.97, muted 4.45→5.36, success 4.04→4.96,
danger 4.60→5.23. Оттенок сохранён, фирменный вид не меняется.

Отдельный класс — светлый токен на тёмной плашке surfaceDark, где шкала
перевёрнута: метки тикера красились muted (2.7) и accentText (2.85), стрелка
между прогнозом и фактом — линейным #2c3a3f (1.50). Переведены на mutedDark
(6.2) и accent (6.24). Заодно убраны сырые hex #9fb0b4 (2.24 на светлом —
.artSoon, .gameScale, .repPaidFine, .artFootnote; 7.84 на тёмном) — палитра
запрещает hex в CSS именно потому, что мимо токена величина не считается
никаким аудитом.

Регресс ловится без браузера: contrast-tokens.test.ts считает яркость по
формуле WCAG прямо на значениях токенов и проверяет ОБА фона страницы, плюс
сторож на новые сырые hex в CSS. Проверен фальсификацией: возврат muted к
#667579 красит тест.
2026-08-30 14:57:21 +05:00
7d6caed5fb МЕРА: лента над первым экраном получает контекст разброса
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 19s
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 1m45s
Над лентой стояло «медианное расхождение с ценой ДКП — 14,5 % по 327
сделкам», а в самой ленте 5 строк из 20 расходились больше чем на 30 %
(75,7 · 63,6 · 47,4 · 41,4 · 38,5). Первое, что видел посетитель страницы
про точность, — крупный промах без единой цифры контекста.

Промахи не прячутся: строки витрины отобраны по ПОЛНОТЕ и СВЕЖЕСТИ
(_sort_key в app/tasks/landing_showcase_deals.py), отбор по величине
ошибки был дефектом и снят. Лечится контекстом — под лентой печатается
тот же разброс ПОКАЗАННЫХ строк, что уже стоит под таблицей сверок:
медиана модуля и худшая, обе из shownSpread() (deal-view.ts), второго
расчёта не заводится. Вывода вида «зато обычно точно» в подписи нет: он
протух бы на первом пересчёте витрины, а два числа рядом — нет.

Разметка: бегущая часть выделена в .tickerStrip — position/overflow
нужны только ей, иначе абсолютный бейдж растянулся бы и на подпись.

Проверка (landing-v3-render.test.tsx): те же строки с худшей и без неё
дают РАЗНЫЕ числа в подписи — она сосчитана по показанному, а не вписана.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 14:54:52 +05:00
00f177df03 лендинг: имя боевого сервера убрано из пользовательской копии
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / 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 58s
«Симуляция пробы покрытия на боевой базе (poincare, 30.08.2026)» доезжала до
экрана: CITY_COVERAGE_SOURCE собирается в coverage-copy.ts и рендерится в
EstimateFlow.tsx:323. На первом экране лендинга строки не видно — она
появляется в форме проверки, после выбора города, поэтому grep по
отрендеренной главной давал ноль и дефект выглядел несуществующим.

Хостнейм посетителю не сообщает ничего: «на боевой базе» несёт весь смысл.
Убран из обеих строк-источников (сверка бэктеста и проба покрытия) и из
комментария в статьях.

Гейт смотрит в ИСХОДНИК, а не в вывод: какой путь рендера показывает строку
сегодня, знать не нужно — завтра он будет другим. Список запретов явный, без
эвристики «похоже на хостнейм»: она ловила бы domclick.ru и avito.ru в текстах
про источники данных. gendsgn.ru в список НЕ входит — это публичный домен B2B,
на который лендинг ссылается кнопкой «Для бизнеса».

Гейт проверен фальсификацией: возврат хостнейма даёт красное с указанием
landing-facts.ts:207, а не пустой список.
2026-08-30 14:53:05 +05:00
5f7ac0d50f витрина: печатать разброс показанных строк рядом с медианой всей сверки
All checks were successful
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 Trade-In / changes (pull_request) Successful in 9s
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m18s
Посетитель видел 20 строк сверки и не мог понять, где они лежат
относительно выборки: первой в таблице стоит сделка с расхождением
+75,7 %, а медиана по всем 327 — 14,5 %. Порядок строк при этом верный
и менять его нельзя — отбор идёт по полноте и свежести, а не по
величине ошибки (отбор по ошибке был дефектом и уже убран).

Поэтому не переупорядочиваем, а добавляем контекст: медиана модуля
расхождения показанных строк, сколько из них в пределах порога и
худшая — рядом с медианой всей сверки. Замер на проде 30.08.2026:
показанные 11,5 % против 14,5 % по всей выборке, 12 из 20 в пределах
20 %, худшая 75,7 %.

Считается на фронте из тех же объектов, которые рисует таблица, —
второе место подсчёта рано или поздно отстало бы от строк на экране.
Своей формулировки «чуть точнее» в подписи нет: она протухнет на
первом же пересчёте витрины, а два числа рядом не протухают.

Порог вынесен в WITHIN_PCT рядом с фильтром: гейт витринных чисел
справедливо покраснел на вписанных руками «20 %» при <= 20 в коде.
2026-08-30 13:54:22 +05:00
2eebc680a6 Merge remote-tracking branch 'origin/fix/audit-accuracy-window' into fix/audit-all
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / 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 4m55s
2026-08-30 00:14:23 +05:00
b343f84784 Merge remote-tracking branch 'origin/fix/audit-faq-contradiction' into fix/audit-all 2026-08-30 00:14:23 +05:00
bb4a6d220f fix(mera/b2c): увести к ответу проверки и показать ожидаемое качество по городу до нажатия
Экран проверки дорисовывал результат НИЖЕ формы и никуда не уводил: на 375 px
человек после нажатия видел ту же форму, а заголовок ответа оставался за нижней
кромкой — нажатие читается как «ничего не произошло». Ответ теперь получает
фокус и прокрутку; анимация прокрутки спрашивается у prefers-reduced-motion, той
же медиа-функции, что глушит остальную анимацию витрины. Фокус здесь не
украшение: без него клавиатурный пользователь остаётся на кнопке и следующим Tab
уходит в обход ответа, а живая область объявляет текст, но не перемещает точку
ввода.

Второе: в дропдауне девять городов, и они не равны по данным, но узнать об этом
можно было только ПОСЛЕ нажатия. Замер на проде (30.08.2026, симуляция когорты
самой ручки /coverage по случайным адресам активных объявлений, собственный
адрес исключён): доля проверок с выборкой не ниже городского порога — ЕКБ 83 %,
Верхняя Пышма 70, Серов 58, Нижний Тагил 52, Первоуральск 50,
Каменск-Уральский 45, Среднеуральск 39, Берёзовский 38, Ревда 16 (по 120
адресов, Среднеуральск — 56, столько их там есть). Величина и её источник лежат
в landing-facts.ts, формулировка — в coverage-copy.ts, в компонент не вписано
ни одного числа.

Города из списка НЕ убраны: систематического отказа нет ни в одном (пустая
когорта у худшего — 11 случаев из 100), разница между ними количественная, и её
честнее назвать числом, чем снятием опции. Счёт по listings.city, дающий ноль по
трём городам-спутникам, здесь не годится — колонка хранит город свипа скрейпера,
а не геокод объявления (разбор над _CITY_CENTROIDS_DEG в trade_in.py).

Тест требует замера на каждый город из OBLAST_CITIES — добавить город в дропдаун,
не измерив его, теперь нельзя.
2026-08-30 00:12:26 +05:00
50a3a29b72 fix(mera/b2c): блок точности называет то окно, которое измерено
Подпись обещала «сделки с июня 2025 года», а выборка бэктеста берётся
ORDER BY id DESC LIMIT :sample (backend/scripts/backtest_estimator.py,
_SAMPLE_SQL) — это последние по порядку загрузки строки, а не срез окна.
Проверка на проде 30.08.2026: у всех 327 сделок deal_date = 2026-04-01,
то есть один квартал; проверены оба варианта запуска (без --city и с
--city Екатеринбург) — результат одинаковый. Случайной выборки в скрипте
нет, поэтому чинится подпись, а не замер: числа те же, окно названо своё.

Заодно:
- доля выборки на витрине (5,5 % сделок квартала). Знаменатель — из ТОГО ЖЕ
  окна (5 954 годных сделки ЕКБ за II кв 2026), а не 24 333 за всё окно
  с июня 2025: доля от непокрытого окна повторила бы ту же ошибку;
- дата замера выведена рядом с числами: регулярного пересчёта у них нет,
  без даты они стареют молча;
- __tests__/backtest-freshness.test.ts краснеет, когда замеру больше
  BACKTEST_MAX_AGE_DAYS (100 дн. = квартальная пачка Росреестра + запас).
  Фальсифицирован: дата 2026-01-05 → красный с текстом «замеру 236 дн.».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 00:09:43 +05:00
808a2e12e2 fix(mera/лэндинг): убрать обещание про снятие объявлений и сроки без замера
FAQ обещал, что мы «отслеживаем снятие объявлений с публикации» и считаем по
этому расхождение прогноза с реальностью. Ни того, ни другого нет: расхождение
считают landing_showcase_deals.py и backtest_estimator.py, оба берут только цену
ДКП; delisted/relisted listing_source_snapshot.py не пишет намеренно (не выводимы
при покрытии обхода 10-35%), на проде 0 таких строк в listing_source_events,
deals.days_on_market заполнена 0 из 108 623. Ответ приведён к тому, что делается,
и прямо говорит, что снятие сделкой не считаем — двумя блоками выше AccuracyV3
по той же причине зовёт величину «экспозицией АКТИВНОГО объявления».

Два срока без источника убраны, а не заменены числом: «против двух месяцев вашей
жизни» (CostOfErrorV3) и «не зависли на полгода» (HeroV3). Измеренная экспозиция
считается по тем, кто ещё висит, и сроком продажи не является — подставлять её
на место этих сроков значило бы подменить величину.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 00:07:07 +05:00
66660bd366 fix(mera/b2c): на мобильном липкий хром съедал 42% экрана
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 / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m38s
CI / openapi-codegen-check (pull_request) Successful in 2m26s
CI Trade-In / backend-tests (pull_request) Successful in 5m28s
CI / backend-tests (pull_request) Successful in 17m44s
Жалоба «много всплывашек, экрана не видно» — это не всплывашки, а постоянный
хром: на 375×812 липкая шапка (235 px) и фиксированная нижняя панель (109 px)
вместе занимали 42% экрана и отъедали их при любой прокрутке. Замер тем же
способом (getBoundingClientRect по элементам с position fixed/sticky): было
366 px = 45,1% высоты экрана, стало 119,5 px = 14,7%. На 1280 — 135,5 px до и
после, без изменений.

Шапка на узком экране сведена к одной строке (лого + «Для бизнеса»):

  - навигация по секциям скрыта — четыре пункта переносились в две строки, а
    на мобильном к секциям всё равно доезжают прокруткой; «Статьи» и «МЕРА для
    бизнеса» остаются ссылками в подвале, «Продажа под ключ» и на десктопе
    неактивная заглушка;

  - CTA скрыт — он вёл туда же, куда нижняя панель, и два одинаковых призыва
    на одном экране это и есть жалоба; панель осталась, шапка уступила.

Селектор города (CityPicker) удалён совсем, а не спрятан: он не владел своим
выбором — состояние жило внутри компонента и никуда не отправлялось, — тогда
как гео-гейт расчёта стоит на городе из формы (FreeCheckCard, #2576). Два
места, задающих одно значение, из которых работает одно, — дефект сам по себе;
то же правило уже записано в InnerHeader. Граница покрытия, ради которой
выпадашку открывали, стоит текстом в герое.

Нижняя панель на узком экране — только кнопка: её строка переносилась на две-
три и делала панель втрое выше кнопки. То же обещание остаётся словами героя.

Шаг медиазапроса — 1000 px, а не общий для файла 720: на 720 шапка макета всё
ещё переносится в две строки (замерено: 109 px хрома), то есть возвращать её
там значило бы вернуть тот же дефект планшету.
2026-08-29 23:25:44 +05:00
a5ca2a326a feat(mera/b2c): в карточке игры схема улицы сделки, а не пятно района
Карта показывала ПОЛИГОН РАЙОНА — десятки квадратных километров, на которых
одинаково выглядят сделка у парка и сделка у ТЭЦ. Схема улиц уже приезжает
строкой витрины (`street_scheme`), рисовать её было нечем.

`StreetMapV3` — серверный компонент, инлайновый <svg>, НИ ОДНОГО внешнего
запроса: тайлы отдали бы провайдеру IP посетителя и то, что он смотрит, на
странице, которая обещает «без звонков и регистрации» (правило шапки
layout.tsx, по нему же в дереве self-hosted шрифты). Слои снизу вверх: плашка
и приглушённая сетка (уже были), вода, фоновые дороги толщиной по классу OSM,
целевая улица акцентом поверх мягкого ореола, подписи соседних улиц
моноширинным.

DealMapV3 ОСТАЁТСЯ и не тронут. Улица матчится у 92.1% строк витрины, у
остальных схемы нет — там по-прежнему район. Ветвление стоит в GuessGameV3,
чтобы обе карты остались тупыми: каждая рисует то, что ей дали, и ни одна не
подставляет вместо отсутствующих данных правдоподобное.

ЧТО КАРТА ГОВОРИТ О СЕБЕ. Подпись — «<УЛИЦА> · ДОМ НЕ ИЗВЕСТЕН». Адрес сделки
уровня улицы, номер дома известен у 2.7% сделок, координата окна — центроид
улицы. Слов «объект» и «адрес» в карточке нет; тест держит и то и другое.
Точку дома поставить нечем и по построению: в `street_scheme` нет ни констант
проекции, ни координат окна.

ЧЕГО НЕ РИСУЕМ. Зданий: `cad_buildings` — 18 307 контуров на город, в плотном
центре 51 здание на радиус 450 м, где их в разы больше; нарисованная застройка
заявляла бы полноту, которой нет. Улицы — фильтрованная выгрузка «источников
шума», дворовых и служебных проездов в ней нет, поэтому фоновые дороги
приглушены: это контекст, а не план квартала.

КАДР — `slice`, а не `meet`: окно квадратное, карточка нет, и `meet` оставил бы
поля по бокам. Обрезка безопасна ровно потому, что целевая улица лежит в ЦЕНТРЕ
окна по построению; обрезаются края с частью подписей соседей.

ДОСТУПНОСТЬ — как у DealMapV3, `aria-hidden`: всё, что схема сообщает, стоит
рядом текстом. `role="img"` заставил бы прочитать то же самое дважды, а
названия соседних улиц без их взаимного расположения ничего не значат.

ЦВЕТА — токенами: `--b2c-water` (вода отдельным оттенком, иначе склеивается с
дорогами) и `--b2c-muted-dark` (подписи на тёмной плашке: `muted` даёт там
3.2:1). Литералов в CSS не добавлено.

Подпись объекта лежит ПОВЕРХ карты, и карта теперь доходит до краёв — добавлено
затемнение под ней и плашка под подписью карты; без них белый текст пересекался
с подсвеченной улицей. Псевдоэлемент позиционирован, поэтому детям
`.gameMapBottom` задан `position: relative` — иначе затемнение красится поверх
текста и стирает его (поймано скриншотом, а не рассуждением).

Тесты (каждый сломан вручную и покраснел): кадр из данных и `slice`; ореол шире
линии и по одному на путь; иерархия дорог и незнакомый класс не дают нулевой
толщины; вода отделена; подпись называет улицу и говорит про дом; `aria-hidden`;
ветвление карточки в обе стороны.

Проверено глазами на dev-сервере, стаб — реальные строки прода плюс схемы,
собранные той же проекцией по геометрии gendesign: улица сматчилась; не
сматчилась (виден район); полей схемы нет вовсе (тот же район, без падения).
2026-08-29 23:25:44 +05:00
4e3279d448 fix(mera/b2c): студия подписывалась как «0-к» на витрине сделок
Увидел на скриншоте карточки игры: «0-к, 25,9 м²». Замер на проде — 3 строки
витрины из 20 имеют rooms = 0, то есть каждая шестая карточка так и выглядит.
«0-к» читается как ошибка выгрузки, а не как тип квартиры.

Студией её называет и наш собственный бэктест (per_rooms.label в
scripts/backtest_estimator.py), и рынок.

Тест двусторонний и фальсифицирован: возврат «0-к» для rooms=0 красит его по
значению, обратная правка — снова зелено.
2026-08-29 23:25:44 +05:00
272d807f55 feat(mera/b2c): в карточке игры настоящая карта вместо декорации — и подпись, которую данные выдерживают
Блок `gameMap` рисовал CSS-сетку и два прямоугольника-«дороги», а подпись
говорила «ОБЪЕКТ · РАЙОН» — то есть заявляла местоположение объекта. Географии
там не было вовсе.

Что сделано: `DealMapV3` — инлайновый <svg> по статике `ekb-districts.ts`, без
единого внешнего запроса (то же правило, по которому в этом дереве self-hosted
шрифты). Район сделки — заливка и обводка акцентом, соседние — контур; кадр по
bbox района с запасом, не по городу: в городском кадре район занимает несколько
процентов площади. Точка — по `project(lon, lat)` координаты витрины.

ПОДПИСЬ. Координата в данных — ЦЕНТРОИД УЛИЦЫ, не дом: 34 021 сделка, 34 017 с
координатой, различных точек 991, номер дома известен у 2.7% (прод, 29.08.2026).
Поэтому карта подписана «СЕРЕДИНА УЛИЦЫ, НЕ ДОМ · РАЙОН» — она произносит
границу вслух, а не оставляет читателю догадываться по размеру маркера. Слова
«объект» и «адрес» в карточке не появляются.

ТОЧКИ НЕТ — ДВА СЛУЧАЯ, оба дают карту с подсвеченным районом и без прочерка:
координаты нет; координата есть, но лежит вне кадра. Второе не теоретическое —
2 132 строки из 34 021 (6.3%) в той же выборке имеют координату за пределами
города, встречаются точки за сотни километров. Прижать такую к краю значило бы
показать место, которого в данных нет; подпись в обоих случаях — «КООРДИНАТЫ
НЕТ».

Доступность: <svg> aria-hidden, всё его содержание (район) стоит рядом текстом
дважды — в подписи карты и в мете сделки.

Гейт витринных чисел: из скана исключён сгенерированный `ekb-districts.ts` —
координаты полигонов не утверждают ничего о рынке. Исключение не бланковое:
добавлена проверка, что исключённый файл несёт маркер генератора.

Проверено глазами на dev-сервере с заглушкой бэкенда: три состояния (Кировский с
точкой, Чкаловский — кадр едет, сделка без координаты).
2026-08-29 23:25:44 +05:00
7b12b07935 feat(mera/b2c): границы районов ЕКБ статикой — основа настоящей карты в игре
Карточка игры «Сыграйте против МЕРЫ» показывает декоративный прямоугольник:
сетка и две «дороги», нарисованные CSS. Заменяем настоящей геометрией.

ПОЧЕМУ СТАТИКА, А НЕ ТАЙЛЫ. Публичное дерево mera-public держится без единого
внешнего запроса — шрифты self-hosted, Leaflet с unpkg сюда намеренно не тянули
(шапка layout.tsx). Тайловый провайдер получал бы IP посетителя и то, что он
смотрит, на странице, которая обещает «без звонков и регистрации». Границы
районов не меняются годами, раздавать их в рантайме незачем.

Что в коммите:
· backend/scripts/export_ekb_districts_svg.py — генератор. У данных обязано быть
  проверяемое происхождение: источник — боевая gendesign_ekb_districts_geom
  (OSM через FDW, читается с 29.08 — грант #3227), допуск упрощения и проекция
  названы в докстринге вместе с причиной выбора.
· _components/v3/ekb-districts.ts — сгенерированное, руками не править.
  8 районов, 7826 символов путей, плюс project() для точки (lon,lat).

Упрощение 0.0012° (~70-130 м) выбрано под масштаб карточки — она показывает
район целиком, где такая ошибка меньше толщины линии.
2026-08-29 23:25:44 +05:00
2e82b0810f fix(mera/b2c): снять с витрины обещание времени и подписать величины измеренным
All checks were successful
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 Trade-In / frontend-checks (pull_request) Successful in 57s
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI / openapi-codegen-check (pull_request) Has been skipped
«30 секунд» ушло из бейджа шага 1 как незамеренное, но осталось видимым
посетителю в четырёх других местах той же страницы: подпись под кнопкой формы,
CTA шапки, CTA блока возражений и липкая нижняя панель, которая висит на экране
всегда. Гейт витринных чисел этого не видел по построению — «сек» не было среди
единиц, а трёх файлов не было в списке сканируемых (обе дыры закрыты отдельно).
Замена — по смыслу: число полей формы (STEP1_FIELDS_LABEL) проверяется глазами,
и оба места берут его из landing-facts.ts, а не пишут у себя.

Медиана аналогов стояла под общей шапкой «пример по рынку Екатеринбурга», но
городом не ограничена: _ANALOGS_SQL идёт по всей таблице trade_in_estimates,
фильтр по city стоит только у экспозиции. Плюс Hero печатал у неё лишь размер
выборки и выбрасывал note «только расчёты, где аналоги нашлись» — отбор
выживших. Теперь у каждой величины своя подпись со своей границей и своя
оговорка рядом со значением; правило зафиксировано двумя проверками рендера
(обе краснеют на возврате к одной строке и на выброшенном note).

price_cut_share_pct считает долю ОБЪЯВЛЕНИЙ и только по Домклику — подпись
«столько продавцов снижают цену» приписывала величине единицу, которой её не
мерили. Один продавец ведёт несколько объявлений.

В ReportResult оставались три литерала «150 ₽» при живой SERVICE_PRICE_RUB:
цена обязана иметь один источник с офертой и политикой возврата.

Обещание «прогноз срока продажи» приведено к тому, что считается:
_estimate_days_on_market — медиана days_on_market аналогов, то есть сколько
похожие объявления УЖЕ висят. deals.days_on_market заполнен 0 раз из 108 623,
сверять прогноз срока не с чем (по этой же причине в ветке уже снято «±6 дн»).
2026-08-29 20:40:20 +05:00
b17a2a310c fix(mera/b2c): гейт витринных чисел ловит происхождение, а не форму записи
Ревьюер нашёл три дыры и воспроизвёл каждую на живом коде.

1. Сканер проверял ФОРМУ. Четыре маркетинговых утверждения, вписанные в
   DealsTickerV3, оставляли гейт зелёным 17/17: «9 из 10» (доля словами),
   «42700 квартир» (разделитель тысяч никакой + единица вне списка),
   «в 2 раза точнее» (кратность словами), «42 городах». Последнее — строка
   из таблицы расхождений СТАРОГО гейта, то есть новый пропускал ровно тот
   класс вранья, ради которого заводился. Добавлены четыре формы; эти же
   четыре строки внесены в контроль на инструмент — прежние шесть образцов
   повторяли то, что сканер и так знал, и покрытия не расширяли.

2. noindex перестал охраняться. `toContain("robots")` остаётся зелёным при
   `index: true` — проверено переворотом флага. Вернулась проверка значения
   (не слабее прежней `noindexIsOn()`), и не только в layout, но и в
   метаданных самой страницы, которые layout перекрывают.

3. Список файлов отставал от страницы: корень рендерил 15 компонентов,
   сканировались 11 — «возражения», шапка, подвал и sticky-CTA были вне
   охраны. Список больше не пишется руками: он выводится из каталога
   компонентов, а отдельная проверка требует, чтобы всё, что импортирует
   корень, попало в этот список (ловит компонент, положенный мимо каталога).

Гейт на этой ветке КРАСНЫЙ и красный по делу: расширенный список нашёл
«Проверить за 30 секунд» в HeaderV3, StickyCtaV3, ObjectionsV3 и
FreeCheckCard. Замера времени заполнения формы нет и не было — это записано
в landing-facts.ts и в StepsV3, откуда ту же фразу уже сняли. Правка текста —
вне участка этой задачи (файлы вёрстки правит соседняя ветка).
2026-08-29 20:39:00 +05:00
2c9b4ab547 feat(mera-public): витрина лэндинга v3 на замеренных числах
Литералы из дизайн-макета заменены величинами из /stats и /showcase, а то,
чему нет входа в данных, снято вместе с блоком.

Что заменено:
- ошибка «1,8%» → 14,5% (n=327) с двумя оговорками рядом: сравнение идёт с
  объявлениями, активными на момент расчёта, а сделка прошлая; цена ДКП
  бывает занижена ради налога;
- «83% в диапазон» → 88% (n=327) неразрывно с шириной коридора ±37% в
  подписи и с третьей плиткой: уверенность «низкая» у 325 из 327;
- «±6 дн. точность по сроку» СНЯТА — deals.days_on_market пуст 0/108 623; на
  её месте медианная экспозиция активного объявления, без ± и без «точности»;
- «42 700 проверок, из них 3 180 сверены» → estimates_total; «сверено» снято
  (контура сверки прогноза со сделкой клиента нет);
- «−4,3% за месяц» → price_cut_median_pct_per_month (среди снижавших, своя
  выборка); «×2,4 дольше» СНЯТА (замер даёт ×1,04) → price_cut_share_pct;
- «14 объявлений / 47 дн.» → analogs_median и listing_age_median_days, прямо
  помеченные как пример по рынку, а не расчёт по адресу посетителя;
- 5 выдуманных строк сверок и 3 раунда игры → реальные сделки /showcase
  (район+комнаты+площадь+этаж, квартал вместо дня) с подписью прогона;
- цена во всех местах — SERVICE_PRICE_RUB из content.ts.

Отказ ручки стоит блока: пустой /stats снимает плитку, пустой /showcase —
таблицу, ленту и игру. Ни нуля, ни прочерка, ни прошлого значения.

Гейт #2904 переписан: он охранял «плейсхолдеры существуют» и покраснел бы по
построению за их снятие. Новый охраняет происхождение чисел — сканирует
компоненты на вписанные руками величины и проверяет заполненность записей
landing-facts.ts. Фальсифицирован: вписанное в AccuracyV3 «1,8% по 42 700
проверкам» роняет гейт тремя формами сразу. Сканер прикрыт контролем на
образцах — он же поймал уже существовавшую «150&nbsp;₽» в FreeResultV3.

robots/noindex не тронуты.
2026-08-29 20:37:58 +05:00
f612401202 feat(mera-public): серверный слой витринных чисел лэндинга
Загрузка /api/public/mera/stats и /showcase для серверных секций лэндинга:
типы обеих ручек, честная деградация и форматирование величины вместе с
размером выборки.

Любой сбой (сервис недоступен, 500, не-JSON, пустой ответ, нет ключа) даёт
«величины нет», а не ноль: /stats отдаёт пустой набор, /showcase — null,
и ни одна из функций не бросает, чтобы отказ ручки стоил блока, а не всей
страницы. Число выходит наружу только через formatStat — вместе с sample_n
и note, поэтому потерять размер выборки по дороге в JSX нельзя.

Адрес серверного fetch — BACKEND_URL, тот же путь, которым Next уже ходит в
backend (rewrites в next.config.ts, 'internal SSR' в prod-компоуз), поэтому
без /trade-in: префикс добавляет Caddy для браузера, не бэкенд. Кэш —
revalidate 3600: пересчёт ночной, no-store жёг бы rate-limit ручки на всех
посетителей, кэш без срока показывал бы вчерашнее до деплоя.

Тесты двусторонние и фальсифицированы: подстановка нуля вместо null роняет
5 из 17, пустая витрина вместо null — 3 из 17.
2026-08-29 20:35:27 +05:00
49652f4413 fix(mera/b2c): вернуть потерянные вопросы FAQ, починить адрес превью и честность двух комментариев
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 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
Правки по трём ревью ветки «v3 становится корнем».

FAQ. Из шести вопросов `content.ts` возражения v3 брали два, остальные
четыре (`how-accurate`, `vs-marketplace`, `personal-data`, `bank-report`)
не рендерились нигде: `Faq.tsx`, который выводил все шесть, удалён вместе
с лэндингом v1. Макетные возражения их тематически закрывали, но
пересказом — а оригиналы проходили юр-ревью. Содержательно пропали два
утверждения: «Имя, паспорт и документы на квартиру мы не спрашиваем» с
отсылкой к странице ПДн (152-ФЗ) и «показываем диапазон, а не одно число»
как прямой ответ про точность (рамка диапазона осталась только в
`AccuracyV3`, рядом с плейсхолдерными числами).

Теперь в секции один порядок чтения: все шесть вопросов из `FAQ` по id
плюс три макетных возражения, которым в `FAQ` пары нет («факт сделки»,
«почему так дёшево», «подписка»). Три макетных пересказа удалены в пользу
оригиналов; у «почему так дёшево» убран второй абзац — почти дословный
повтор второго абзаца `how-accurate`, который теперь стоит следующим.
Новых формулировок не добавлено. Отсылка к странице ПДн стала живой
ссылкой через `PublicLink` (обычный `<a>`, basePath не течёт); текст
ссылки — слова самого `content.ts`, найденные по вхождению.

Гейт стал двусторонним: тест перебирает `FAQ` и требует, чтобы КАЖДЫЙ id
был отрисован. Прежняя версия перечисляла два id руками и потому молчала
ровно в тот момент, когда четыре ответа исчезли со страницы. Проверено
мутацией: снятие вопроса, переименование id и переформулировка фразы про
ПДн красят тест по отдельности.

CADDY. `/trade-in/mera-public/v3` выбыл из @meraLongPages вместе со
страницей и никуда не попал — длинный адрес превью падал в catch-all 404,
хотя раньше вёл на страницу. Обе его формы добавлены в @meraV3Gone. Там же
редирект перестал терять query: было `redir * /`, стало `redir * {uri}`
со срезанием пути — как у @meraLongPages, где перенос UTM и обоснован.
Проверено на живом Caddy 2.11 по настоящему site-блоку: все четыре формы
дают 301 на `/` с сохранённой query, без параметров — чистый `/` без
хвоста `?`, соседние матчеры не задеты, лишний сегмент по-прежнему 404.

КОММЕНТАРИИ, ОПИСЫВАЮЩИЕ НЕСУЩЕСТВУЮЩЕЕ. Строка `page.tsx` в гейте
плейсхолдеров убрана: комментарий над ней утверждал, что устаревший путь
«перестал бы что-либо сканировать», но ни в корневом `page.tsx`, ни в
прежнем `v3/page.tsx` нет ни одного имени из `PLACEHOLDER_EXPORTS` —
страница только собирает секции. Замер подтверждает: множество найденных
плейсхолдеров со строкой и без неё совпадает, а сама по себе она не даёт
ничего. Держать её значило держать ложное ощущение охвата.

Дескриптор «Оценка вторичного жилья по рыночным данным · <регион>» на
внутренние страницы не возвращается — причина записана в докстринге
`InnerHeader`: эту шапку носят и `/estimate`, `/articles`, `/docs`, где
дескриптора не было никогда; требование юриста было про первый экран
лэндинга и там выполняется `HeroV3`; на самих юр-страницах то же сказано
сильнее — в тексте оферты (п. 1.3, 6.2) и в `FooterV3` на каждой странице.
2026-08-29 19:35:57 +05:00
6976ca8915 feat(mera/b2c): лэндинг v3 становится корнем публичного домена
Вариант дизайна выбран — превью с /mera-public/v3 переехало в корень
целиком, вместе с блоками, перенесёнными из v1 предыдущим шагом. Каталог
v3/ и компоненты v1 (Hero / HowItWorks / WhatYouGet / DataSources / Faq /
AddressForm / SiteHeader / SiteFooter) удалены: их данные живут в
content.ts и уже читаются секциями v3, поэтому ушла только вёрстка.

Юридические страницы (оферта, возврат, ПДн) держались на SiteHeader/
SiteFooter — единственные, кто их ещё импортировал. Переведены на
InnerHeader + FooterV3, тем же способом, что /estimate, /articles и /docs.

ПЕРИМЕТР. /v3 убран из @meraPages, @meraShortSlash и @meraLongPages, но
не выброшен: адрес две недели был открыт наружу и разошёлся по закладкам,
поэтому вместо тишины белого списка — 301 на корень (@meraV3Gone, обе
формы). На это заведена проверка в public-perimeter.test.ts по образцу
соседних; PREVIEW_ONLY стал пустым, а не удалённым — он держит проверку
«наружу не открыто ничего сверх известных страниц» двусторонней.

Из PUBLIC_SHORT_PATHS гварда /v3 убран: редирект отрабатывает в Caddy, до
приложения запрос не доходит, а недостижимая строка в списке публичных
путей сделала бы публичной будущую страницу с тем же адресом. Гейт
Caddy→гвард односторонний и сам бы этого не поймал — обоснование в коде.

NOINDEX ОСТАВЛЕН. Он держится за плейсхолдерные числа marketing-v3.ts
(#2904), а не за то, какой вариант дизайна стоит в корне. Путь в гейте
обновлён на page.tsx: placeholdersInUse() молча пропускает ненайденный
файл, поэтому устаревшая строка не покраснела бы, а перестала бы
сканировать.
2026-08-29 18:59:43 +05:00
d4e106882c feat(mera/b2c): перенести потерянные блоки v1 в лэндинг v3
Корень meraocenka.ru отдаёт v1, новый дизайн живёт на /v3 и скоро станет
корнем. Шесть вещей из v1 в v3 отсутствовали — при переключении они бы
просто исчезли с публичного сайта:

- реестра источников не было вовсе (SourcesV3, из SOURCE_GROUPS);
- «что вы получите» и дисклеймера 135-ФЗ не было (WhatYouGetV3): о том,
  что это не отчёт аккредитованного оценщика, узнавал только тот, кто сам
  догадался открыть нужное возражение;
- герой не называл границу покрытия (полное — Екатеринбург, частично —
  остальные города области, по другим регионам не считаем) и не говорил,
  что мы не покупаем квартиры и не берём их на продажу;
- два вопроса FAQ («откуда вы знаете, сколько стоит именно моя квартира»,
  «почему только Свердловская область») в возражениях v3 не звучали —
  теперь берутся из FAQ по id, а не копией строк;
- карточка проверки не спрашивала город: гео-гейт #2576 на новом лэндинге
  был сломан (без города «Ленина, 1» уходит в одноимённую улицу ЕКБ);
- она же спрашивала состояние обязательным полем и выбрасывала ответ на
  переходе — теперь он доезжает в черновике.

Тексты перенесены дословно (проходили юр-ревью), новых чисел нет.
Новые секции встали после «Точности»: сверяем прогноз с фактом → на каких
данных → что получаете на руки, всё до первого разговора про деньги.

Гейт: __tests__/v3-ported-copy.test.ts — проверен мутацией (снятие секции
со страницы и переименование id вопроса красят его).
2026-08-29 18:52:09 +05:00