23 KiB
KT AI — Definition of Done (единый гейт качества)
Это — единственный чек-лист системы. Прогоняй его при ЛЮБОМ изменении UI, а не только при новом прототипе.
Раньше чек-листы жили в трёх местах (docs/PRINCIPLES.md, docs/DESIGN.md, docs/ELEVENLABS_DESIGN.md) и расходились — теперь
канон тут, остальные доки ссылаются сюда. «Почему так» — docs/PRINCIPLES.md + docs/ELEVENLABS_DESIGN.md; «чем строить» — docs/DESIGN.md + COMPONENTS.md.
[auto] = проверяет scripts/validate_product.py или сборка (машина не забывает — это и есть «соблюдается каждый раз»).
[review] = глазами/в showcase (машина пока не ловит; см. «Что нельзя автоматизировать» внизу).
Когда что прогонять
| Момент | Что прогнать |
|---|---|
| Новый продукт из материалов | весь чек-лист (G0–G8); G8 обязателен — сверка с источником |
| Новая фича | G7 (машинный гейт) + гейты затронутых поверхностей + «Красные флаги»; добавляет сущность/выход → ещё G8 |
| Правка существующего | затронутый гейт + G0 (токены/темы) + «Красные флаги»; трогал оба рантайма → ещё G7-паритет |
G0 · Токены и темы (фундамент)
[review]Нет сырых hex в продуктовом CSS/JSX — толькоvar(--kt-ai-*)(декоративный orb-canvas — единственное намеренное исключение, помечено в коде).[review]Геометрия не зависит от темы: темы меняют ТОЛЬКО цвет, не радиусы/отступы/размеры.[auto]Контраст —python3 scripts/build_tokens.py(гейт падает и называет пары). Пороги: текст ≥7:1, приглушённый ≥4.5:1, фокус и заливка состояния ≥3:1. Раньше пункт стоял с меткой[review], и три нарушения прожили несколько мажорных версий: 2,8:1 на кегле 11px глаз читает не как «нечитаемо», а как «тихо».[review]Обе темы проверены переключателем вshowcase.html— глазами проверяется соразмерность и иерархия, а не контраст.[auto]Менялtokens.json/kt-ai-components.css→ прогналpython3 scripts/build_tokens.py(+scripts/visual_check.mjsпри наличии chromium).[auto]Статика адресуетсяktAsset()(в HTML-рантайме —ktAiAsset()), а не путём от корня сайта. Под префиксом развёртывания (/cons) путь/kt-ai-orb.jsуходит мимо приложения; ломается только на развёртывании, в корне домена всё работает. Гейт 18 вdoctor.py.
G1 · Высота и иерархия
[auto]Один primary на зону (scenario.control);[review]кнопки названы глаголами действия, не «Да/ОК».[review]Page-title 24–26px semibold БЕЗ лого + muted-подпись; лого живёт в сайдбаре.[auto]KPI отвечают на вопрос пользователя на экране; наview: operational— без ROI/FTE/экономии (это management-view).[auto]Лейблы коротко: filter ≤2 слов, status ≤4, header колонки ≤2–3;[review]детали — в drawer/help, не в лейбле.[review]Размер/вес/цвет ведут глаз: title → секции → body → meta; один акцент на зону.[review]KPI-плашка читается четырьмя уровнями сверху вниз: подпись → значение →.hint(знаменатель, доля, период) →.note(оговорка: чем число НЕ является). Значение идёт сразу под подписью и НЕ прижимается к низу; полоса выравнивает карточки по верху — иначе в соседних карточках с уточнением и без числа встают на разной высоте.[auto]Три места для трёх разных текстов, и они не путаются:banner— из-за чего число соврёт (до чисел),kpis[].note— что число значит (у самого числа),conclusion— что из чисел следует (после таблицы). Валидатор предупреждает, когда баннер длиннее 200 знаков: это абзац до первого числа, и почти всегда там лежит чужой текст.[review]Текста на экране столько, сколько нужно для решения. Поясняющая строка, оговорка и подзаголовок сперва пробуются как свойство самого элемента (подпись пилюли, подсказка у метки, название колонки) и живут рядом с ним, а не отдельной строкой над списком. Строка, которая не меняется от данных, — кандидат на удаление; если она нужна для честности (непроверенное правило, тестовые данные, пробел в данных), она остаётся, но переезжает к элементу.[review]Ориентация: экран отвечает где я / куда пойти / что здесь / как выйти (крошка, активный nav, заголовок, Esc). Лейблы конкретны (nav по содержимому, не «зонтик»); контролу не нужен поясняющий лейбл.[auto]Подпись сочетания стоит там, где сочетание работает, и называет клавишу этой машины (⌘K / Ctrl K). Подпись без обработчика — обещание, обработчик без подписи — незаявленная горячая клавиша; включаются одним полем. Гейт 19.[auto]У подписи поля естьfor=или обёртка<label class="kt-ai-field">. Подпись рядом с контролом выглядит подписью, но без связи диктор объявляет поле безымянным, а щелчок по подписи не ставит фокус. Гейт 22.[auto]Кит показывает контракт целиком:npm run kit:render(гейт 23). Типы и гейты читают код и не ловят главное — когда рантайм молча не читает блок контракта. Проверка рендерит кит в строку и сверяет содержимое с контрактом.[auto]Архетип реализован в том рантайме, где вы строите:runtimesвarchetypes/registry.json.status: stableпро рантайм не говорит. Гейт 21 + заметка валидатора.[auto]id разметки не совпадают с id символов вшитого спрайта: спрайт стоит выше, иgetElementByIdвернёт<symbol>— элемент останется мёртвым молча, без единой ошибки. Гейт 20.[review]Кнопки create/«Новый X» начинаются с ведущего «+». Один фильтрующий поиск на экран (глобальная ⌘K-палитра и табличный фильтр — разные роли, но два одинаково фильтрующих поля — нет). AI-кнопка Төре — в топбаре рядом с поиском. Не добавляй input/селект, если прямой кнопки достаточно.
G2 · Язык ElevenLabs (эстетика и плотность)
[review]Структуру задают hairline-границы 1px, а не тени; тени — шёпот (карточки приподняты, не парят).[review]Поверхности нейтральные (white/near-white/near-black); цвет — только статус/ошибка/акцент, не украшение.[review]Статус = пилюля со скруглениемradius-tag(5px) и тоном; число/текст остаются нейтральными (не цвет текста).[review]Радиусы 8–12px для карточек/кнопок/инпутов; полный радиус — только pill/toggle/chip.[review]Контент важнее хрома: сайдбар/топбар тонкие и тихие, рабочая область доминирует.[auto]Ширина колонок — по содержимому (colw/ktColWidth), не «на глаз»; короткие колонки не растянуты, длинные не зажаты.[review]Нет карточки-в-карточке (две рамки спорят за группировку).[review]Ритм блоков — из шкалы, и он группирует: регионы экрана (шапка / баннер / содержимое) —stack-group(24); панель управления ближе к тому, чем управляет —stack-block(16); внутри группы (фильтры ↔ вкладки, карточка ↔ карточка) —stack-tight(12); интерьер карточки —pad-card(20); поля области и отрыв от топбара —stack-region(32). Выражаетсяkt-ai-v[data-gap]; собственныйapp-stack-*в продукте — сигнал, что группировку обошли.[review]Проверка ритма: пройди по вертикали и назови каждый зазор. Все числа разные (три и больше на отношение «блок ↔ блок») — фейл, читается как небрежность. Все числа одинаковые — фейл ровно такой же: экран плоский, группы не читаются. Чисел должно быть столько, сколько уровней вложенности.
G3 · Состояния и обратная связь
[review]Каждое действие даёт отклик ≤100мс (toast / смена статуса / состояние кнопки); лоадер на всём, что дольше 300мс — skeleton, не спиннер.[review]Нарисованы empty / loading / error; пустота — осознанный empty-state («что здесь будет»), не голая белизна.[review]Лестница состояний контрола: default → hover → active/selected → focus-visible → disabled; у инпута — focus + error.[review]Микроинтеракция подтверждает результат (copy → чип «скопировано», не только hover).[review]Движение: отклик на pointer-down (не на отпускании); появление отscale(.95), не из ниоткуда; переходы прерываемы/обратимы; overlay растёт из триггера и уходит тем же путём; анимируются толькоtransform/opacity. Подробно —docs/DESIGN.md §Движение.
G4 · Честность данных и копирайт
[review]Только подтверждённые материалами факты; цифр «с потолка» нет; ПДн маскированы (ИИН •••4821, без реальных ФИО).[auto]Нет debug-языка генерации в UI (fallback,модель вернула,JSON,prompt,shell,не проходит дизайн-систему).[review]Поля поиска пусты по умолчанию — не префилл произвольными примерами.[auto]Статусы строк — только из объявленных вstatuses;scenario/drawer/chartссылаются на существующие ключи.[review]AI-контент атрибутирован («Сгенерировано ИИ» / орб) там, где это генерация.[auto]Без дисклеймеров и нравоучений в UI («это прототип», «данные иллюстративны», «AI не действует сам», «решение подтверждает человек») — решение показывают контролы, а не объяснения.[review]Лейблы-секции — нейтральные существительные, не лозунги капсом («Решение», не «РЕШЕНИЕ — ЗА ВАМИ»). Продукт-специфичный текст живёт в контракте (scenario.aiSummary), а НЕ зашит в shell.
G5 · Законы взаимодействия
[review]Выход из любого состояния: Esc / назад / закрытие работают; деструктив — глаголом + подтверждением, необратимое — вводом имени объекта.[auto]Первичные фильтры ≤4 + «Все», только workflow-status (не confidence/risk-type/системы);[review]остальное — прогрессивным раскрытием.[review]Прогрессивное раскрытие: детали в drawer/modal, редкие действия — в hover/popover/kebab, не вшиты в UI навсегда.[review]Shell неприкосновенен: sidebar, topbar, theme-toggle, AI-кнопка Төре — системный контракт, не творческая зона.
G6 · Адаптивность и локализация
[review]<lg → icon-rail, <md → sidebar-drawer; на 360/390/393px нет горизонтального скролла (documentElement.scrollWidth ≤ clientWidth) и топбар влезает.[review]Сенсорная цель ≥44px на ≤768px. Порог задаёт ДС: токен--kt-ai-control-h-touchи правило-порог вkt-ai-components.css(условие(pointer: coarse), (max-width: 767.98px)). Своими руками высоты не поднимай — проверь, что элемент попадает под правило: компоненты через классы.kt-ai-*, разметка кита — через[data-kt-ai-kit]. Осознанно меньший контрол помечаетсяdata-kt-touch="off"; молча — нельзя.[review]Колонка не шире отведённого: у прямых детей сеток (.kt-ai-colsэто делает сам)min-width: 0. Без него1fr=minmax(auto,1fr)и одна длинная строка распирает страницу — сворот в одну колонку это НЕ лечит.[review]Длинные казахские строки (≈+20%) — truncate + tooltip; фикс-ширины под текст кнопок/чипов/пунктов запрещены.[auto]Icon-only кнопка — с tooltip и aria-label (гейт 16 вdoctor.py;titleза имя не считается — диктор его не обязан читать). У кнопки-переключателя подпись зависит от состояния: «Свернуть меню» в свёрнутом виде — ложь.[review]Кнопка, за которой слой, сообщает состояние:aria-expanded,aria-haspopup,aria-controls. Фокус виден с клавиатуры на всём пути.[review]Модальный слой закрывается по Esc — через общий хук китаuseKTEscape, а не своей копией слушателя: копий было две, и они успели разойтись (windowпротивdocument), а два слоя Esc не имели вовсе.[review]Учтеныprefers-reduced-motion(обнуление длительностей),prefers-contrast: more(толще фокус/границы) иprefers-reduced-transparency(blur снят, фон плотный).
G7 · Контракт и паритет (машинный гейт)
[auto]python3 scripts/validate_product.py "<config>.json" --strict→0 ошибок, 0 предупреждений.[review]HTML ↔ React выглядят и ведут себя идентично (если трогал оба рантайма) — один контракт, одно поведение.[auto]Прототип собранpython3 scripts/build_prototype.py— НЕ инлайнен руками.[auto]МенялDS/→ синхронизировал Töре +integrity.testбез падений (0 failed; число тестов не фиксируем — оно растёт).[review]Правил гейт вdoctor.py→ сломай его намеренно снова и убедись, что он падает. Одной проверки при написании мало: рефакторинг 5.9.1 молча вырезал тело гейта 16, иdoctor.pyостался зелёным.[auto]Добавил компоненту рантайм-ресурс (<script src="/…">, спрайт, шрифт) → он лежит в бандле какpublic/…. Проверяет гейт 15 вdoctor.py: компонент без своего файла собирается и типизируется чисто, а у потребителя даёт 404 и пустое место.
G8 · Соответствие источнику (сборка из материалов / Төре-интервью)
Закрывает разрыв «UI ≠ то, что обещали материалы». Не про пиксели — про содержание. Обязателен, когда продукт собран из PRD/ТЗ или из интервью Төре.
[review]Есть карта источника (source-map): выписаны ВСЕ сущности, выходные артефакты (документы, которые продукт производит: ТС, отчёт, СЗ…), роли, сценарии (S-xx) и критерии приёмки (A-xx) из материалов.[review]Трассировка: каждый пункт карты → UI-дом (nav-секция / поле или артефакт в drawer / явный «вне v1»). Правило: представлен или осознанно отложен — никогда молча не выброшен.[review]Каждый named output из PRD (раздел «первая версия» / «данные» / «сценарии») имеет видимое место в UI — не зарыт в_flags.[review]Роли из материалов отражены (кто решает, кто согласует, кто получает) — экран собран под пользователя, принимающего решение.[review]Доменные термины (ТС, ОБ/БКВ, СЗ, ЕНС ТРУ…) — как первоклассные сущности/артефакты, а не обезличенный текст.
Красные флаги (мгновенный фейл ревью)
Лого в page-header · заголовок 30–32px bold (надо 24–26 semibold) · два тоггла сворачивания · гигантская пустота над/под контентом ·
вертикальные сепараторы в таблице · статус сырым текстом/цветом вместо пилюли · сырой hex вместо var(--kt-ai-*) · карточка-в-карточке ·
спиннер вместо skeleton · >1 primary в зоне · разнобой отбивок между блоками (три и больше значений на одно отношение) · один зазор на всё — плоский экран без групп · плотный экран, растянутый во всю ширину монитора (нет content-table-клампа) · цвет ради украшения · синий как заливка primary-кнопки · ROI/FTE на рабочем экране ·
debug-язык генерации в UI · самодельные глифы вместо иконок спрайта · разная геометрия в темах ·
дисклеймер/мета-копирайт («это прототип», «данные иллюстративны») · нравоучение про human-in-the-loop («AI не действует сам», «решение подтверждает человек») ·
лозунг-лейбл капсом («РЕШЕНИЕ — ЗА ВАМИ») · продукт-специфичный текст зашит в shell вместо контракта · два одинаково фильтрующих поиска · кнопка create без ведущего «+».
Что нельзя автоматизировать (всегда [review])
Валидатор ловит структуру и честность контракта (статусы, один primary, ROI на operational, debug-язык, длину лейблов, иконки, chart, паритет схемы).
Он НЕ видит пиксели: иерархию, ритм отступов, тени-vs-hairline, состояния hover/empty/error, паритет тем, адаптив. Это — обязательный глазами-проход в showcase.html/preview. «Зелёный валидатор» ≠ «прошло DoD».