Спецификация v1: паритет «По любви» Android с макетом Figma (.fig) и Flutter-приложением

Версия 1.2 · 08.09.2026 · один автор и владелец документа — исполняющий агент; заказчик решений — владелец продукта. Заменяет docs/FLUTTER_PARITY.md как порядок работы и критерии приёмки: тот чеклист ставил ✅ по 44 пунктам, из которых паритет по дизайну, текстам и поведению выдерживают 4 (сверка 08.09, раздел 8.1). Отчёт сверки — https://salam0nn.ru/artifacts/polyubvi-parity.html — остаётся справочником и первым реестром расхождений, но подчинён ноде и коду Flutter (К-1).

Документ живой: меняется после каждой фазы, каждое изменение — запись в разделе 12. Замороженная спека хуже отсутствующей: ей верят.

Сестринский документ — android-events/docs/FIG_PARITY_SPEC.md v3.1 («По душе»). Метод, конституция и ритуал перенесены оттуда дословно там, где предмет совпадает; отличия «По любви» — три источника истины вместо одного (нода, Flutter, arb), готовый бэкенд вместо фейков и уже существующее приложение, которое нужно не дописать, а привести в соответствие.


0. Мандат (слова заказчика, обязательны дословно)

Ты обязан абсолютно строго и агрессивно проверять всё. Ты обязан читать, изучать и размышлять всегда полностью самостоятельно, не использовать код и субагентов, т.к. это может являться недоверительным источником информации за счёт сужения задачи и отсечения неосознанно важных элементов, что в будущем приведёт в тупик. Мне неважно, сколько времени это займёт и сколько токенов будет потрачено. Мне важен результат, и результат должен быть абсолютным. Если у тебя будут проблемы с доступами — это исключительно твои проблемы: все ключи имеются как к гиту, так и к моему ноуту. […] Информация должна быть вся — от самой мелкой до самой значимой и незначимой.

Изучи в интернете, как составляются спецификации для разработчиков. Вот одна из наших спек [FIG_PARITY_SPEC v3 «По душе»] — перенести характер и требования к агенту как там.

Мандат «По душе» действует здесь в той же силе: «буквально брать каждый экран нашего приложения, смотреть его каждое состояние и сверять его с Figma. Задачу нельзя сужать до чего-то меньшего — выполнять в полном объёме. Всегда сам себя перепроверять. Задавать себе множество вопросов „как, почему, зачем, откуда, для чего“ и идти выяснять ответы. Результат должен быть абсолютным: в точности как в Figma».

Всё ниже — развёртка этих слов в проверяемые правила.


1. Конституция агента

Правила К-1…К-14 действуют на каждом шаге каждой фазы. Каждое сопровождается причиной (почему оно есть) и способом проверки (как узнать, что оно соблюдено). Нарушение любого — задача не закрыта, независимо от результата.

К-1 · Три источника истины и их иерархия

Правило. У «По любви» три источника, и у каждого — своя область. Смешивать области запрещено.

Область Источник истины Где лежит
Форма: размеры, координаты, цвета, прозрачности, скругления, порядок и видимость слоёв, шрифты, ассеты, варианты компонентов дерево ноды в .fig /root/figma/polyubvi-2026-09-08.fig (md5 74083b20631708ad5259ed4b72dde44c), дампы docs/figma/fig/<page>.json
Текст интерфейса: подписи, заголовки, кнопки, подсказки, ошибки characters ноды; там, где у ноды нет такого состояния, — ключ из app_ru.arb .fig → backend/po-lyubvi-flutter/lib/l10n/app_ru.arb (999 строк)
Поведение: что происходит по событию, какой запрос уходит, с какими полями, какой ответ во что превращается, коды ошибок, лимиты, таймеры, кэш, WebSocket, диплинки, аналитика код Flutter backend/po-lyubvi-flutter/lib/features/**, lib/api/**, lib/core/** (снапшот 29.06.2026, 359d99c, v1.4.2+128)
Доказательство совпадения формы настоящий рендер кадра из Figma ×3 docs/figma/fig/renders/<guid>.png (Р-1)
Реестр расхождений на старте отчёт сверки 08.09 https://salam0nn.ru/artifacts/polyubvi-parity.html, раздел 8.2
Не источник docs/FLUTTER_PARITY.md, NATIVE_ANDROID_SUMMARY.md, REMAINING_PLAN.md, CLIENT_SCOPE_PLAN.md, комментарии в коде Android доказательной силы не имеют; читаются как история

Иерархия при конфликте: нода → arb → Flutter-код → отчёт. Текст ноды и текст arb расходятся (например, кнопка гео-шага «Поделиться геопозицией» в ноде против «Далее» в locationDeterminationShareLocationButton) — верна нода, расхождение записывается в раздел 4 (Р-2), реализация ждёт решения только если владелец уже отменял это правило для похожего случая. Поведение Android, которого нет во Flutter, не является поведением продукта (см. 8.5), кроме расширений plibvi (S66–S71, Р-5).

Каждое число, цвет, строка и вызов API в коде обязаны иметь адрес: guid ноды (sessionID:localID, совпадает с id из URL Figma), ключ arb или файл:строку Flutter. Число без адреса — выдумка.

Поправка про геометрию (унаследована из «По душе», Ф0-1). В .fig у детей авто-лейаута хранится размер «как было в момент последней записи», а настоящий Figma считает при отрисовке (в файле «По душе» так расходились 26 % узлов). Положение и размер берутся из absoluteBoundingBox API-дампа того же файла (Р-1), всё остальное — из .fig. Пока API-дампа нет, геометрия из .fig помечается geomSrc: "fig" и считается непроверенной до сравнения с рендером.

Почему. Сверка 08.09 показала, что Android строился по прозе чеклиста и по памяти о Flutter: заголовки «Пол и рост» вместо «Твой пол и рост?», чёрный пузырь вместо терракотового, поле с подписью снаружи вместо input--line. Ни одно из этих значений не имело адреса в ноде. Там, где Flutter отступает от макета (раздел 8.4), Android не должен копировать отступление второй раз.

Проверка. У каждого элемента в карточке сверки (7.4) заполнены колонки «нода → значение» и «Flutter → поведение». Пустая колонка = нарушение.

К-2 · Сам, руками, глазами

Правило. Всю работу выполняет сам агент: чтение дерева и кода Flutter, вёрстка, съёмка, сравнение, трассировка запросов, вердикт. Субагенты, фоновые агенты и делегирование не используются ни для чего. Скрипты допустимы как инструмент измерения и рутины (разбор .fig, экспорт ассетов, подсчёт пикселей, снятие снимка, чтение HTTP-лога) при трёх условиях: агент прочитал код скрипта целиком, проверил его выход на одном примере вручную (открыл картинку, посчитал число сам, сверил лог с кодом) и записал эту проверку в карточку. Вердикт «совпадает / не совпадает» выносит только агент, посмотрев на кадр, на цифры и на лог. Почему. Субагент и скрипт могут ошибаться и не знают, что ошибаются; ответственность не делегируется. Чеклист FLUTTER_PARITY.md — пример того, как «функция есть» превращается в ✅ без единого измерения. Проверка. В журнале каждой задачи есть фраза «проверил вручную: …» с конкретным примером. Вердикт написан от первого лица с описанием того, что агент увидел.

К-3 · Полный объём

Правило. Задача — это все кадры экрана из индекса (раздел 2) и все состояния этого экрана во Flutter (состояния bloc, ветки ошибок, коды ApiException, лимиты), а не «основное состояние». Закрыть задачу можно только когда каждый кадр прошёл ритуал раздела 7 и каждая строка поведенческой карты (7.2б) доказана трассой (6.5). Если часть невозможна (нет решения владельца, нет ручки на стенде), агент доводит всё остальное до конца, а невозможное записывает как блокер с доказательством невозможности и точной формулировкой того, что нужно от владельца. Почему. Android закрывался по «основному состоянию»: у ленты есть свайп, но нет счётчика, лимитов, возврата, видео, обучения скроллу; у чата есть отправка, но нет времени сообщения, редактирования, удаления, статуса собеседника. Заказчик прямо запретил сужение. Проверка. В карточке экрана перечислены все guid кадров из индекса и все состояния из Flutter, у каждого — статус. Нет строки «остальные аналогично».

К-4 · Протокол вопросов

Правило. Перед вёрсткой каждого экрана и после неё агент письменно задаёт себе и отвечает на вопросы. Обязательный минимум — раздел 7.3 (26 вопросов). Ответ «не знаю» допустим только с планом выяснения и результатом выяснения ниже. Ответ «очевидно» запрещён: очевидное записывается словами. Почему. Каждое найденное расхождение предотвращалось одним вопросом: «откуда это число?», «какой arb-ключ у этой строки?», «какой запрос уходит по этой кнопке во Flutter и с какими полями?», «почему в ноде красные флаги сверху, а у меня зелёные?». Вопрос, который не задан, не имеет ответа. Проверка. Карточка сверки содержит заполненный протокол. Пустой пункт = задача не закрыта.

К-5 · Двойная самопроверка

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

К-6 · Ничего не выдумывать

Правило. Если в ноде нет данных, если нода и arb расходятся, если Flutter делает что-то, чего нет в макете, или макет противоречит сам себе — агент не выбирает «разумный вариант». Он пишет маркер [ТРЕБУЕТ РЕШЕНИЯ: <точный вопрос с guid / ключом arb / файлом Flutter и двумя вариантами>] в раздел 4 и в карточку, продолжает всё, что от решения не зависит, и останавливает только зависимую часть. Почему. Каждая догадка — это отклонение, которое потом ищут часами: «Быстрый выбор» флагов, PriorityCard «Усиль анкету», кнопка «Войти» на OTP, обязательное поле «О себе» — всё это чьи-то «разумные варианты». Проверка. Все FIGMA-CHECK и FLUTTER-CHECK в коде имеют пару в разделе 4. В коде нет значений с комментарием «примерно», «похоже», «на глаз», «как во Flutter» без ссылки на файл.

К-7 · Красный diff и пустая трасса — не закрыто

Правило. Доказательство соответствия формы — только сравнение картинок: скриншот экрана приложения на устройстве 390×844 dp в ×3 против рендера ноды в ×3, выровненные по верхнему инсету, с числовым результатом (раздел 6). Доказательство соответствия поведения — только трасса (6.5): лог запросов/ответов/WS-кадров с устройства, сопоставленный строка в строку с поведенческой картой из Flutter, плюс снимок результирующего состояния. Пока сравнение красное или в трассе есть строка без доказательства, задача открыта. «Совпало визуально» и «работает» без цифр и логов не принимаются. Почему. Заявленное в чеклисте «Thread + WS send ✅» скрывало отсутствие времени сообщений и статуса собеседника; «Subscription + payment URL ✅» — отсутствие TYP, pending, failed и продления. Проверка. В карточке лежат скриншот, рендер, diff и число; для поведения — файл трассы с отметками ✓ у каждой строки карты.

К-8 · Журнал с доказательствами

Правило. После каждой задачи — запись в docs/figma/PROGRESS.md по шаблону раздела 9 и карточка сверки в docs/figma/cards/<guid>.md. Запись содержит только факты, которые можно перепроверить: guid, ключи arb, файлы Flutter, числа, команды, хеши коммитов. Слова «готово», «совпадает», «исправлено» без файла-доказательства запрещены. Почему. FLUTTER_PARITY.md и REMAINING_PLAN.md состоят из «✅» без единого файла-доказательства; проверить их можно было только повторной сверкой всего приложения, что и пришлось сделать. Проверка. Каждое утверждение в записи имеет ссылку на файл, guid или ключ.

К-9 · Границы кода

Правило. Изменения только внутри android/. Строки — только app/src/main/res/values/strings.xml, дословно из ноды или arb (включая «ё», пробелы, многоточия и кавычки-ёлочки), имя ресурса = ключ arb, если он есть. Цвета — только токены PlColors, значения из ноды (с альфой там, где нода задаёт альфу: Powdery-50a над фото — это 50 %, а не #F9F3F2). Типографика — только PlTypography, стили из UI Kit. Ассеты — только из .fig (сверить 139 скопированных из Flutter SVG с векторами мастер-компонентов страницы UI Kit; расхождение — заменить). Ни один видимый элемент управления не строится напрямую из androidx.compose.material3 (Button, OutlinedTextField, Checkbox, Switch, Slider, RangeSlider, AlertDialog, ModalBottomSheet, DropdownMenu, SegmentedButton, LinearProgressIndicator) вне пакета ui/kit/ — только через компонент кита, воспроизводящий ноду UI Kit. Исключение по ноде: dialog--android (Material 3) для подтверждений, где макет сам рисует Material-диалог. Нет ручки на бэке или стенде — // BACKEND-GAP: <ручка, тело, ответ по Flutter> и запись в раздел 4. backend/, ios/, android-events/, web* не трогать. Токены и ключи не коммитить. Почему. Сверка 08.09: все 60 с лишним текстов Android живут в Kotlin-константах, все поля — Material, чекбоксы и свитчеры — Material, диалоги — Material. Это и есть «другое приложение». Проверка. grep -rn '"[А-Яа-яЁё]' app/src/main/java/ru/polyubvi/app/ui пуст; grep -rn 'androidx.compose.material3.\(Button\|OutlinedTextField\|Checkbox\|Switch\|Slider\|RangeSlider\|AlertDialog\|ModalBottomSheet\|DropdownMenu\|SegmentedButton\|LinearProgressIndicator\)' app/src/main/java/ru/polyubvi/app/ui --include=*.kt | grep -v '/ui/kit/' пуст; git diff --stat не выходит за android/.

К-10 · Сборка и тесты зелёные всегда

Правило. Перед каждым коммитом: ./gradlew :app:assembleDushaDebug :app:lintDushaDebug :app:testDushaDebugUnitTest — все зелёные (JDK 17, SDK 35). Один коммит — одна задача, сообщение по формату репозитория, в теле — guid нод, ключи arb, файлы Flutter и ссылка на карточку. Таблица кодов ошибок (Г-10) покрыта unit-тестом ErrorMappingTest: все коды 101…122, 400, 404, 429, таймаут → ожидаемый текст. Проверка. Вывод команд в записи журнала.

К-11 · Время и токены не ограничены

Правило. Агент не ускоряется за счёт глубины. Если проверка требует открыть 40 нод и 12 файлов Flutter — открыть 40 нод и 12 файлов. Если ритуал нужно повторить пять раз — повторить пять раз. Единственный критерий остановки — раздел 2. Почему. Заказчик: «мне неважно, сколько времени это займёт и сколько токенов будет потрачено; результат должен быть абсолютным».

К-12 · Читать этот документ перед каждой задачей

Правило. Перед началом каждой задачи агент перечитывает разделы 1, 6 и 7 целиком и раздел 4 (открытые решения). Перед началом каждой фазы — весь документ. Проверка. Первая строка записи журнала задачи: «Перечитал К-1…К-14, разделы 6, 7, 4 в версии <номер>».

К-13 · Не копировать чужие ошибки

Правило. Flutter — источник поведения, но не источник его дефектов. Раздел 8.4 перечисляет 19 известных отступлений Flutter от макета и от здравого смысла; ни одно из них не переносится в Android. Столкнувшись с местом, где Flutter расходится с нодой, агент проверяет 8.4, при отсутствии там — пишет [ТРЕБУЕТ РЕШЕНИЯ] (К-6) и по умолчанию делает как нода. Собственные изобретения Android (8.5) удаляются, а не «сохраняются, раз уже написаны». Почему. Иначе паритет с Flutter будет означать перенос DeletePairConverter, который любую причину разрыва пары превращает в «Мы встретились лично». Проверка. В карточке экрана есть строка «8.4/8.5: проверено, найдено: …».

К-14 · Данные пользователя неприкосновенны

Правило. Каждый запрос на запись (PATCH /users/profile, PATCH /users/profile/settings, PATCH /matching/recalculate, POST /users/profile/position и все остальные) отправляет ровно те поля и ровно в тех случаях, что и Flutter (PersonalDetailsRequestDto собирается из изменённых полей, а не из дефолтов). Ни одно значение по умолчанию («Пользователь», «—», male, 1995-01-01, 170, «Пока без описания», «Не указано») не может уйти на сервер вместо данных пользователя. Дата рождения и пол после регистрации не редактируются (нода Auth 9.1: «Исправить дату рождения будет нельзя»). Флаг видимости шага анкеты (show) берётся из правил Flutter (шаг «Стабильность» скрыт), а не ставится true. Перед первым запуском любой записи на стенд с реальными данными агент показывает в карточке тело запроса, полученное из трассы, и сверяет его с DTO Flutter поле в поле. Почему. ProfilePatchBuilder сейчас шлёт полный PATCH с дефолтами; редактируемые пол и дата; show=true для всех шагов; ответы вложенных вопросов не собираются. Это не расхождение дизайна — это порча данных пользователей, общих с iOS. Проверка. Раздел 8.3 закрыт построчно с трассой на каждую строку; ErrorMappingTest и ProfilePatchTest (тело PATCH при изменении одного поля содержит только это поле) зелёные.


2. Цель и определение готовности

Цель. Каждый кадр каждого рабочего раздела макета присутствует в приложении и неотличим от макета при сравнении по правилам раздела 6, а каждое поведение каждого экрана Flutter воспроизведено и доказано трассой по правилам 6.5. Расширения plibvi (S66–S71) собраны из компонентов кита и не противоречат ноде там, где встраиваются в её экраны.

Рабочие страницы и объём. Кадр — узел типа FRAME, SYMBOL или INSTANCE шириной ровно 390, лежащий непосредственно на канве рабочей страницы (в файле «По любви» секций-контейнеров нет: секции обозначены рамками-заголовками «Nn» с виджетом статуса, а кадры лежат на канве рядом). Числа ниже получены 08.09 одним путём — подсчётом по текстовому дампу .fig — и потому предварительные: в Ф0-1 индекс строится заново двумя независимыми путями (разбор .fig и дерево Figma API, Р-1) и совпадение поимённо записывается в журнал, как это было сделано для «По душе» (там первый счёт 490 оказался 623).

Страница guid страницы (по API 08.09) Кадров 390 Из них не экраны (по имени) Секции макета и их статус
Онбординг 644:5049 132 0 Авторизация, Данные о себе, Анкетирование, Медиа, Верификация — Готово
Главная 2265:24270 109 0 Анкеты, Фильтры, Подписка — Готово; Прототипы — анимации
Профиль 2426:31988 47 0 Основной, Анкета, Модерация фото, Настройки — Готово
Симпатии 7761:36382 19 0 Кому понравилась анкета, Скролл лайков — Готово
Чаты 7047:32641 36 0 Все чаты и симпатии, Переписка — Готово
Доработки 8831:25341 7 0 Обучение, Подписка — Готово
V3 10129:6551 44 1 («комментарий») Медиа, Фильтры, Статус активности, Твои пары — Готово; Видео + избранное — В работе
V4 12894:28443 64 19 (11 «комментарий», 4 «Tag», 4 плашки 390×36/52) Обновление и тех. работы, Реферальная программа, Сообщества — Готово; Онбординг — В работе
V5 16970:46438 39 0 Изменить селфи, Чаты — Готово; Сообщества — В работе
Итого 497 20 477 кадров-экранов (предварительно)

UI Kit (страница 644:5051 «UI Kit», 15 секций: System Elements, Input, Atoms, Button, Selectors, Snack, Graphic, Navbar, Loader, & other components, Primitives, Cells, Icons FOR DESIGN, Icons FOR FLUTTER TYPE, Development) — объём Ф2: каждый компонент со всеми вариантами. Страницы «📈 Развитие», «Wireframing», «DSGN — concept», «Прототипы», «Presentations», «Сторы», «Internal Only Canvas», «Cover» — не объём работы (черновики, маркетинг, системные шаблоны). Секции «В работе» (Видео + избранное V3, Онбординг V4, Сообщества V5) — объём только по решению владельца (Р-4); до решения их кадры в индексе помечены wip.

Кадры выше 844 (874, 880, 908, 932, 964, 1224, 1373, 1905, 2274, 2868, 3140) — прокручиваемое содержимое; сравниваются по частям с прокруткой (6.3). Кадры-дубли (INSTANCE без переопределений) — через мастер, отдельного снимка не требуют.

Экраны. Кадры сгруппированы в 71 логический экран S01–S71 (раздел 8.1) по отчёту сверки. Экран — единица планирования; кадр — единица приёмки.

Готово (DoD) — все пункты для каждого кадра и каждого экрана:

# Критерий Метод проверки
Г-1 Каждый видимый слой ноды имеет соответствие в приложении: тот же тип (текст, картинка, фигура), та же вложенность, тот же порядок отрисовки. Скрытые слои (visible=false) отсутствуют. Системные слои макета (Status Bar - iPhone, home-indicator--iOS, Keyboard) не воспроизводятся, а заменяются системным хромом Android и исключаются маской (6.4). Инспекция дерева против дерева Compose, карточка 7.4
Г-2 Геометрия: положение и размер каждого элемента совпадают с нодой с отклонением не больше 0,5 dp (1,5 px при ×3). Измерение по скриншоту ×3, раздел 6.3
Г-3 Цвета и прозрачности совпадают точно (hex и alpha из ноды), включая заливки, обводки, тени, градиенты, размытия (bgblur). Инспекция токенов + пипетка по скриншоту в трёх точках на элемент
Г-4 Тексты совпадают побайтно с characters ноды (или с arb там, где состояния нет в ноде), стиль совпадает (семейство, начертание, кегль, интерлиньяж, трекинг, регистр, выравнивание, перенос, число строк). Данные-примеры ноды («Всеволод», «+7(910)436-30-78», «78») воспроизводятся фикстурой (Ф0-5), а не зашиваются в UI. Сравнение строк + измерение блока текста
Г-5 Ассеты (иконки, иллюстрации, брендовые формы shape_1…5, фото фикстуры) взяты из .fig, а не заменены похожими. Список ассетов экрана с sha1 источника
Г-6 Состояние воспроизведено на устройстве с теми же данными, что в ноде (фикстура Ф0-5), после завершения анимаций и загрузок. Скриншот
Г-7 Сравнение скриншота с рендером ноды по правилам раздела 6 зелёное. Число diff в карточке
Г-8 Переходы работают как во Flutter-роутере (auto_route, lib/navigation) и как в коннекторах макета; расхождение между ними — Р-2/раздел 4. Список переходов в карточке
Г-9 Поведение: каждая строка поведенческой карты экрана (7.2б: событие → bloc-событие Flutter → запрос/WS-кадр с полями → ответ/код → состояние → UI) воспроизведена и доказана трассой (6.5). Файл трассы с ✓ на каждой строке
Г-10 Ошибки и лимиты: коды ApiException 101…122, 400, 404, 429, таймаут маппятся на тексты arb как во Flutter (ErrorDisplayer, SubscriptionErrorDisplayer, CommunitiesErrorDisplayer, коды медиа 106/109/113, верификации 107/108/115, реакций 110); лимиты (6 фото / 10 МБ, видео 10 с / 50 МБ, записка 120→по Р-2, флаги 7×30, открывашки 3×120, интересы 10, «О себе» 2000, должность 50) — как во Flutter. ErrorMappingTest + трасса на каждый лимит
Г-11 Данные: запросы на запись содержат только поля, изменённые пользователем (К-14); ничего из раздела 8.3 не осталось открытым для этого экрана. Тело запроса из трассы, ProfilePatchTest
Г-12 В коде экрана нет FIGMA-ASSET, FIGMA-CHECK, FLUTTER-CHECK, CONTENT-GAP, Material-компонентов вне ui/kit/ (К-9), строк-констант с кириллицей. grep

Готовность проекта: все кадры индекса с Г-1…Г-12, все [ТРЕБУЕТ РЕШЕНИЯ] закрыты владельцем, раздел 8.3 закрыт построчно, финальный docs/figma/REPORT.md (9.3) собран, полный повторный проход разделов 6 и 6.5 по всем кадрам и всем поведенческим картам зелёный.

Не-цели. Серверная логика и backend/; ios/; android-events/ («По душе»); web*; тёмная тема (не нарисована и во Flutter отсутствует); бейджи на таббаре (в макете только заметка); реальные SMS, платёжные ключи, ESIA/ФССП, SpeechKit — внешние блокеры из EXTERNAL_BLOCKERS.md остаются внешними, клиентская часть при этом делается полностью; пиксельное сравнение живого видео и системных диалогов (6.4).


3. Входы и инструменты

3.1 Исходник макета

3.2 Flutter — эталон поведения и текстов

3.3 Android — объект работы

3.4 Устройство для съёмки

Сравнение раздела 6 требует экрана 390×844 dp при плотности 3× (1170×2532 px). Такой эмулятор уже развёрнут для «По душе» на Mac mini владельца (agent@100.85.254.75 через Tailscale, вход только с проброшенным ssh-агентом SSH_AUTH_SOCK=~/.ssh/agent.sock; SDK и AVD podushe390 в ~/podushe-shoot: hw.lcd.width=1170, hw.lcd.height=2532, hw.lcd.density=480). Метрики устройства не зависят от приложения — тот же AVD пригоден для «По любви» (Р-10: подтверждение владельца, что машину можно занимать вторым проектом; сборка кладётся в ~/polyubvi-build, снимки — android/docs/figma/shots/).

3.5 Рендеры ×3: отработанный рецепт съёмки (получен 08.09 от агента «По душе», 623 кадра сняты без ошибок)

  1. Доступ на Mac mini — только через проброшенный ssh-агент: export SSH_AUTH_SOCK=~/.ssh/agent.sock; ssh -o StrictHostKeyChecking=accept-new agent@100.85.254.75. Ключ -i не работает. macOS 26.5, 14 ядер, python3 3.9 с PIL, 675 ГБ свободно. Свой каталог — ~/polyubvi-render; чужие (~/podushe-shoot с эмулятором podushe390, порт 5554; ~/podushe-render) не трогать.
  2. Сеть на маке. Системный DNS внешние имена не резолвит, IP-связность есть, nslookup host 8.8.8.8 отвечает. Прокси 127.0.0.1:18099 через обратный туннель рвётся под нагрузкой — не полагаться. Рабочее решение: в скрипте подменить socket.getaddrinfo шимом, который резолвит через nslookup … 8.8.8.8 и кэширует; SNI и проверка сертификата остаются по имени. Готовый шим — в начале ~/podushe-render/tools/figma_render3.py на маке, копировать оттуда.
  3. Figma. Лимит считается по тому, где лежит файл: файл в бесплатной команде — 6 запросов в месяц и 429 на 4,5 суток даже при месте Full. Файл должен лежать в платной команде владельца («Shawn's team», Professional), тогда files/nodes/images — 15 запросов в минуту. Токен Shawn — на сервере /root/.figma_token и на маке ~/.figma_token (права 600, в репозиторий не класть). Проверка одной командой: curl -s -D - -o /dev/null -H "X-Figma-Token: $(cat ~/.figma_token)" "https://api.figma.com/v1/files/<KEY>?depth=1" | grep -i 'HTTP/\|x-figma-rate' — если x-figma-rate-limit-type: low, файл не в платной команде. Id нод в копии совпадают с оригиналом (проверено на «По душе», ключ копии 8ZMuzfwPb3RGHBarCAnhCI), поэтому дерево из .fig оригинала и рендеры из копии сшиваются по guid.
  4. Перечень кадров. Не брать дамп GET /files?depth=4 — секции вложены до трёх уровней, кадры теряются (у «По душе» так недобрали 133 из 623). Правило отбора, проверенное двумя путями: узлы FRAME/SYMBOL/INSTANCE шириной ровно 390, лежащие прямо в SECTION или на канве рабочей страницы; без фильтра по высоте. Источник — полный дамп страниц (/files/<KEY>/nodes?ids=<page>, по одной странице) и разбор .fig (fig2sketch, figformat.decodefig); итог — index.json (guid / page / section / name / w / h), совпадение двух путей поимённо — в журнал.
  5. Съёмка. tools/figma_render3.py (на маке — версия с DNS-шимом): /v1/images/<KEY>?ids=…&scale=3&format=png, затем скачивание с S3. Параметры, к которым пришли: пачка 2 кадра (кадры 1900 dp рендерятся дольше 4 минут, пачка 6 упиралась в таймаут), таймаут 600 с, повторов до 40, --shard i/n делит только неснятые кадры, 8–10 потоков — итого ~7 запросов/мин при лимите 15; темп 7–15 кадров/мин (477 кадров ≈ 35–70 минут). Всё в nohup, pid-ы в файл, лог в файл; манифест у каждого потока свой, потом слить.
  6. Результат и заливка. Файлы <guid с дефисом>.png, 1170 px шириной, в ~/polyubvi-render/renders/. На сервер — rsync -a --ignore-existing -e ssh agent@100.85.254.75:~/polyubvi-render/renders/ android/docs/figma/fig/renders/ циклом раз в 2 минуты (манифест синкать отдельно, без --ignore-existing). Проверка после: каждый PNG открыть PIL, ширина 1170; у кадров с тенями и поп-апами экспорт шире (1242/1314 px) — смещение для кропа считается как (absoluteBoundingBox − absoluteRenderBounds) × 3 из дампа API и пишется в манифест; diff.py читает смещение из манифеста.
  7. Что нужно от владельца для старта съёмки: ссылка на файл «По Любви Дизайн_Client», уже перенесённый в «Shawn's team» (ключ — часть адреса между /design/ и следующим слэшем); подтверждение, что берём токен Shawn с сервера (или новый токен в ~/.figma_token); подтверждение объёма — 9 рабочих страниц + UI Kit из раздела 2, все кадры ×3; .fig того же файла уже есть (3.1); результат складывается в android/docs/figma/fig/renders/.

3.6 Отчёт сверки 08.09

https://salam0nn.ru/artifacts/polyubvi-parity.html — 71 экран, три колонки (Figma с размерами нод / Flutter / Android) и списки расхождений, таблица дизайн-системы, матрица API (63 строки), разбор чеклиста, 19 дефектов Flutter, 20 рисков данных, приложение с конспектами всех источников. Используется как стартовый реестр (8.2) и как карта чтения; каждая его строка в Ф1 либо подтверждается карточкой, либо опровергается с доказательством.


4. Открытые решения владельца

08.09.2026 владелец подтвердил все предложения агента по Р-2…Р-14 («всё остальное подтверждаю»): действуют варианты, помеченные «(предложение)». Строки сохранены как история решений; новые вопросы дописываются ниже.

Формат: [ТРЕБУЕТ РЕШЕНИЯ] · вопрос · варианты · что блокирует · дата. Агент дописывает сюда каждый новый вопрос (К-6) и не двигает зависимую часть, пока строка не закрыта. Предложение агента отмечено «(предложение)».

ID Вопрос Варианты Что блокирует
~~Р-1~~ Доступ к файлу через Figma API. Решено 08.09 (владелец): файл «По Любви Дизайн_Client (Copy)» лежит в платной команде, ключ Khr7eRd65r8CQynElpWwwJ; токен Shawn с сервера. Проверено в 12:46: GET /v1/files/<KEY>?depth=1 → HTTP 200, 24 страницы, версия 2396765041287810064, lastModified 2026-09-08T12:45:11Z; дампы nodes 10 рабочих страниц скачаны в docs/figma/fig/api/ (92 МБ). Дальше — рецепт 3.5. —
Р-2 Текст ноды против arb. Известные расхождения: гео-шаг — нода «Поделиться геопозицией» + «Пропустить», arb locationDeterminationShareLocationButton = «Далее» без пропуска; записка — счётчик «0 / 200» в ноде, maxLength 120 во Flutter; правила сообщества — экран в ноде, внешний URL во Flutter; блокировка — кнопка «Отменить подписку» в ноде, нет во Flutter; OTP — «59 с.» в ноде, code_lifetime сервера во Flutter (что верно: сервер). Полный список собирается в Ф0-4 (docs/figma/fig/strings-conflicts.md). а) по умолчанию верна нода, список подтверждается одним ответом (предложение); б) владелец решает построчно Г-4 для затронутых кадров
Р-3 Дефекты Flutter (8.4): не копировать. Каждый пункт 8.4 предлагается реализовать по ноде, а не по Flutter. а) принять список целиком (предложение); б) исключения построчно Г-9 для затронутых экранов
Р-4 Есть в макете, нет во Flutter: премиум-фильтры V3 (секция «Фильтры» — Готово: верификация, подписка, число фото, видео, совместимость, религия, алкоголь, курение, «Сбросить фильтры», popup «Расширили подписку»); список «Твои пары» V3 (Готово); maintenance-карточка/BS V4 (Готово); раздел видео V3 (В работе); Онбординг V4 и Сообщества V5 (В работе). а) «Готово» — в объём Ф4 с BACKEND-GAP там, где нет ручек; «В работе» — вне объёма до нового .fig (предложение); б) иное Индекс кадров (пометка wip), Ф4
Р-5 Расширения plibvi S66–S71 (события «По душе», компания, GEO, скоринг, голос, trust-чип, mixed feed). Макета нет. а) оставить, пересобрать из компонентов кита Ф2 без изменения логики, trust-чип на карточке ленты убрать из зоны summary ноды и перенести в шторку/секцию по решению дизайнера (предложение); б) скрыть за фича-флагами до появления макета; в) оставить как есть Г-12 для их экранов; S27
Р-6 Оплата на Android. Нода и Flutter: WebView «Оплата» с перехватом success/fail-редиректов, BS «Оформляем покупку»/«Оплата не прошла», TYP. Android: Google Play Billing (PREFER_PLAY_BILLING) и Custom Tabs с ручной проверкой. а) WebView + BS + TYP по Flutter, Play Billing остаётся за флагом как второй путь (предложение); б) только Play Billing; в) только WebView S40–S42, Ф4
Р-7 Верхний инсет. Кадр макета — iPhone со статус-баром 54 dp. а) фиксированный отступ 54 dp на любом Android (совпадение с макетом гарантировано); б) системный инсет, снимок сдвигается на (54 − инсет) при сравнении Г-2 для всех кадров с шапкой
Р-8 Порог diff (6.3). Предложение: ≤ 0,3 % пикселей кадра с ΔE2000 > 6 вне масок. принять / изменить числа Г-7
Р-9 Шрифты. В res/font есть Manrope Medium/Bold и TT Runs Medium/Bold/ExtraBold. Нода: lineLight/baseLight — вес 540 (TT Runs Trial Variable), заголовки Auth 2 / Главная 0 / premium 1 — Inter Bold 27 (несогласованность макета), системные элементы — SF Pro. а) 540 → Medium 500 как во Flutter, Inter-заголовки → TT Runs по UI Kit, SF Pro не воспроизводится; критерий Г-4 для этих текстов — блок ±1 px (предложение); б) владелец даёт вариативный TT Runs и/или дизайнер чинит макет Г-4/Г-7 для текстов
Р-10 Устройство. Занять Mac mini (podushe390) вторым проектом «По любви». а) да, каталог ~/polyubvi-build (предложение); б) отдельная машина Ф0-7, все снимки
Р-11 Данные для съёмки. Нода содержит конкретные данные («Всеволод, 31», «78 %», «Константин Сидоров, 29 лет», «Дизайнер интерьеров», «Т-БАНК 210 участников»); стенд dusha.uxbrain.ru таких пользователей не содержит. а) отладочный источник данных DesignFixture в debug-сборке, включаемый флагом, тексты дословно из нод с guid в комментарии (предложение, как Ф0-5 «По душе»); б) владелец заводит на стенде аккаунты с данными ноды Г-6, Ф0-5
Р-12 Удаление самодеятельности Android (8.5) — 17 позиций. а) удалить всё списком (предложение); б) исключения построчно Г-1/Г-12 для затронутых экранов
Р-13 Противоречия и опечатки макета — передать дизайнеру: «поставл лайк» (профиль, premium-offer), «секуд до удаления аккаунта» (deleting 3), «вовзращай» (from rewind), «важайте частную жизнь» (Rules), «Пригласиеще 4 друга» (ref_main), «Анкета содержит нецензурную лексику» (двойной пробел), Inter вместо TT Runs в трёх заголовках (Р-9), кнопка main #000 в UI Kit против #1B1A1C на экранах подписки/анкеты, текст secondary #C66F53 в ките против #000 на экранах, поле телефона caption с lock light #9E8075 в старой Auth 3 против терракоты в V4, «titleLight 21» в макете против 22 в ките, счётчик записки «0 / 200» против arb 120. ответ по каждому пункту: править макет или принять текущее Соответствующие кадры
Р-14 Ветка Android. Работа идёт в feature/figma-parity; в репозитории есть более старая ветка feature/profile-redesign с «redesigned home». а) одна ветка feature/figma-parity, старую не мержить (предложение); б) иное Ф1

5. Фазы работы

Фазы строго последовательны. Фаза закрыта, когда выполнен её критерий завершения и запись о закрытии есть в docs/figma/PROGRESS.md. Внутри фазы задачи идут по ритуалу раздела 7. Порядок экранов внутри фаз — по зависимостям: кит → вход → онбординг → лента → симпатии → чаты → профиль → подписка/сообщества/реферал → системные экраны.

Ф0 · Подготовка: инструменты, индекс, ассеты, строки, фикстура, поведенческие карты

  1. Инструменты. Скопировать android-events/tools/{fig_dump,fig_geom,fig_query,fig_icons,diff,diff_selftest,shoot,figma_render3}.py в android/tools/, заменить пути и идентификаторы (3.1), прогнать каждый на одном примере и записать проверку. Критерий: docs/figma/fig/<page>.json для 9 рабочих страниц и UI Kit, docs/figma/fig/index.{json,md} — индекс кадров (guid · страница · секция-заголовок «Nn» · имя · размер · тип · wip · дубль-мастер); число кадров получено двумя независимыми путями (при Р-1) и совпало поимённо; записаны расхождения с таблицей раздела 2.
  2. Иконки. Все мастер-компоненты секций «Icons FOR DESIGN» и «Icons FOR FLUTTER TYPE» (16/24/32/40, light/heavy/filled) → SVG из .fig → сравнение с 139 файлами assets/icons/ (растр ×8 рядом, агент смотрит каждую пару глазами); расхождения заменяются, отсутствующие добавляются, лишние удаляются. Критерий: docs/figma/fig/icons.md с вердиктом на каждую пару; PlIcons/PlAssets ссылаются только на проверенные файлы.
  3. Иллюстрации и растры. pic-* (welcome, progress-circle, location-main/fail, directional-sign, checkmark, verification, network, no-people, match, chatting, attention, time, no-lovenotes, no-videos, oopps, update, main, premium-icon, invite/pic, invite/pic2), figure-male/female, photo-placeholder*, брендовые формы shape 1…6, like-overlay/dislike-overlay, gradient-bottom, обучающие стрелки и pointer — из .fig в res/drawable* в тех размерах, в каких они стоят в нодах (векторы — VectorDrawable, растры — ×2/×3/×4 без апскейла). Коллаж pic-welcome собирается из слоёв ноды (две фото в формах shape 3/2 с поворотами −6,86°/6,29° и семь эмодзи-бейджей 38), couple.png удаляется. Критерий: docs/figma/fig/rasters.md (имя → guid → sha1 → размеры → где используется); assets/images/ не содержит файлов без guid.
  4. Строки. Все characters текстовых нод рабочих страниц + все ключи app_ru.arb → res/values/strings.xml (имя = ключ arb; для нод без ключа — fig_<page>_<snake> с guid в комментарии; plural/gender-формы arb → <plurals>/варианты). Конфликты нода↔arb — docs/figma/fig/strings-conflicts.md (Р-2). Критерий: grep кириллицы в ui/ пуст после Ф3 (здесь — файл строк готов), каждая строка имеет источник.
  5. Фикстура (Р-11). DesignFixture в debug-источнике данных: пользователи, карточки, лайки, записки, чаты, сообщения, тарифы, сообщества, реферальные цифры — дословно из нод («Всеволод» 31, «Москва»/«9 км», «78 %», теги «Не планирую детей»/«Йога», «Константин Сидоров» 29 лет, «Извини, не могу в этот раз с тобой», «7 минут назад», «Т-БАНК» 210 участников, «299 ₽»/«699 ₽» «-20 %», «6 друзей приглашено» «30 дней подписки»…), фото — из images/ по хешу заливок. Критерий: каждая строка фикстуры имеет guid в комментарии; экран на устройстве с фикстурой показывает тексты ноды.
  6. Рендеры ×3 (Р-1, рецепт 3.5). Все кадры индекса → docs/figma/fig/renders/<guid>.png 1170×N; манифест со смещениями кропа. Критерий: индекс отмечает наличие; кадров без рендера — ноль, либо перечень с причиной и [ТРЕБУЕТ РЕШЕНИЯ].
  7. Устройство и съёмка (Р-10). Сборка dusha-debug на Mac mini, tools/shoot.py по имени состояния (флаг фикстуры + маршрут) → docs/figma/shots/<guid>.png. Критерий: снимок Auth 2 сделан, размер совпадает с рендером.
  8. Сравнение. tools/diff.py проверен на трёх заведомых парах (одинаковые кадры — 0; сдвиг 1 px — число и прямоугольник видны; другой цвет кнопки — найдено) → docs/figma/fig/diff-selftest.md.
  9. Поведенческие карты Flutter. Для каждого экрана S01–S71 (кроме S66–S71) агент читает соответствующие view/bloc/state/api/dto Flutter и выписывает карту 7.2б в docs/figma/behavior/<Sxx>.md: событие → bloc-событие → запрос (метод, путь, тело поле в поле) → ответ/коды → состояние → UI; таймеры, лимиты, кэши, аналитика, диплинки. Пункты 8.4 помечены не копировать. Критерий: 65 файлов; каждая строка карты имеет файл:строка Flutter; сводная матрица API docs/figma/behavior/api-matrix.md (63 строки отчёта проверены заново по коду).
  10. Трассировка. В debug-сборке — файл-лог запросов/ответов/WS-кадров (HttpLoggingInterceptor уже есть в debug; добавить запись в файл и лог WS-кадров; в release ничего не меняется). tools/trace.py забирает лог с устройства и раскладывает по экранам. Критерий: трасса экрана входа получена и сопоставлена с картой S03–S04 вручную.
  11. Решения Р-7 (инсет), Р-8 (порог), Р-9 (шрифт) — применить; пока решения нет — строка в журнале, все Г-7 помечены «предварительно».
  12. Маркеры. Все места в коде, где значение взято не из ноды/arb/Flutter, помечаются FIGMA-CHECK / FLUTTER-CHECK с парой в разделе 4.

Критерий завершения Ф0: пункты 1–10 закрыты с доказательствами; запись «Ф0 закрыта» с перечнем файлов и чисел (кадров в индексе, иконок проверено/заменено, строк, строк фикстуры, карт).

Ф1 · Ревизия существующего (код не правится)

Объём: все 49 экранов, которые в Android уже есть (8.1: статус ◐ и ✓), по всем кадрам этих экранов из индекса и всем строкам их поведенческих карт.

  1. Для каждого кадра — карточка сверки (7.4) с полным ритуалом 7.1–7.3, кроме шага «исправить». Найденное — в реестр docs/figma/fig/ledger.md (9.2).
  2. Для каждого экрана — трасса (6.5) на текущем коде против карты Ф0-9; каждая строка карты получает статус (✓ / ✗ / нет ручки).
  3. Реестр 8.2 (находки 08.09) проверяется первым: каждая строка либо подтверждается карточкой, либо опровергается с доказательством. Раздел 8.3 (риски данных) проверяется трассой: тело каждого запроса на запись сохраняется в карточку.
  4. По итогам — обновить этот документ: 8.1 (статусы), раздел 4 (новые решения), 8.4/8.5 (новые находки).

Критерий завершения Ф1: у каждого кадра ревизуемых экранов есть карточка с числом diff и списком расхождений, у каждого экрана — трасса; реестр полон; запись «Ф1 закрыта» с количеством кадров, расхождений по классам (структура / геометрия / цвет / текст / ассет / поведение / данные).

Ф2 · UI Kit: компоненты по странице «UI Kit»

Всё остальное зависит от кита; пока кит не по ноде, ни один экран не может быть зелёным. Пакет ui/kit/, по компоненту на ноду UI Kit, все варианты и состояния из COMPONENT_SET, имена свойств — как в ноде. Порядок:

  1. Токены: PlColors (с альфа-вариантами), PlTypography (14 стилей + cameraEmoji), радиусы и smooth-скругления (squircle, как SmoothRectangleBorder во Flutter — Shape с непрерывной кривизной, а не RoundedCornerShape), отступы, длительности (Flutter: pressed 100 мс, scale 0,95, switch 300 мс, snack 4 с).
  2. Button (Big 56 / Small 44 × main / secondary / text × default / pressed / disabled / load, иконка слева, .counter), button-circle 80 (Like / Dislike / Disabled), button-camera 72, .44-button, .56-button, switcher-text-icon 208×52.
  3. Инпуты: input--line (плавающая подпись 74×24 на белой плашке, prefix «+7», caption с иконкой 16, erase 44), input--textarea (счётчик «0 / N» на плашке), input--code + input--code-group (6 × 56 × 56, состояния default/filled/focus/blocked/error), input--bday-group (8 × 38,75 × 56 с точками), input--search, input--chat (44 → 104 → 184), btn_copy dark/light.
  4. Селекторы: checkbox 24 и checkbox-cell 366×60, .swicher 56×32 и switcher-cell 366×60, picker-single 120×44, picker-multiple h40 r28 (Default / Checked / Deletable / dark-non-interactive / light-non-interactive / disabled / skeleton), picker-numbers 48×44, slider-cell 358×94 (сегменты, эмодзи, tag), .manipulator 40, cupertino-picker 248×228 / cupertino-picker-pair 296×228 (барабан, ListWheel-аналог с itemExtent 40 и подсветкой 52).
  5. Навигация: navbar--iOS 44 (default / dark / media с таймером / chat 88 с фото 56 и статусом), navbar-custom 390×68 (counter-box с барабаном цифр counter-reel, profile-return 56), progress-bar 390×20 (3 сегмента), Tab Bar 83 (.tab 48×40 с чёрной подложкой 50×44 у выбранной; без верхней линии).
  6. Оверлеи: bottom-sheet r28/28 pad 24 12 8 12 + .handle-cell 36×4 над листом, overlay #000 60 %, Snack 390×56 negative/positive сверху с очередью, Tooltip 147×32 Light/Dark с треугольником, dialog--android.
  7. Индикаторы: Loader 48/32/20 (кольцо stroke 3 #000), skeleton-плашки, .status 16, status V3 on/off, read галочки, .progress, .indicator-pager / pager 80×8, .counter 24, name-label, tag, icon-caption, date-separator, photo-editing 122×128 (added / Проверяем… / drop / error / loading / drag 1,2×), photo-add-cell, video-circle (Big 366 / Medium 208 / Small 128 / profile 96 / loading / Paused / Progress), video-add-circle, .sound-switcher, .button-play 112, .delete-transformer, photo-frame 366 Default/Helper, _Message Bubble (You / Your Friend / reply / edit / typing «Печатает»), chat-preview 390×131,77 (Default / Skeleton / V3), lovenote 366×228 (Default / Skeleton / Error), like-sender 366×416,59 (формы 1…6, name-label, «Новый», timer), profile-face 366×627 (pager, gradient-bottom, summary, video-circle), prompt (empty / on-profile / in-profile / editing / Skeleton), community-cell, city-cell, cell-ы.
  8. Галерея кита: debug-экран, показывающий каждый компонент во всех вариантах; для каждого варианта — сравнение с нодой UI Kit по Г-1…Г-5 (геометрия, цвета, тексты) и, где есть рендер экрана с этим компонентом, — diff фрагмента.

Критерий завершения Ф2: для каждого компонента — карточка docs/figma/cards/kit-<name>.md со списком вариантов из ноды и статусом каждого; ui/components/* из старого кода либо переехали в ui/kit/ с приведением к ноде, либо удалены; grep К-9 по Material-компонентам пуст для ui/kit/-галереи.

Ф3 · Приведение существующих экранов

Порядок: A вход (S01–S04) → B онбординг (S07–S10, S12) → C анкета/медиа (S13, S15–S20) → D верификация (S22–S24) → E лента (S26–S28, S30–S32, S34, S35, S39) → G симпатии (S43–S45) → H чаты (S46–S48) → I профиль (S52–S59, S61, S62) → F подписка (S40, S41) → J общее (S64, S65). Для каждого экрана:

  1. Все кадры из индекса и все строки поведенческой карты — список в карточке.
  2. Пересборка на компонентах Ф2; каждый размер, цвет, текст — с guid/ключом в комментарии кода или в карточке; поведение — по карте, с файл:строка Flutter в комментарии у каждого нетривиального решения (таймер, лимит, код ошибки, порядок запросов).
  3. Снять, сравнить, трассировать, править до зелёного на всех кадрах и ✓ на всех строках карты (7.1).
  4. Самодеятельность Android для этого экрана (8.5) удалена, риски данных (8.3) этого экрана закрыты.
  5. Старый экран удаляется только после того, как новый прошёл все кадры; маршрут не остаётся пустым.

Критерий завершения Ф3: все 49 существующих экранов с Г-1…Г-12 по всем кадрам и картам; 8.3 закрыт для них; реестр Ф1 пуст или содержит только строки с закрытым [ТРЕБУЕТ РЕШЕНИЯ].

Ф4 · Недостающие экраны и поведение

  1. Экраны ✕ (8.1): S05 блокировка (код 101, интерцептор, экран, «Написать в поддержку», «Отменить подписку» по Р-2), S06 обновления (GET /update-works, X-App-Version, коды 121/122, hard/soft/maintenance по V4 — soft и maintenance по ноде, Р-3), S11 геолокация (все четыре состояния Flutter + «Пропустить»/«Выбрать город» по ноде, POST position с координатами), S14 разводная «Не упусти свою любовь», S21 запись видео-кружка (камера, 10 с, 50 МБ, круглый предпросмотр, удаление с подтверждением), S25 превью проверенного селфи, S29 экономика реакций (GET /users/profile/reactions, счётчик-барабан, heart zero, тултип «Подожди N ч M мин», переходы в пейволл endedLikes/endedUndos/endedSuperLikes), S33 и S51 pre-permission BS, S36 обучающие видео (GET /advertising, show_frequency), S37 видео-аватар 96 и полноэкранный кружок, S38 плашка «нецензурная лексика» (лента и профиль), S42 продление/отмена (PATCH /subscriptions/me, блок подписки в профиле, секция «Подписка» в настройках, BS «Отключить продление?»), S49 превью пары из чата (NewPairProfilePreview + go-to-chat), S50 контекстное меню сообщения (edit-text / delete-message / copy / reply, поиск источника ответа с подсветкой, предупреждение о ссылках), S60 превью «Моя анкета».
  2. Поведение из матрицы API (3.6, строки ✕/◐): bring-back, undo-like, users/activity, not_shown_user_ids в recalculate, коды 110 и все остальные (Г-10), code_lifetime OTP, SMS User Consent, пагинация лайков/записок/чатов по курсору, messages/next, new-pairs/{id} GET/DELETE, user-chats/updates, локальный кэш чатов (drift-аналог: Room или SQLDelight — решение агента с обоснованием в карточке, поведение по ChatCacheBloc), WS user-is-typing/edit-text/delete-message исходящие и все входящие события, закрытие 1008 и «Max connections reached», set-last-read throttle 4 с, обновления по foreground-пушам, диплинки пушей во все экраны (NotificationDeeplink), AppsFlyer OneLink для реферала и сообществ (или BACKEND-GAP/Р, если ключей нет), communities/{id}/visible|invisible, referral/approve, subscriptions/me полная модель (paymentSystem, tariff, features, deferredDays, gift/giftReferral), очередь снеков, DeletePair с причинами (по ноде, не по Flutter — 8.4), причины деактивации + таймер 10 с, отправка position с координатами.
  3. Р-4 «Готово в макете, нет во Flutter» — по решению владельца, с BACKEND-GAP там, где ручек нет.

Критерий завершения Ф4: все кадры индекса (кроме wip) с Г-1…Г-12; матрица API без ✕ у строк Flutter (или BACKEND-GAP с решением владельца).

Ф5 · Расширения plibvi (Р-5)

S66–S71 пересобираются на компонентах кита без изменения серверной логики; там, где они встраиваются в экраны ноды (карточка ленты, профиль, лента с событиями), нода не нарушается. Критерий: Г-1/Г-3/Г-4/Г-12 по компонентам, трасса по их API.

Ф6 · Финиш

  1. Снять все пометки FIGMA-ASSET, FIGMA-CHECK, FLUTTER-CHECK, CONTENT-GAP, мёртвый код старых экранов и ui/components/*; docs/FLUTTER_PARITY.md заменить ссылкой на REPORT.md.
  2. Полный повторный проход разделов 6 и 6.5 по всем кадрам и картам заново (не по старым карточкам) — регрессия после всех правок.
  3. docs/figma/REPORT.md (9.3).

Критерий завершения Ф6: отчёт собран, в нём строка на каждый кадр с зелёным diff и на каждый экран с полной трассой, [ТРЕБУЕТ РЕШЕНИЯ] нет.


6. Правила сравнения

6.1 Эталон формы

Рендер ноды из Figma ×3 (1170 px шириной). Локальная отрисовка по дереву эталоном не является. До Р-1 все вердикты Г-7 — «предварительно».

6.2 Снимок

Скриншот приложения на устройстве 390×844 dp ×3 с фикстурой (Ф0-5), в состоянии кадра, после завершения всех анимаций и загрузок; Coil получает картинки фикстуры локально, без сети. Для кадров выше 844 — серия снимков с прокруткой, склеенная по перекрытию, либо сравнение по частям с указанием смещения.

6.3 Выравнивание и метрика

  1. Снимок и рендер приводятся к одной системе координат: при Р-7-а без сдвига; при Р-7-б снимок сдвигается на (54 − инсет устройства) dp.
  2. Маска исключений: системный статус-бар и home-indicator макета (верхние 54 dp при Р-7-б и нижние 34 dp кадра), клавиатура, системные диалоги, область живого видео, область размытия (blur 30 в симпатиях — сравнивается только положение и размер прямоугольника), при Р-9-а — прямоугольники текстовых нод с весом 540 или шрифтом Inter (их положение и размер при этом измеряются по Г-4).
  3. Метрика: доля пикселей вне маски с ΔE2000 > 6; порог по Р-8. Дополнительно — для каждого элемента кадра из дерева измеряется его ограничивающий прямоугольник на снимке и сравнивается с нодой: Г-2 требует ≤ 0,5 dp по каждой стороне.
  4. Зелёный = обе проверки прошли. Красный = хотя бы одна нет. Число и тепловая карта сохраняются в карточку.

6.4 Что не сравнивается попиксельно и почему

6.5 Доказательство поведения (трасса)

  1. Для каждого экрана есть поведенческая карта (7.2б) из Flutter.
  2. Агент воспроизводит на устройстве каждое событие карты и забирает лог (Ф0-10): метод, путь, заголовки (X-App-Version, Authorization без значения), тело запроса поле в поле, статус и тело ответа, WS-кадры в обе стороны, время.
  3. Каждая строка карты получает ✓ только если: запрос совпал с Flutter по методу, пути и набору полей тела (лишнее поле = ✗, недостающее = ✗); реакция UI совпала с состоянием Flutter (снимок); коды ошибок воспроизведены (стенд, MockWebServer в unit-тесте или отладочный инжектор ошибок в debug-сборке — что именно, записано в карточке).
  4. Таймеры и лимиты измеряются (секундомер по логу; для 4-секундного throttle — три события подряд и один кадр set-last-read).
  5. Поведение, которого нет на стенде (нет ручки, нет данных), доказывается unit-тестом с MockWebServer по DTO Flutter и помечается BACKEND-GAP до появления ручки.

7. Ритуал задачи

7.1 Шаги (по порядку, каждый — с критерием завершения)

  1. Перечитать К-1…К-14, разделы 4, 6, 7. Критерий: строка в журнале (К-12).
  2. Собрать кадры экрана из индекса и состояния экрана из Flutter (bloc states, ветки ошибок). Критерий: список guid со ссылками на рендеры и список состояний со ссылками файл:строка; ни один не пропущен (проверить по индексу и по канве страницы; по bloc и по view — два независимых источника для каждого).
  3. Прочитать дерево каждого кадра до листьев и код Flutter экрана целиком (view, bloc, state, use case, api, dto, arb-ключи). Выписать структуру слоёв (7.2а) и поведенческую карту (7.2б). Критерий: у каждого видимого слоя есть строка структуры; у каждого события Flutter — строка карты.
  4. Протокол вопросов 7.3 — до вёрстки. Критерий: все 26 пунктов заполнены.
  5. Проверить 8.3/8.4/8.5 для этого экрана. Критерий: строка «найдено: …» в карточке.
  6. Сверстать и реализовать по структуре и карте на компонентах кита: каждый размер, цвет, текст — с guid/ключом; каждое поведение — с файл:строка Flutter. Критерий: сборка зелёная; grep К-9 пуст.
  7. Снять каждый кадр (6.2). Критерий: файл снимка на каждый guid.
  8. Сравнить (6.3) и трассировать (6.5). Критерий: число и тепловая карта на каждый guid; файл трассы со статусом на каждой строке карты.
  9. Разобрать красное: для каждого прямоугольника расхождения и каждой строки ✗ — какой слой/событие, какое значение в ноде/Flutter, какое в коде, почему. Критерий: у каждого — объяснение и правка либо [ТРЕБУЕТ РЕШЕНИЯ].
  10. Править и повторять 6–9 до зелёного на всех кадрах и ✓ на всех строках. Критерий: все кадры зелёные и все строки ✓ одновременно.
  11. Протокол вопросов 7.3 — после, второй проход (К-5). Критерий: заполнен; найденные ошибки исправлены и снова 6–10.
  12. Компоненты: если менялся компонент кита — список всех экранов, где он используется (grep), и повторные снимки их кадров. Критерий: все они зелёные.
  13. Журнал и карточка (9.1, 7.4). Критерий: файлы записаны, коммит сделан (К-10).

7.2а Структура слоёв (что выписывать из дерева)

Для каждого слоя: guid · тип · имя · видимость · x, y, w, h относительно кадра (источник геометрии: API или fig) · auto-layout (направление, паддинги, gap, выравнивание, fill/hug/fixed) · скругления (и smooth) · заливки (тип, цвет, alpha, blend, bgblur) · обводки (цвет, толщина, положение) · эффекты (тень, blur) · для текста: characters, шрифт, вес, кегль, интерлиньяж, трекинг, выравнивание, перенос, максимум строк · для инстансов: мастер-компонент, вариант и переопределения · для картинок: sha1 · для векторов: наличие vectorNetwork. Особое внимание: слои с opacity < 1, Powdery-50a над фото, bgblur на тегах карточки, скрытые слои (visible=false — не рисовать, но учитывать влияние на auto-layout), инстансы Status Bar - iPhone/home-indicator--iOS/Tab Bar - iPhone (системный хром).

7.2б Поведенческая карта (что выписывать из Flutter)

Таблица: № · событие пользователя/системы (тап, свайп, ввод, таймер, пуш, возврат из фона, потеря сети) · виджет и обработчик (файл:строка) · bloc-событие → состояние · запрос (метод, путь, заголовки, тело поле в поле с DTO) или WS-кадр · ответ/коды и реакция на каждый (включая 101…122, 400, 404, 429, таймаут) · изменение UI (что показать: снек, BS, экран, анимация, длительность) · побочные эффекты (кэш, storage-ключ, аналитическое событие с параметрами, вибрация) · навигация (роут, стек). Плюс строки: предусловия экрана (что грузится при входе, в каком порядке, с какими ретраями), пагинация (триггер, курсор, размер страницы), обновления (пуш, сеть, foreground), лимиты и таймеры с числами, пункты 8.4 с пометкой не копировать.

7.3 Протокол вопросов (обязательный минимум, письменно)

  1. Откуда взято каждое число на экране? (guid для каждого; числа без guid — искать)
  2. Откуда взята каждая строка? (характеры ноды или ключ arb; обе — совпадают ли; если нет — есть ли строка в Р-2)
  3. Какие кадры у этого экрана есть в .fig, все ли они в моём списке? Чем я доказал, что не пропустил ни одного?
  4. Какие состояния у этого экрана есть во Flutter (bloc states, ветки ошибок, пустые списки, нет сети, нет разрешения)? Все ли в моей карте?
  5. Какие слои в ноде скрыты? Почему я уверен, что не нарисовал скрытый и не пропустил видимый?
  6. Есть ли у инстанса переопределения, и учтены ли они?
  7. Что в ноде лежит поверх чего? Совпадает ли порядок отрисовки?
  8. Из какого материала элемент: сплошная заливка, альфа, градиент, картинка, bgblur? Чем это доказано?
  9. Какой шрифт, кегль, вес у каждого текста в ноде, и что стоит в PlTypography? Есть ли на экране Inter или вес 540 (Р-9)?
  10. Где текст переносится в ноде и где у меня? Почему?
  11. Что происходит с элементом при инсете 24, 48, 54 dp (Р-7)? Где это в ноде?
  12. Почему элемент именно здесь, а не выше/ниже? Какой auto-layout это задаёт?
  13. Какой компонент кита это должен быть, и совпадает ли он с нодой UI Kit во всех вариантах, которые тут нужны?
  14. Как этот экран получает данные во Flutter: какие запросы, в каком порядке, с какими ретраями, что кэшируется? Совпадают ли данные снимка с текстами ноды?
  15. Что уходит на сервер по каждому действию: какие поля, откуда их значения, что будет, если поле не заполнено? (К-14)
  16. Какие коды ошибок обрабатывает Flutter на этом экране и каким текстом? Воспроизвёл ли я каждый?
  17. Какие лимиты и таймеры здесь есть (символы, файлы, секунды, попытки)? Откуда числа?
  18. Какие переходы ведут в этот кадр и из него — во Flutter-роутере и в коннекторах макета? Все ли работают? Совпадают ли между собой?
  19. Что общего у этого экрана с уже сделанными компонентами, и не сломает ли правка компонента другие экраны? Какие именно (grep)?
  20. Что здесь Flutter делает не так, как нода (8.4)? Что я с этим сделал?
  21. Что здесь Android придумал сам (8.5)? Удалил ли я это?
  22. Что в ноде противоречит другой ноде того же экрана или UI Kit? Записано ли в Р-13?
  23. Где я мог принять «похоже» за «совпадает»? Перепроверил ли это число измерением, а поведение — логом?
  24. Что показал diff и что показала трасса, и объяснён ли каждый прямоугольник и каждый ✗?
  25. Что бы сказал дизайнер, глядя на мой снимок рядом с рендером, и что бы сказал автор Flutter, глядя на мою трассу рядом со своим bloc? Что первым бросится в глаза?
  26. Что я не проверил, потому что было долго? Как я узнаю, что ошибся, если ошибся? Что нужно от владельца, чтобы закрыть экран полностью, и записано ли это в раздел 4?

7.4 Карточка сверки docs/figma/cards/<guid>.md

# <guid> · <страница> · <секция «Nn»> · <имя кадра>
Экран: <Sxx>   Состояние: <описание>   Flutter: <файлы>   Рендер: <путь>   Снимок: <путь>
Diff: <путь>, <число>, порог <число>, маска <что исключено>   Трасса: <путь>, строк ✓/✗/gap: N/N/N
## Структура (7.2а)
| guid | слой | видим | x y w h (src) | стиль | значение в коде (файл:строка) | статус |
## Поведенческая карта (7.2б)
| № | событие | Flutter файл:строка | запрос / WS | ответ / коды | UI | побочные эффекты | статус |
## Протокол вопросов до / после (7.3)
1. … 26. …
## 8.3 / 8.4 / 8.5: проверено, найдено: …
## Расхождения
| # | слой / событие | нода / Flutter | приложение | класс | правка / [ТРЕБУЕТ РЕШЕНИЯ] |
## Проход 1 (дата) · Проход 2 (дата)
## Переходы (роутер Flutter / коннекторы)
## Вердикт: зелёный / красный — что именно измерено и протрассировано

8. Реестры

8.1 Экраны S01–S71: статус на 08.09.2026 и целевая фаза

Статус по сверке 08.09: ✓ близко к паритету · ◐ есть, но расходится · ✕ отсутствует · ⊕ только Android. Кадры перечислены ориентиром (имена фреймов на канве); полный список guid на экран строится в Ф1/Ф3 по индексу (К-3).

S Экран Страница · кадры (ориентир) Статус Фаза
S01 Сплэш Онбординг · Auth 1, Android ◐ (нет темы splash, фона #C66F53, леттеринга) Ф3
S02 Приветствие «Первый шаг к семейному счастью» Онбординг · Auth 2 ✓ (кнопка Material, PNG вместо коллажа) Ф3
S03 Телефон Онбординг · Auth 3, 3.1–3.5; V4 · Auth 3, 3.2 ◐ Ф3
S04 OTP Онбординг · Auth 4, 4.1–4.4 ◐ Ф3
S05 Аккаунт заблокирован Главная · banned user ×3 ✕ Ф4
S06 Hard/soft update, тех. работы V4 · hard update, maintenance, soft update, maintenance bs, maintenance alert ✕ Ф4
S07 Сплэш онбординга «Привет, расскажи о себе!» Онбординг · Auth 5 ◐ Ф3
S08 Имя и фамилия Онбординг · Auth 6, 7, 8 ◐ Ф3
S09 Дата рождения Онбординг · Auth 9, 9.0–9.3 ◐ Ф3
S10 Пол и рост Онбординг · Auth 13, 14 ◐ Ф3
S11 Геолокация Онбординг · Auth 15, 16, 17; Главная · 0.1 Geo ✕ Ф4
S12 Выбор города Онбординг · Auth 18–23, 25, 26 ◐ Ф3
S13 Шаги анкетирования 1–8 Онбординг · 1…8.1, 1.0 loader ◐ Ф3
S14 Разводная «Не упусти свою любовь» Онбординг · 9 (найти в индексе Ф0-1) ✕ Ф4
S15 Интересы Онбординг · 10, 15, 16, 17 ◐ Ф3
S16 Должность Онбординг · 11, 11.1 ◐ Ф3
S17 Флаги Онбординг · 12, 12.1–12.4 ◐ Ф3
S18 Открывашки Онбординг · 13, 13.01–13.3, 18 ◐ Ф3
S19 «Что мы забыли спросить?» Онбординг · 14, 14.1, 14.2 ◐ Ф3
S20 Медиа: фото и видео Онбординг · Медиа 1, 1.7, 2, 2.1, 2.3, 3, 3.1, 3.2, 6…6.4; V3 · 1, photo-moderation, informer ◐ Ф3
S21 Запись видео-кружка Онбординг · Медиа 1.1–1.5, 5–5.2 ✕ Ф4
S22 Разводная верификации Онбординг · Верификация 1 ◐ Ф3
S23 Инструкция и селфи Онбординг · Верификация 2, 3–3.3, 19, 20; V5 · 1–4, 19–21 ◐ Ф3
S24 Ожидание проверки Онбординг · Верификация 4, 4.1 ◐ Ф3
S25 «Фото проверено» Чаты · Селфи ✕ Ф4
S26 «Последний шаг и в бой!» Главная · 0 ◐ Ф3
S27 Карточка анкеты и верхняя панель Главная · 1, 1.1 full, 1.2/1.3 scroll, 0.2–0.52, 7; V3 · Online, Offline, 1.1 full; V4 · Tag, Tags ◐ Ф3
S28 Лайк / дизлайк / возврат Главная · 5, 5.1–5.3, 12 ◐ Ф3
S29 Лимит лайков, счётчик, тултип Главная · 6, 6.1, 6.2 ✕ Ф4
S30 Мэтч «Это по любви!» Главная · 11 match ◐ Ф3
S31 Записка Главная · 8, 8.2, 1.4, 1.5 ◐ Ф3
S32 Жалоба Главная · 10, 10.1–10.4 ◐ Ф3
S33 BS «Не пропусти симпатию» Главная · 5.4, 5.5 ✕ Ф4
S34 Обучение свайпу и скроллу Главная · 2.a–2.d, 2.4, 2.5, 3, 3.1; Доработки · 2.a, 3.b ◐ Ф3
S35 Пусто / ошибка / нет сети Главная · 0.6, 0.7, 0.8 ◐ Ф3
S36 Обучающие видео в ленте Главная · 9, 9.1, 9.2; V3 · 9 videos, Обучение ✕ Ф4
S37 Видео-кружок и фулскрин Главная · 4, 4.1.1, 4.1.2, 4.2–4.4 ✕ Ф4
S38 Плашка «нецензурная лексика» Доработки · 1.1 full, non-verified ✕ Ф4
S39 Фильтры Главная · filters 1–5, 4.1–4.4; V3 · filters 1–3, 14–16; V4 · filters 1, 2 ◐ Ф3
S40 Пейволл Главная · premium 1, 2, 1.1–1.3, from likes/rewind/lovenotes, payment native/webview; Доработки · from likes ◐ Ф3
S41 TYP, ошибка, ожидание оплаты Главная · payment-success/error/pending; Доработки · payment-success ◐ Ф3
S42 Продление подписки Профиль · subscription ON, not-prolonged iOS/Android, re-prolonged, cancelling subscription; Настройки ✕ Ф4
S43 Таб «Симпатии» Симпатии · all ×6, placeholders, skeleton, empty ×2, error-state, no subscription, loader, pagination ◐ Ф3
S44 Список записок Симпатии · lovenotes screen, deeplink loading, pagination, pagination error ◐ Ф3
S45 Превью анкеты из симпатий Симпатии · profile-preview ◐ Ф3
S46 Таб «Чаты» Чаты · all, network searching, carousel scroll, skeleton ×3, scroll, pagination, error, even amount of matches; V4 · scroll ◐ Ф3
S47 Переписка Чаты · default state, keyboard ×2, keyboard-closed, offline, searching network, message ×2, messages ×2, message-group, looking for a network ×2, pagination-top/bottom, date-separation ×2, no-photo, no-verification; V5 · date-separation 2–5, короткий/длинный текст, messages ◐ Ф3
S48 Меню чата, разрыв пары Чаты · chat actions ×2, profile-preview actions ◐ Ф3
S49 Превью пары из чата Чаты · profile-preview ×2, profile-preview scroll; V3 · Matching ×3, 1.1 full ✕ Ф4
S50 Контекстное меню сообщения, ссылки V5 · message ×10, подскролл ×2, scroll; V3 · message ✕ Ф4
S51 BS «Не пропусти сообщение» Чаты · system permission ✕ Ф4
S52 Таб «Профиль» Профиль · non-verified ×3, verified, subscription ON, email-exists, snackbar, switcher loader ×2; Доработки · non-verified; V3/V4 · non-verified, profile ◐ Ф3
S53 Удаление профиля Профиль · deleting, deleting 2, deleting 3 ◐ Ф3
S54 Настройки Профиль · Настройки, не дали разрешение на пуши ◐ Ф3
S55 Добавление почты Профиль · email ◐ Ф3
S56 Связаться с нами Профиль · report ×2 ◐ Ф3
S57 FAQ Профиль · FAQ ◐ Ф3
S58 Правила и политика Профиль · Rules ◐ Ф3
S59 Редактирование анкеты Профиль · editing-profile ×4, photo-moderation ×3, informer, открывашки ×3, name editing, editing-question; V3 · photo-moderation, informer; V5 · 1 ◐ Ф3
S60 Превью «Моя анкета» Профиль · profile-preview ×4, preview ✕ Ф4
S61 Сообщества V4 · com_main ×6, com_no com ×3, com_main_ext alert ×2, 5.4 push-custom (сообщества), Tag/Tags, filters; V5 · com_list ×5, com_main ×3, com_no com ◐ Ф3
S62 Реферальная программа V4 · ref_main ×8, ref_share, 5.4 ref-info ×2, 5.4 push-custom (реферал), subscription ON ×3, 1.0 loader ×2 ◐ Ф3
S63 Советы по здоровым отношениям (видео) V3 · videos ×3, videoplayer-* ×8 — секция «В работе» ◐ (нет нигде) Р-4
S64 Таббар UI Kit · Tabbar; все табы ✓ (лишняя линия 1 px) Ф2
S65 Снеки, лоадеры, ошибки, пуши, диплинки UI Kit · Snack, Loader; Главная · 0.6, 0.7; Симпатии · error-state ◐ Ф2/Ф4
S66–S71 События «По душе», компания, GEO, скоринг, голос, trust-чип макета нет ⊕ Ф5 (Р-5)

8.2 Реестр расхождений, найденных 08.09 (проверить первыми в Ф1)

Полные списки — в карточках отчёта сверки (3.6). Здесь — то, что определяет объём Ф2–Ф4; нумерация продолжается в ledger.md.

  1. Кит (все экраны). Поля: подпись снаружи и фон #F9F3F2 вместо input--line с плавающей подписью в белой плашке 74×24 и обводкой #E1D6D1/#C66F53. Чекбокс, свитчер, слайдеры, RangeSlider, диалоги, шторки, dropdown — Material вместо нод UI Kit. Углы — RoundedCornerShape вместо smooth. Secondary-кнопка — чёрная обводка 1,5 и чёрный текст вместо stroke #E1D6D1 1,33 и текста #C66F53. Нажатия — ripple вместо pressed #514A47 / alpha 65 % / scale 0,95. Снека нет — тосты-пилюли, красная лента, inline-тексты.
  2. S03/S04. OTP-ячейки 48×56 на #F9F3F2 вместо 56×56 белых с обводкой; лишняя кнопка «Войти»; таймер 45 с из клиента вместо code_lifetime; номер не выделен терракотой; кнопка «Назад» текстом; ошибка строкой вместо снека.
  3. S07–S10. Тексты не из ноды («Расскажи о себе», «Начать», «Пол и рост», «Мужской/Женский», хинты «Как вас зовут»); заголовки titleHeavy 22 вместо headingHeavy 30; нет кольца «60 % доступно», 8 ячеек даты, превью возраста «Тебе 38 лет», барабана роста 248×228, подписей приватности; прогресс — линейный вместо 3 сегментов с подсказками.
  4. S13/S15. Вопрос = экран со списком; нет slider-cell с эмодзи и тегом, picker-single 120×44, зависимых «[Необязательно]» под-вопросов; show=true; лимит интересов 10 на категорию.
  5. S16/S19. Должность и «О себе» обязательны, лимиты 80→50 и 300 против 2000.
  6. S17/S18. Порядок 💚/🚩 обратный; чипы accent@12 % r12 вместо чёрных r28 с close-light; inline-ввод вместо BS «Красный флаг»/«Сохранить ответ»; нет счётчиков в навбаре; открывашки без sticky-категорий и BS со счётчиком «0 / 120».
  7. S20. Одно фото в круге вместо photo-grid 3×2 + video-add-circle 208; фото можно пропустить; тексты на «вы».
  8. S22–S24. Один экран со статусами вместо четырёх; тексты преимуществ не из ноды; селфи из галереи; нет камеры с силуэтом и «✌️ Повтори жест»; нет «Включить уведомления»/«Завершить».
  9. S26. RangeSlider вместо cupertino-picker-pair; чёрный заголовок; лишняя «Пропустить».
  10. S27. Верхняя панель «Лента» + фильтры вместо navbar-custom (profile-return 56, counter-box с барабаном, filters); пейджер — полоски вместо точек 8; описание в шторке вместо секций под карточкой; нет match-filled 32 + «78» + «%», категорий совпадения, «Отправить записку» 240 с counter, роста «171 см» Medium 22, «Пожаловаться» text-кнопкой; кнопки 68 с тенью вместо 80 с белой обводкой 3, не уменьшаются при скролле; ★ вместо premium-icon; TrustChip в зоне summary (Р-5).
  11. S28/S29. Нет undo (profile-return, bring-back, undo-like), счётчика, «Heart zero», тултипа «Подожди 1 ч 36 м», кода 110; оверлеи без поворота 22–30°.
  12. S30. «Это взаимно!» / «Ты и … понравились друг другу» / «Написать …» / «Продолжить свайпы» вместо «Это по любви!» / «Вы и … нравитесь друг другу» / «Открыть чат»; прямоугольники 148×196 вместо shape-brand 160×182; нет эллипсов фона и анимации въезда.
  13. S31/S32. Диалог по центру вместо панели снизу; тексты другие; нет снека «Отправили твою записку»; жалоба — AlertDialog со строками вместо BS с secondary-кнопками и loader.
  14. S35/S39. Кнопка «Обновить» в пустом состоянии; фильтры: «Применить» внизу вместо «Готово», RangeSlider, нет геолокации, рост без «Не важен», порядок и тексты секций.
  15. S40–S42. Список тарифов вместо двух карточек 179×140 со скидкой и бейджем; «Premium «По любви»» вместо «Любовь без границ»; нет terms/«Условия»/«Приватность»/«Восстановить покупки»; Custom Tabs вместо WebView; нет TYP, pending, failed, продления.
  16. S43–S45. Сегмент-контрол и строки вместо стека записок и like-sender в формах 1…5 с поворотом; блюр раскрывает имя/возраст; таймер сырой строкой; нет «Узнай, кто поставил лайк», псевдонимов, пагинации; превью: «Симпатия» вместо имени, кнопки 64/72 без обводки.
  17. S46–S48. Список без sliver-хедера «Твои пары» и карусели с zoom; строки 56 без online, «Имя, возраст», «7 минут назад», ⋮; без WS-обновления, пагинации, баннера правил; тред: чёрный пузырь, r16, нет времени, «Изменено», ChatUserInfo (Проверен/Подписка/128), статус по сокету, одна страница истории, composer r28; меню dropdown без причин разрыва и «Поделиться профилем».
  18. S52–S59. «redesigned home» вместо top-row 68×66 + аватар 128 + «63 %» + switcher 208×52 + verify-card + premium-offer + «Добавь почту» + links-cells; удаление без причин и таймера; настройки без секций «Контент»/«Подписка»; почта и поддержка экранами вместо BS; FAQ аккордеоном; правила — свой текст; редактирование одной формой с редактируемыми полом/датой, без ответов анкеты, с «Быстрым выбором», без «Показать анкету» и «Измени селфи».
  19. S61/S62. Белые каталоги вместо чёрных экранов с каруселью 120 и иллюстрациями; нет видимости значка, ссылок-приглашений, поиска, BS результатов; реферал делится userId, approve не вызывается, нет истории и прогресса 0–20.
  20. S64/S65. Линия 1 px над таббаром; нет очереди снеков, диплинков AppsFlyer, foreground-обновлений по пушам.

8.3 Риски данных — закрыть построчно (К-14), доказательство — трасса

  1. ProfilePatchBuilder шлёт полный PATCH с дефолтами → только изменённые поля по PersonalDetailsRequestDto.
  2. show=true для всех шагов анкеты; hidden-шаги не фильтруются → правила Flutter (stabilityStepInRequest скрыт).
  3. Вложенные answer.questions не парсятся → собирать и отправлять зависимые ответы.
  4. Дата рождения и пол редактируются из профиля → только рост (Flutter genderAndHeightStep из профиля), дата — никогда.
  5. 18+ проверяется только на submit → блокировка «Далее» на шаге даты (нода Auth 9.2).
  6. Селфи из галереи → только фронтальная камера в приложении.
  7. dismissedPersonIds не уходят как not_shown_user_ids → как во Flutter MatchingStorage.
  8. Код 110 и остальные не распознаются → Г-10.
  9. deactivate reason всегда other → 4 причины + таймер 10 с.
  10. delete-pair без причины → 5 причин по ноде (не по DeletePairConverter, 8.4).
  11. referral/approve не вызывается, делится userId → OneLink + approve + didAccept.
  12. Код 101 не обрабатывается → интерцептор + экран S05 + разлогин.
  13. Рост в фильтре всегда отправляется (150–200) → «Не важен» = null.
  14. Заблюренные лайки раскрывают имя и возраст → псевдонимы sympathyMale/Female1..10, blur 30.
  15. Статус «в сети» = состояние сокета → user-is-online/offline собеседника, lastVisit.
  16. Список чатов не обновляется по WS; история — 30 сообщений; edit/delete не применяются → ChatsListBloc/ChatBloc целиком.
  17. Лимиты медиа не проверяются на клиенте → 10 с / 50 МБ / 10 МБ / 6 фото до отправки.
  18. «О себе» 300 и обязательное; должность обязательная → 2000 / 50, оба необязательные.
  19. Нет hard update → GET /update-works + коды 121/122 + X-App-Version.
  20. Интересы: 10 на категорию → 10 суммарно; code_lifetime игнорируется → таймер с сервера.

8.4 Дефекты Flutter — не копировать (К-13, Р-3)

# Что во Flutter Что делать в Android
1 DeletePairConverter → всегда metOffline Отправлять выбранную причину (5 значений DTO)
2 POST /users/{id}/report без message (TODO) Отправлять report_place, report_reason; поле message — по DTO, если сервер принимает
3 Soft update и «Технические работы» — TODO Реализовать по V4 (BS soft update, экран и BS maintenance, alert-карточка)
4 darkTheme == lightTheme Тёмной темы нет — не-цель; не изобретать
5 Нет бейджей таббара Не-цель (в макете только заметка)
6 EmptyPhotoWidget height/width перепутаны Ячейка 116,67×128 из ноды
7 Гео-шаг без «Пропустить»/«Выбрать город», кнопка «Далее» По ноде Auth 15: «Поделиться геопозицией» + «Пропустить» (Р-2)
8 Опечатка «только только» в repeatVerificationSubtitle Текст ноды V5 «1»: «Это фото могут видеть только твои пары…»
9 Пустое состояние «Записок» не отрисовано (закрывает экран) По ноде «Записки» пусто: «Когда кто-нибудь напишет тебе записку…» + pic-no-lovenotes
10 Правила и политика — внешние URL Экран «Rules» по ноде (разделы Уважение…Контент, текст ноды) + политика внешним URL как в ноде
11 Блокировка без «Отменить подписку» По ноде: кнопка есть при активной подписке (Р-2)
12 Записка maxLength 120 По ноде «0 / 200» (Р-2)
13 MediaQuery.withNoTextScaling Не копировать: системный масштаб шрифта уважается, сравнение — при масштабе 100 %
14 PlDialog на Android — TODO по стилю dialog--android из ноды
15 couple.png вместо коллажа Коллаж из слоёв ноды (Ф0-3)
16 lineLight 500 вместо 540 Р-9
17 Дизлайк не отправляется на сервер (локально + not_shown_user_ids) Так же (это поведение продукта), но с not_shown_user_ids
18 После онбординга верификация поверх экрана возраста; экран возраста при каждом логине с surveyPercentage > 0, пока не сброшен флаг Воспроизвести порядок роутов; флаг isFirstMatchingLaunch — как во Flutter (MatchingStorage)
19 Опечатки макета, повторённые в arb Р-13; в код — исправленные строки после ответа владельца

8.5 Самодеятельность Android — удалить (Р-12)

  1. Кнопка «Войти» на OTP (Auth 4 — автопроверка, кнопки нет).
  2. Кнопка «Назад» текстом на экране телефона и внизу шагов онбординга.
  3. Подсказка «Тестовый код» — только dusha-debug, никогда в stage/prod (оставить за флагом).
  4. «Расскажи о себе» / «Начать» / «Несколько коротких шагов…» и все тексты онбординга не из ноды.
  5. Обязательность должности и «О себе»; лимиты 80/300.
  6. «Быстрый выбор» флагов (Честность, Юмор… / Грубость, Ложь…).
  7. Слоты открывашек с номерами и hint «это icebreaker».
  8. «Пропустить» на экране возраста партнёра.
  9. TrustChip в зоне summary карточки ленты (перенос — Р-5).
  10. «Узнать больше» → UserCardDetailsSheet с заголовками «Образ жизни», «В отношениях», «🟢/🔴» (описание — под карточкой, как в ноде).
  11. «Это взаимно!» / «Продолжить свайпы» и весь текст мэтча.
  12. Сегмент-контрол «Лайки N / Записки N» и строка «Таймер бесплатного просмотра: …».
  13. «redesigned home» профиля: hero-карточка, CompletenessBar, VisibilityToggleRow, PriorityCard «Усиль анкету», группы меню, футер с ID.
  14. Секция «Безопасность» и техническое пояснение про Firebase в настройках.
  15. Экраны почты и поддержки вместо BS; аккордеон FAQ; собственный текст правил.
  16. Реферальный «код» = userId, «Бонусы за объём», «Твой прогресс»; каталог сообществ «Зачем это», StatChip.
  17. Обращение на «вы» во всех строках («Введите имя», «Добавьте фото», «Когда кто-то лайкнет вас»).

8.6 Ловушки (проверять при каждом кадре)


9. Отчётность

9.1 Запись в docs/figma/PROGRESS.md после каждой задачи

### <дата> · <Sxx экран> · кадры: <guid, …> · состояния Flutter: <N>
Перечитал К-1…К-14, разделы 4, 6, 7 в версии <x.y>.
Кадров всего/зелёных: N/N. Diff по кадрам: <guid>: <число> …
Строк карты всего/✓/✗/gap: N/N/N/N. Трасса: <путь>.
Проверил вручную: <что именно, на каком примере>.
8.3/8.4/8.5: <что найдено и что сделано>.
Расхождения найдены/закрыты: <ссылки на ledger>.
[ТРЕБУЕТ РЕШЕНИЯ]: <новые строки раздела 4 или «нет»>.
Команды: assemble ✓ lint ✓ test N ✓. Коммит <hash>.
Карточки: docs/figma/cards/<guid>.md …

9.2 Реестр расхождений docs/figma/fig/ledger.md

Одна строка — одно расхождение: id · guid кадра · guid слоя или файл:строка Flutter · класс (структура / геометрия / цвет / текст / ассет / поведение / данные / материал) · нода или Flutter → приложение · вес (1 — не тот элемент, порядок, отсутствующее поведение или запрос; 2 — сдвиг ≥ 4 dp, не тот размер, не те поля запроса; 3 — цвет, материал, ассет, текст; 4 — до 4 dp, трекинг, перенос, длительность) · статус · карточка.

9.3 Итоговый docs/figma/REPORT.md

Таблица по кадрам: guid · экран · рендер · снимок · diff · число · вердикт · карточка. Таблица по экранам: S · строк карты · ✓ · BACKEND-GAP · трасса. Плюс: решения владельца с ответами; исключения масок; список BACKEND-GAP; список удалённого кода (8.5); список ассетов с sha1; итог по 8.3.


10. Формат требований в этом документе

Требования пишутся по EARS: «<система> ДОЛЖНА <ответ>» (постоянные), «КОГДА <событие>, … ДОЛЖНА …», «ПОКА <состояние>, …», «ЕСЛИ <нежелательное>, ТО …», «ГДЕ <функция включена>, …». Одно требование — одно условие; у каждого — способ проверки и адрес (guid / ключ arb / файл Flutter). Слова «примерно», «похоже», «как правило», «по возможности», «как во Flutter» без адреса в требованиях запрещены; неясность помечается [ТРЕБУЕТ РЕШЕНИЯ] и не реализуется.

Примеры в формате этого документа: - КОГДА пользователь вводит шестую цифру кода, приложение ДОЛЖНО отправить POST /auth/otp/verify без нажатия кнопки и показать ячейки в состоянии blocked до ответа (нода Auth 4.2, EnterOtpBloc). - ПОКА у пользователя 0 лайков и нет подписки, КОГДА он свайпает вправо, приложение ДОЛЖНО открыть пейволл с заголовком «Открой больше сердечек» (нода «from likes», SubscriptionOffers(endedLikes)), а карточку вернуть на место. - ЕСЛИ ответ содержит code = 101, ТО приложение ДОЛЖНО разлогинить пользователя и показать экран «Твой аккаунт заблокирован» (нода banned user, BlockedUserInterceptor). - Приложение ДОЛЖНО отправлять в PATCH /users/profile только изменённые поля (PersonalDetailsRequestDto, К-14); тест ProfilePatchTest.


11. Глоссарий

Кадр — фрейм шириной 390 на рабочей странице, одно состояние экрана. Экран — S01…S71, объединяет кадры и состояния Flutter. Нода — узел дерева .fig с guid. Рендер — картинка кадра из Figma. Снимок — скриншот приложения. Diff — результат сравнения по разделу 6. Карта — поведенческая карта экрана из Flutter (7.2б). Трасса — лог с устройства, сопоставленный с картой (6.5). Карточка — файл сверки кадра (7.4). Реестр — ledger расхождений (9.2). Кит — пакет ui/kit/, воспроизводящий страницу UI Kit. Зелёный/красный — вердикт diff по порогу Р-8. BACKEND-GAP — ручка или данные, которых нет на стенде.


12. Журнал изменений документа

Источники метода (прочитаны при подготовке; что взято): - Joel Spolsky, «Painless Functional Specifications» parts 1, 2, 4 — спека проектирует продукт до кода; один автор; сценарии; не-цели; открытые вопросы; детали вплоть до текстов сообщений; живой документ; писать просто и перечитывать; «templates considered harmful» — разделы здесь есть только те, что нужны. - NASA Systems Engineering Handbook, Appendix C «How to Write a Good Requirement» — формулировка «должен», верифицируемость, обоснование при каждом требовании (наши «Почему»), запрет расплывчатых слов. - Alistair Mavin, EARS (Easy Approach to Requirements Syntax) — пять шаблонов, порядок клауз, одно требование — одно условие (раздел 10). - ISO/IEC/IEEE 29148 — структура SRS: цель, объём, определения, требования с атрибутами, верификация, приложения (разделы 2, 6, 11). - GitHub Spec Kit, spec-driven.md и AGENTS.md — конституция как неизменяемые принципы, маркеры [NEEDS CLARIFICATION] вместо догадок, ворота между фазами с чек-листами, спека — «что и почему», план — «как», задачи — маленькие и проверяемые. - Kiro specs — три артефакта (requirements / design / tasks), требования в EARS, задачи ссылаются на требования (наши карточки ссылаются на guid и файл:строка). - Addy Osmani, «How to write a good spec for AI agents» — команды сборки и тестов дословно, границы «всегда / спросить / никогда», самопроверка агента против спеки после каждой задачи, домены типичных ошибок в спеке (8.6), спека под версионным контролем. - Claude Code best practices — «дать агенту способ проверить свою работу» (тесты, сборка, скриншот против макета — наши diff и трасса), explore → plan → code → commit, показывать доказательства вместо утверждений, спека самодостаточна и заканчивается сквозной проверкой. - Google, «Design Docs at Google» — контекст и границы, цели и не-цели, рассмотренные альтернативы (наши варианты в разделе 4), документ обновляется, когда реальность расходится с планом. - Figma, «The designer's handbook for developer handoff» — документировать состояния и крайние случаи, токены и компоненты 1:1 с кодом, именование слоёв и переопределения (7.2а, Ф2). - AltexSoft, «Acceptance Criteria» — форматы Given/When/Then и чек-лист, критерии измеримы и проверяемы, без деталей реализации, негативные сценарии (Г-10, 6.5). - Практика визуальной регрессии — сравнение в одной среде и масштабе, числовой порог, маски исключений, человек выносит вердикт (раздел 6).