Версия 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), готовый бэкенд вместо фейков и уже существующее приложение, которое нужно не дописать, а привести в соответствие.
Ты обязан абсолютно строго и агрессивно проверять всё. Ты обязан читать, изучать и размышлять всегда полностью самостоятельно, не использовать код и субагентов, т.к. это может являться недоверительным источником информации за счёт сужения задачи и отсечения неосознанно важных элементов, что в будущем приведёт в тупик. Мне неважно, сколько времени это займёт и сколько токенов будет потрачено. Мне важен результат, и результат должен быть абсолютным. Если у тебя будут проблемы с доступами — это исключительно твои проблемы: все ключи имеются как к гиту, так и к моему ноуту. […] Информация должна быть вся — от самой мелкой до самой значимой и незначимой.
Изучи в интернете, как составляются спецификации для разработчиков. Вот одна из наших спек [FIG_PARITY_SPEC v3 «По душе»] — перенести характер и требования к агенту как там.
Мандат «По душе» действует здесь в той же силе: «буквально брать каждый экран нашего приложения, смотреть его каждое состояние и сверять его с Figma. Задачу нельзя сужать до чего-то меньшего — выполнять в полном объёме. Всегда сам себя перепроверять. Задавать себе множество вопросов „как, почему, зачем, откуда, для чего“ и идти выяснять ответы. Результат должен быть абсолютным: в точности как в Figma».
Всё ниже — развёртка этих слов в проверяемые правила.
Правила К-1…К-14 действуют на каждом шаге каждой фазы. Каждое сопровождается причиной (почему оно есть) и способом проверки (как узнать, что оно соблюдено). Нарушение любого — задача не закрыта, независимо от результата.
Правило. У «По любви» три источника, и у каждого — своя область. Смешивать области запрещено.
| Область | Источник истины | Где лежит |
|---|---|---|
| Форма: размеры, координаты, цвета, прозрачности, скругления, порядок и видимость слоёв, шрифты, ассеты, варианты компонентов | дерево ноды в .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 → поведение». Пустая колонка = нарушение.
Правило. Всю работу выполняет сам агент: чтение дерева и кода Flutter, вёрстка, съёмка, сравнение, трассировка запросов, вердикт. Субагенты, фоновые агенты и делегирование не используются ни для чего. Скрипты допустимы как инструмент измерения и рутины (разбор .fig, экспорт ассетов, подсчёт пикселей, снятие снимка, чтение HTTP-лога) при трёх условиях: агент прочитал код скрипта целиком, проверил его выход на одном примере вручную (открыл картинку, посчитал число сам, сверил лог с кодом) и записал эту проверку в карточку. Вердикт «совпадает / не совпадает» выносит только агент, посмотрев на кадр, на цифры и на лог.
Почему. Субагент и скрипт могут ошибаться и не знают, что ошибаются; ответственность не делегируется. Чеклист FLUTTER_PARITY.md — пример того, как «функция есть» превращается в ✅ без единого измерения.
Проверка. В журнале каждой задачи есть фраза «проверил вручную: …» с конкретным примером. Вердикт написан от первого лица с описанием того, что агент увидел.
Правило. Задача — это все кадры экрана из индекса (раздел 2) и все состояния этого экрана во Flutter (состояния bloc, ветки ошибок, коды ApiException, лимиты), а не «основное состояние». Закрыть задачу можно только когда каждый кадр прошёл ритуал раздела 7 и каждая строка поведенческой карты (7.2б) доказана трассой (6.5). Если часть невозможна (нет решения владельца, нет ручки на стенде), агент доводит всё остальное до конца, а невозможное записывает как блокер с доказательством невозможности и точной формулировкой того, что нужно от владельца.
Почему. Android закрывался по «основному состоянию»: у ленты есть свайп, но нет счётчика, лимитов, возврата, видео, обучения скроллу; у чата есть отправка, но нет времени сообщения, редактирования, удаления, статуса собеседника. Заказчик прямо запретил сужение.
Проверка. В карточке экрана перечислены все guid кадров из индекса и все состояния из Flutter, у каждого — статус. Нет строки «остальные аналогично».
Правило. Перед вёрсткой каждого экрана и после неё агент письменно задаёт себе и отвечает на вопросы. Обязательный минимум — раздел 7.3 (26 вопросов). Ответ «не знаю» допустим только с планом выяснения и результатом выяснения ниже. Ответ «очевидно» запрещён: очевидное записывается словами. Почему. Каждое найденное расхождение предотвращалось одним вопросом: «откуда это число?», «какой arb-ключ у этой строки?», «какой запрос уходит по этой кнопке во Flutter и с какими полями?», «почему в ноде красные флаги сверху, а у меня зелёные?». Вопрос, который не задан, не имеет ответа. Проверка. Карточка сверки содержит заполненный протокол. Пустой пункт = задача не закрыта.
Правило. Каждая задача проверяется дважды. Первый раз — сразу после реализации, по ритуалу. Второй раз — после того, как агент закрыл файлы экрана, заново открыл дерево ноды, код Flutter и рендер и прошёл карточку с чистого листа, как чужую работу. Цель второго прохода — найти хотя бы одну ошибку. Если не найдена, агент записывает, какие именно проверки сделал повторно и почему уверен. Почему. Первый проход видит то, что ожидает увидеть. Проверка. Две датированные записи в карточке: «проход 1», «проход 2» с находками.
Правило. Если в ноде нет данных, если нода и arb расходятся, если Flutter делает что-то, чего нет в макете, или макет противоречит сам себе — агент не выбирает «разумный вариант». Он пишет маркер [ТРЕБУЕТ РЕШЕНИЯ: <точный вопрос с guid / ключом arb / файлом Flutter и двумя вариантами>] в раздел 4 и в карточку, продолжает всё, что от решения не зависит, и останавливает только зависимую часть.
Почему. Каждая догадка — это отклонение, которое потом ищут часами: «Быстрый выбор» флагов, PriorityCard «Усиль анкету», кнопка «Войти» на OTP, обязательное поле «О себе» — всё это чьи-то «разумные варианты».
Проверка. Все FIGMA-CHECK и FLUTTER-CHECK в коде имеют пару в разделе 4. В коде нет значений с комментарием «примерно», «похоже», «на глаз», «как во Flutter» без ссылки на файл.
Правило. Доказательство соответствия формы — только сравнение картинок: скриншот экрана приложения на устройстве 390×844 dp в ×3 против рендера ноды в ×3, выровненные по верхнему инсету, с числовым результатом (раздел 6). Доказательство соответствия поведения — только трасса (6.5): лог запросов/ответов/WS-кадров с устройства, сопоставленный строка в строку с поведенческой картой из Flutter, плюс снимок результирующего состояния. Пока сравнение красное или в трассе есть строка без доказательства, задача открыта. «Совпало визуально» и «работает» без цифр и логов не принимаются. Почему. Заявленное в чеклисте «Thread + WS send ✅» скрывало отсутствие времени сообщений и статуса собеседника; «Subscription + payment URL ✅» — отсутствие TYP, pending, failed и продления. Проверка. В карточке лежат скриншот, рендер, diff и число; для поведения — файл трассы с отметками ✓ у каждой строки карты.
Правило. После каждой задачи — запись в docs/figma/PROGRESS.md по шаблону раздела 9 и карточка сверки в docs/figma/cards/<guid>.md. Запись содержит только факты, которые можно перепроверить: guid, ключи arb, файлы Flutter, числа, команды, хеши коммитов. Слова «готово», «совпадает», «исправлено» без файла-доказательства запрещены.
Почему. FLUTTER_PARITY.md и REMAINING_PLAN.md состоят из «✅» без единого файла-доказательства; проверить их можно было только повторной сверкой всего приложения, что и пришлось сделать.
Проверка. Каждое утверждение в записи имеет ссылку на файл, guid или ключ.
Правило. Изменения только внутри 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/.
Правило. Перед каждым коммитом: ./gradlew :app:assembleDushaDebug :app:lintDushaDebug :app:testDushaDebugUnitTest — все зелёные (JDK 17, SDK 35). Один коммит — одна задача, сообщение по формату репозитория, в теле — guid нод, ключи arb, файлы Flutter и ссылка на карточку. Таблица кодов ошибок (Г-10) покрыта unit-тестом ErrorMappingTest: все коды 101…122, 400, 404, 429, таймаут → ожидаемый текст.
Проверка. Вывод команд в записи журнала.
Правило. Агент не ускоряется за счёт глубины. Если проверка требует открыть 40 нод и 12 файлов Flutter — открыть 40 нод и 12 файлов. Если ритуал нужно повторить пять раз — повторить пять раз. Единственный критерий остановки — раздел 2. Почему. Заказчик: «мне неважно, сколько времени это займёт и сколько токенов будет потрачено; результат должен быть абсолютным».
Правило. Перед началом каждой задачи агент перечитывает разделы 1, 6 и 7 целиком и раздел 4 (открытые решения). Перед началом каждой фазы — весь документ. Проверка. Первая строка записи журнала задачи: «Перечитал К-1…К-14, разделы 6, 7, 4 в версии <номер>».
Правило. Flutter — источник поведения, но не источник его дефектов. Раздел 8.4 перечисляет 19 известных отступлений Flutter от макета и от здравого смысла; ни одно из них не переносится в Android. Столкнувшись с местом, где Flutter расходится с нодой, агент проверяет 8.4, при отсутствии там — пишет [ТРЕБУЕТ РЕШЕНИЯ] (К-6) и по умолчанию делает как нода. Собственные изобретения Android (8.5) удаляются, а не «сохраняются, раз уже написаны».
Почему. Иначе паритет с Flutter будет означать перенос DeletePairConverter, который любую причину разрыва пары превращает в «Мы встретились лично».
Проверка. В карточке экрана есть строка «8.4/8.5: проверено, найдено: …».
Правило. Каждый запрос на запись (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 при изменении одного поля содержит только это поле) зелёные.
Цель. Каждый кадр каждого рабочего раздела макета присутствует в приложении и неотличим от макета при сравнении по правилам раздела 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).
/root/figma/polyubvi-2026-09-08.fig — экспорт файла «По Любви Дизайн_Client (Copy)» с ноутбука владельца, 938 МБ, md5 74083b20631708ad5259ed4b72dde44c. Zip: canvas.fig (kiwi), images/ (фото, иллюстрации), meta.json, thumbnail.png. 19 страниц, из них 9 рабочих + UI Kit (раздел 2)./root/figma/figenv (fig2sketch, cairosvg); from figformat import decodefig. guid ноды — кортеж (sessionID, localID), равен id из URL Figma. Дети — по parentIndex.guid, порядок — parentIndex.position. Векторы — vectorData.vectorNetworkBlob → figformat.vector_network.decode. Картинки — fillPaints[].image.hash → images/<sha1>.android-events/tools/fig_dump.py (деревья страниц + индекс кадров), fig_geom.py (подстановка геометрии из API-дампа), fig_query.py (чтение деревьев), fig_icons.py (SVG из мастер-компонентов), diff.py + diff_selftest.py (сравнение ×3, ΔE2000, маски), shoot.py (снимок с эмулятора), figma_render3.py (рендеры ×3 через API). В Ф0-1 они копируются в android/tools/ с заменой путей (docs/figma/fig, пакет com.polyubvi.app.dusha, активность ru.polyubvi.app.MainActivity) и проверяются на одном примере каждый (К-2)..fig нет: отрисовок кадров (только thumbnail.png), файлов шрифтов (только имена: TT Runs Trial Variable, Manrope, местами Inter, SF Pro в системных элементах) и посчитанной геометрии авто-лейаута (Р-1)..fig; агент сравнивает деревья (5.1, шаг 9) и обновляет индекс..fig): текстовые деревья страниц docs/figma/fig/pages/<session>_<page>.txt — переносятся из scratchpad сверки 08.09 в Ф0-1.backend/po-lyubvi-flutter/ — копия снапшота GitHub farmgameowner/date (коммит 359d99c от 29.06.2026, po-lyubvi-flutter, версия 1.4.2+128) плюс lib/features/dating_tz/ (README и DTO расширений plibvi без UI). Другой ветки Flutter в доступных источниках нет; если у владельца есть новее — присылает, агент сравнивает git diff и обновляет раздел 8.4.lib/features/<feature>/presentation/** (view, bloc, state), lib/features/<feature>/domain/** (entities, use cases), lib/api/service/** (Retrofit-интерфейсы и DTO), lib/core/** (ErrorDisplayer, ApiException, снеки, permissions), lib/navigation/** (роуты, guard'ы), lib/l10n/app_ru.arb (тексты, plural-формы), lib/uikit/** (компоненты и токены — вторичны к UI Kit ноды, но задают поведение: анимации, длительности, haptics).notes_flutter_*.md в приложении отчёта) — карта, где что искать; доказательством не являются: агент открывает файл Flutter сам./root/plibvi/android, ветка feature/figma-parity, последний коммит в android/ — d7ce69a (31.08.2026). Код app/src/main/java/ru/polyubvi/app/{core,ui}, тема ui/theme/{PlColors,PlTypography,Theme}.kt, общие компоненты ui/components/* (после Ф2 — ui/kit/*), ресурсы res/values/{strings,colors,themes}.xml, res/font/{manrope_medium,manrope_bold,tt_runs_medium,tt_runs_bold,tt_runs_extrabold}.ttf, assets/icons/ (139 SVG), assets/images/ (10 PNG).dusha (dusha.uxbrain.ru, основной), dushalove, stage (stage.plbvi.ru, suffix .dev), prod (production.plbvi.ru). Команды К-10. CI .github/workflows/android.yml.dusha.uxbrain.ru (вход 79050859820 / OTP 338610 по docs/BUILD.md); чего на стенде нет — Р-11 и BACKEND-GAP.Сравнение раздела 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/).
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) не трогать.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 на маке, копировать оттуда.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.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), совпадение двух путей поимённо — в журнал.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-ы в файл, лог в файл; манифест у каждого потока свой, потом слить.<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 читает смещение из манифеста./design/ и следующим слэшем); подтверждение, что берём токен Shawn с сервера (или новый токен в ~/.figma_token); подтверждение объёма — 9 рабочих страниц + UI Kit из раздела 2, все кадры ×3; .fig того же файла уже есть (3.1); результат складывается в android/docs/figma/fig/renders/.https://salam0nn.ru/artifacts/polyubvi-parity.html — 71 экран, три колонки (Figma с размерами нод / Flutter / Android) и списки расхождений, таблица дизайн-системы, матрица API (63 строки), разбор чеклиста, 19 дефектов Flutter, 20 рисков данных, приложение с конспектами всех источников. Используется как стартовый реестр (8.2) и как карта чтения; каждая его строка в Ф1 либо подтверждается карточкой, либо опровергается с доказательством.
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 |
Фазы строго последовательны. Фаза закрыта, когда выполнен её критерий завершения и запись о закрытии есть в docs/figma/PROGRESS.md. Внутри фазы задачи идут по ритуалу раздела 7. Порядок экранов внутри фаз — по зависимостям: кит → вход → онбординг → лента → симпатии → чаты → профиль → подписка/сообщества/реферал → системные экраны.
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..fig → сравнение с 139 файлами assets/icons/ (растр ×8 рядом, агент смотрит каждую пару глазами); расхождения заменяются, отсутствующие добавляются, лишние удаляются. Критерий: docs/figma/fig/icons.md с вердиктом на каждую пару; PlIcons/PlAssets ссылаются только на проверенные файлы.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.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 (здесь — файл строк готов), каждая строка имеет источник.DesignFixture в debug-источнике данных: пользователи, карточки, лайки, записки, чаты, сообщения, тарифы, сообщества, реферальные цифры — дословно из нод («Всеволод» 31, «Москва»/«9 км», «78 %», теги «Не планирую детей»/«Йога», «Константин Сидоров» 29 лет, «Извини, не могу в этот раз с тобой», «7 минут назад», «Т-БАНК» 210 участников, «299 ₽»/«699 ₽» «-20 %», «6 друзей приглашено» «30 дней подписки»…), фото — из images/ по хешу заливок. Критерий: каждая строка фикстуры имеет guid в комментарии; экран на устройстве с фикстурой показывает тексты ноды.docs/figma/fig/renders/<guid>.png 1170×N; манифест со смещениями кропа. Критерий: индекс отмечает наличие; кадров без рендера — ноль, либо перечень с причиной и [ТРЕБУЕТ РЕШЕНИЯ].dusha-debug на Mac mini, tools/shoot.py по имени состояния (флаг фикстуры + маршрут) → docs/figma/shots/<guid>.png. Критерий: снимок Auth 2 сделан, размер совпадает с рендером.tools/diff.py проверен на трёх заведомых парах (одинаковые кадры — 0; сдвиг 1 px — число и прямоугольник видны; другой цвет кнопки — найдено) → docs/figma/fig/diff-selftest.md.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 строки отчёта проверены заново по коду).HttpLoggingInterceptor уже есть в debug; добавить запись в файл и лог WS-кадров; в release ничего не меняется). tools/trace.py забирает лог с устройства и раскладывает по экранам. Критерий: трасса экрана входа получена и сопоставлена с картой S03–S04 вручную.FIGMA-CHECK / FLUTTER-CHECK с парой в разделе 4.Критерий завершения Ф0: пункты 1–10 закрыты с доказательствами; запись «Ф0 закрыта» с перечнем файлов и чисел (кадров в индексе, иконок проверено/заменено, строк, строк фикстуры, карт).
Объём: все 49 экранов, которые в Android уже есть (8.1: статус ◐ и ✓), по всем кадрам этих экранов из индекса и всем строкам их поведенческих карт.
docs/figma/fig/ledger.md (9.2).Критерий завершения Ф1: у каждого кадра ревизуемых экранов есть карточка с числом diff и списком расхождений, у каждого экрана — трасса; реестр полон; запись «Ф1 закрыта» с количеством кадров, расхождений по классам (структура / геометрия / цвет / текст / ассет / поведение / данные).
Всё остальное зависит от кита; пока кит не по ноде, ни один экран не может быть зелёным. Пакет ui/kit/, по компоненту на ноду UI Kit, все варианты и состояния из COMPONENT_SET, имена свойств — как в ноде. Порядок:
PlColors (с альфа-вариантами), PlTypography (14 стилей + cameraEmoji), радиусы и smooth-скругления (squircle, как SmoothRectangleBorder во Flutter — Shape с непрерывной кривизной, а не RoundedCornerShape), отступы, длительности (Flutter: pressed 100 мс, scale 0,95, switch 300 мс, snack 4 с).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.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.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).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 у выбранной; без верхней линии).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.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-ы.Критерий завершения Ф2: для каждого компонента — карточка docs/figma/cards/kit-<name>.md со списком вариантов из ноды и статусом каждого; ui/components/* из старого кода либо переехали в ui/kit/ с приведением к ноде, либо удалены; grep К-9 по Material-компонентам пуст для ui/kit/-галереи.
Порядок: 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). Для каждого экрана:
файл:строка Flutter в комментарии у каждого нетривиального решения (таймер, лимит, код ошибки, порядок запросов).Критерий завершения Ф3: все 49 существующих экранов с Г-1…Г-12 по всем кадрам и картам; 8.3 закрыт для них; реестр Ф1 пуст или содержит только строки с закрытым [ТРЕБУЕТ РЕШЕНИЯ].
NewPairProfilePreview + go-to-chat), S50 контекстное меню сообщения (edit-text / delete-message / copy / reply, поиск источника ответа с подсветкой, предупреждение о ссылках), S60 превью «Моя анкета».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 с координатами.BACKEND-GAP там, где ручек нет.Критерий завершения Ф4: все кадры индекса (кроме wip) с Г-1…Г-12; матрица API без ✕ у строк Flutter (или BACKEND-GAP с решением владельца).
S66–S71 пересобираются на компонентах кита без изменения серверной логики; там, где они встраиваются в экраны ноды (карточка ленты, профиль, лента с событиями), нода не нарушается. Критерий: Г-1/Г-3/Г-4/Г-12 по компонентам, трасса по их API.
FIGMA-ASSET, FIGMA-CHECK, FLUTTER-CHECK, CONTENT-GAP, мёртвый код старых экранов и ui/components/*; docs/FLUTTER_PARITY.md заменить ссылкой на REPORT.md.docs/figma/REPORT.md (9.3).Критерий завершения Ф6: отчёт собран, в нём строка на каждый кадр с зелёным diff и на каждый экран с полной трассой, [ТРЕБУЕТ РЕШЕНИЯ] нет.
Рендер ноды из Figma ×3 (1170 px шириной). Локальная отрисовка по дереву эталоном не является. До Р-1 все вердикты Г-7 — «предварительно».
Скриншот приложения на устройстве 390×844 dp ×3 с фикстурой (Ф0-5), в состоянии кадра, после завершения всех анимаций и загрузок; Coil получает картинки фикстуры локально, без сети. Для кадров выше 844 — серия снимков с прокруткой, склеенная по перекрытию, либо сравнение по частям с указанием смещения.
Status Bar - iPhone, home-indicator--iOS, Keyboard, permission-ios, share-sheet, action-sheet, App Store sheet) — Android рисует свои; сравнивается только факт и момент вызова.X-App-Version, Authorization без значения), тело запроса поле в поле, статус и тело ответа, WS-кадры в обе стороны, время.MockWebServer в unit-тесте или отладочный инжектор ошибок в debug-сборке — что именно, записано в карточке).set-last-read).MockWebServer по DTO Flutter и помечается BACKEND-GAP до появления ручки.файл:строка; ни один не пропущен (проверить по индексу и по канве страницы; по bloc и по view — два независимых источника для каждого).файл:строка Flutter. Критерий: сборка зелёная; grep К-9 пуст.[ТРЕБУЕТ РЕШЕНИЯ].Для каждого слоя: 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 (системный хром).
Таблица: № · событие пользователя/системы (тап, свайп, ввод, таймер, пуш, возврат из фона, потеря сети) · виджет и обработчик (файл:строка) · bloc-событие → состояние · запрос (метод, путь, заголовки, тело поле в поле с DTO) или WS-кадр · ответ/коды и реакция на каждый (включая 101…122, 400, 404, 429, таймаут) · изменение UI (что показать: снек, BS, экран, анимация, длительность) · побочные эффекты (кэш, storage-ключ, аналитическое событие с параметрами, вибрация) · навигация (роут, стек). Плюс строки: предусловия экрана (что грузится при входе, в каком порядке, с какими ретраями), пагинация (триггер, курсор, размер страницы), обновления (пуш, сеть, foreground), лимиты и таймеры с числами, пункты 8.4 с пометкой не копировать.
.fig, все ли они в моём списке? Чем я доказал, что не пропустил ни одного?bgblur? Чем это доказано?PlTypography? Есть ли на экране Inter или вес 540 (Р-9)?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 / коннекторы)
## Вердикт: зелёный / красный — что именно измерено и протрассировано
Статус по сверке 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) |
Полные списки — в карточках отчёта сверки (3.6). Здесь — то, что определяет объём Ф2–Ф4; нумерация продолжается в ledger.md.
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-тексты.code_lifetime; номер не выделен терракотой; кнопка «Назад» текстом; ошибка строкой вместо снека.slider-cell с эмодзи и тегом, picker-single 120×44, зависимых «[Необязательно]» под-вопросов; show=true; лимит интересов 10 на категорию.close-light; inline-ввод вместо BS «Красный флаг»/«Сохранить ответ»; нет счётчиков в навбаре; открывашки без sticky-категорий и BS со счётчиком «0 / 120».photo-grid 3×2 + video-add-circle 208; фото можно пропустить; тексты на «вы».cupertino-picker-pair; чёрный заголовок; лишняя «Пропустить».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).profile-return, bring-back, undo-like), счётчика, «Heart zero», тултипа «Подожди 1 ч 36 м», кода 110; оверлеи без поворота 22–30°.shape-brand 160×182; нет эллипсов фона и анимации въезда.like-sender в формах 1…5 с поворотом; блюр раскрывает имя/возраст; таймер сырой строкой; нет «Узнай, кто поставил лайк», псевдонимов, пагинации; превью: «Симпатия» вместо имени, кнопки 64/72 без обводки.userId, approve не вызывается, нет истории и прогресса 0–20.ProfilePatchBuilder шлёт полный PATCH с дефолтами → только изменённые поля по PersonalDetailsRequestDto.show=true для всех шагов анкеты; hidden-шаги не фильтруются → правила Flutter (stabilityStepInRequest скрыт).answer.questions не парсятся → собирать и отправлять зависимые ответы.genderAndHeightStep из профиля), дата — никогда.dismissedPersonIds не уходят как not_shown_user_ids → как во Flutter MatchingStorage.deactivate reason всегда other → 4 причины + таймер 10 с.delete-pair без причины → 5 причин по ноде (не по DeletePairConverter, 8.4).referral/approve не вызывается, делится userId → OneLink + approve + didAccept.sympathyMale/Female1..10, blur 30.user-is-online/offline собеседника, lastVisit.ChatsListBloc/ChatBloc целиком.GET /update-works + коды 121/122 + X-App-Version.code_lifetime игнорируется → таймер с сервера.| # | Что во 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; в код — исправленные строки после ответа владельца |
dusha-debug, никогда в stage/prod (оставить за флагом).UserCardDetailsSheet с заголовками «Образ жизни», «В отношениях», «🟢/🔴» (описание — под карточкой, как в ноде).userId, «Бонусы за объём», «Твой прогресс»; каталог сообществ «Зачем это», StatChip.all ×6, message ×10, profile-preview ×4) — разные состояния; различать по guid и содержимому.Status Bar - iPhone 54, home-indicator--iOS 34, Tab Bar - iPhone 83 — системный хром iOS внутри кадра; на Android статус-бар системный (Р-7), таббар — компонент кита без home-indicator.input--line в разных состояниях, _Message Bubble с Tail/Read/Изменено, chat-preview Default/V3, lovenote с counter — читать overrides, не мастер.visible=false): filters «Премиум-фильтры» в чипах подписки, Отправить записку на превью своей анкеты, «Сколько всего ты хочешь детей?» в editing-question — не рисовать, но auto-layout родителя учитывать.Powdery-50a — это #F2E7E3 с alpha 50 %; над белым даёт ≈ #F9F3F2, над фото — нет. Использовать alpha там, где нода использует alpha.tags-common на фото — #252525@70 % + bgblur (V3/V4: #000@30 %); Tooltip, Snack, bottom-sheet — smooth-скругления.button-stack/navbar с FIXED_WHEN_CHILD_OF_SCROLLING_FRAME — прижаты, не скроллятся.N ч M мин, «N лет/года»); Kotlin-склонения руками запрещены — <plurals>.SmoothRectangleBorder — squircle; Compose RoundedCornerShape даёт другую форму угла на радиусах 20–28 (заметно на карточке 366×627 и BS).com_main есть и в V4 Готово, и в V5 В работе) — верна версия из секции «Готово», пока Р-4 не решён.code — интерцепторы Flutter (BlockedUser, UpdateError, Fresh) смотрят в тело, а не в HTTP-статус.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 …
docs/figma/fig/ledger.mdОдна строка — одно расхождение: id · guid кадра · guid слоя или файл:строка Flutter · класс (структура / геометрия / цвет / текст / ассет / поведение / данные / материал) · нода или Flutter → приложение · вес (1 — не тот элемент, порядок, отсутствующее поведение или запрос; 2 — сдвиг ≥ 4 dp, не тот размер, не те поля запроса; 3 — цвет, материал, ассет, текст; 4 — до 4 dp, трекинг, перенос, длительность) · статус · карточка.
docs/figma/REPORT.mdТаблица по кадрам: guid · экран · рендер · снимок · diff · число · вердикт · карточка. Таблица по экранам: S · строк карты · ✓ · BACKEND-GAP · трасса. Плюс: решения владельца с ответами; исключения масок; список BACKEND-GAP; список удалённого кода (8.5); список ассетов с sha1; итог по 8.3.
Требования пишутся по 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.
Кадр — фрейм шириной 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 — ручка или данные, которых нет на стенде.
docs/figma/PROGRESS.md)..fig и кодом Flutter (https://salam0nn.ru/artifacts/polyubvi-parity.html) и по образцу android-events/docs/FIG_PARITY_SPEC.md v3.1. Числа объёма (497/477 кадров) — предварительные, по одному пути подсчёта; уточняются в Ф0-1.Источники метода (прочитаны при подготовке; что взято):
- 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).