astra-contract-to-figma — Контракт → Figma → сравнение (round-trip)

Источник: ds-docs/component-contracts/skills/astra-contract-to-figma/SKILL.md Справочник скилла — вложенная страница. В вики не выложены, лежат в папке скилла: scripts/render.js, scripts/compare.js, references/navigation-bar-pilot.yaml, references/proven.json.

Шапка скилла:

name: astra-contract-to-figma
description: Обратный ход round-trip. Берёт контракт компонента v2 (YAML из astra-component-contract) и собирает по нему набор компонентов в Figma на тестовой странице того же файла, затем сравнивает с оригиналом узел за узлом и раскладывает расхождения по трём классам. Использовать, когда просят «проверь контракт круговым прогоном», «нарисуй компонент по контракту», «round-trip», «собери из YAML в Figma». Только для проверки формата — не для авторинга компонентов.

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

Строим в том же файле, на отдельной странице. Только в нём есть те же локальные переменные, текстовые стили и дочерние компоненты. Страница называется zz · <Компонент> round-trip (test); удаляется по отдельной просьбе. Оригинал не трогаем никогда.

Три класса расхождений

Каждое расхождение обязано попасть в один из трёх классов — иначе прогон не закончен.

контракт не хранитполе есть в оригинале, в контракте нет места под негорасширить формат либо записать в gaps как сознательную потерю
рендер не умеетконтракт знает, Plugin API так не выставилчинить render.js
оригинал непоследователенконтракт нормализует то, что в макете сделано по-разномулибо выразить узким правилом when, либо записать в gaps

Ноль расхождений достижим только если третий класс тоже выражен правилами. Так был сделан Tag в августовской редакции контракта; у Button часть оставлена в gaps, и он остановился на 193. Ноль — свойство версии контракта, а не компонента: после перестройки контракта Tag по оригиналу прогон 23.09.2026 начался с 467.

Порядок

  1. Загрузить скилл figma-use.
  2. Контракт → JSON: contract.mjs <карточка|yaml> --out <папка>. Раздел gaps рендеру не нужен — убрать и минифицировать, чтобы уложиться в лимит кода. У семейства с forms та же команда кладёт рядом по срезу на форму (<slug>-<форма>.contract.json). Рендер работает со срезом, не с семейством целиком: форма — это отдельное базовое дерево, и собирается она отдельным прогоном.
  3. Разведка окружения файла — см. чек-лист, раздел 6. Результат кладётся в ENV рендера: id дочерних наборов, умолчания свойств подмены, точные id переменных-дублей, начертания шрифта, текстовые стили, auto-layout и ширина набора.
  4. Рендер — scripts/render.js, батчами по 30–60 мастеров:
    • первый батч: SET_ID = null, CONTRACT_JSON = <строка> — создаёт страницу, кладёт контракт текстовым слоем contract.json, собирает мастера, объединяет в набор, заводит свойства;
    • остальные: PAGE_ID, SET_ID, KEYS из первого, CONTRACT_JSON = null.
    • у семейства мастера группируются по имени набора: один контракт даёт несколько наборов, свойства фильтруются по when каждого набора. Пустой problems означает, что все привязки нашлись.
  5. После сборки набора: перенести на него auto-layout и ширину оригинала, затем выставить мастерам FILL, если контракт этого просит. До объединения в набор FILL не принимается.
  6. Сравнение — scripts/compare.js, read-only. Возвращает пропавшие и лишние мастера, расхождения определений свойств, счётчик по полям и шаблоны «поле: оригинал → копия» со счётчиком и примером.
  7. Разобрать шаблоны по классам, починить первые два, перезапустить. Третий — сверить с gaps: чего там нет, дописать.
  8. Показать сводку и остановиться.

Результаты

references/round-trip.md — протокол по трём компонентам.

Замеры на 23.09.2026:

Button12733193, все «оригинал непоследователен»
Tag, август, прежний контракт15430
Tag, сентябрь, контракт перестроен по оригиналу1541467, разбор в round-trip.md
Navigation Bar (пилот)5 из 1920332, две причины

Там же: что вскрылось в оригиналах, какие дефекты формата и рендера нашлись, почему схлопывание семейства в один контракт имеет предел, и что дал разбор проброшенных инстансов.

Порядок применения свойств в рендере

Порядок неочевиден и стоил двух прогонов:

  1. создать узел, назвать, положить к родителю;
  2. раскладка, обрезка, заливка, скругление;
  3. текст: стиль файла → выравнивание → цвет → привязки переменных;
  4. инстанс: свойства → проброс → переопределения;
  5. размеры — HUG и FILL требуют, чтобы узел уже лежал в auto-layout;
  6. после размеров: обрезка строк и maxLines (их сбрасывает resize), затем скрытие узлов (скрытый узел не пересчитывает HUG).

Что рендер не переносит намеренно

  • позиции мастеров на холсте: defaultVariant берётся из положения, контракт этого не описывает, поэтому мастер по умолчанию просто ставится в верхний левый угол;
  • overrides инстансов сложнее заливки, размера и текстового стиля;
  • слой виброотклика ставится по interaction.when, координата — центр верхней кромки.