# Часть 9. Проверка фичи: от спецификаций к фактам Проверка фичи — это не формальность после кода. Это отдельный режим работы, где текстовая спецификация превращается в проверяемые факты. Спецификация объясняет намерение, но сама по себе не доказывает, что оно реализовано. Факт — это утверждение, которое машина или человек может проверить без повторной интерпретации длинной прозы. Короткая формула: ```text Спецификации направляют. Факты допускают к слиянию. ``` ```mermaid flowchart LR A["Текстовая спецификация
намерение и границы"] --> B["Набор фактов
validation.md"] B --> C["Проверки
команды, тесты, ручные сценарии"] C --> D{"Факты подтверждены?"} D -- "да" --> E["Ветку можно сливать"] D -- "нет" --> F["Исправить код
или уточнить спецификацию"] F --> B ``` Для работы с агентами, которые пишут код, это критично. Модель может по-разному прочитать одну и ту же текстовую спецификацию в разных сессиях или версиях. Тест, код выхода, HTTP-статус, инвариант базы данных или явный контракт меньше зависят от интерпретации. Поэтому `validation.md` должен быть не только контрольным списком, а набором фактов для допуска к слиянию. ## Что такое факт Факт — это исполняемое или однозначно проверяемое утверждение: - `npm run typecheck` завершается с кодом 0; - `GET /` возвращает 200; - ответ содержит `

AgentClinic

`; - истёкший JWT возвращает 401; - вставка отзыва без сообщения не проходит проверку; - миграцию можно запустить дважды без неожиданного изменения схемы; - для каждого начального агента страница деталей возвращает связанный список недугов. Плохой пункт проверки: ```text Убедись, что страница выглядит хорошо. ``` Лучше: ```text При ширине 375px заголовок, основное содержимое и подвал не перекрываются. Главный заголовок остаётся видимым без горизонтальной прокрутки. ``` ## Уровни набора фактов Используйте четыре уровня. **Примеры** проверяют конкретные пары входа и выхода: ```text curl -s http://localhost:3000 | rg "

AgentClinic

" ``` **Инварианты** описывают то, что всегда должно быть истинным: ```text У каждой записи обратной связи есть непустое сообщение. ``` **Свойства** проверяют класс случаев: ```text Любая оценка вне диапазона 1..5 отклоняется. ``` **Контракты** фиксируют предусловие, действие и постусловие: ```text Если сессия не аутентифицирована, при запросе GET /dashboard ответ перенаправляет на /login. ``` Не каждая фича требует все четыре уровня. Но у каждой фичи должно быть хотя бы несколько машинно проверяемых фактов. ## Какой уровень фактов нужен — по типу риска Не все фичи одинаковы. Простой UI-баннер и миграция базы данных требуют разной плотности проверок. Минимально достаточный уровень фактов можно выбирать по матрице: | Тип фичи / риск | Пример | Инвариант | Свойство | Контракт | Ручной факт | |-----------------------------|-----------------|-----------|----------|----------|-------------| | Визуальное / UI-изменение | обязательно | | | | обязательно | | CRUD-маршрут | обязательно | | | обязательно | | | Валидация форм / ввод | обязательно | | обязательно | обязательно | | | Миграция данных | | обязательно | обязательно | | | | Авторизация / доступ | | | обязательно | обязательно | | | Платёж / побочный эффект | обязательно | | | обязательно | | | Интеграция со сторонним API | обязательно | обязательно | | обязательно | | | Фоновые задачи / шедулер | | обязательно | обязательно | | | Как читать таблицу: - **Пример** — конкретная пара вход-выход (одна команда, один curl, один утверждённый тест). - **Инвариант** — то, что всегда истинно после действия. Для миграции это «повторный запуск не меняет схему». Для фоновых задач — «после успешного запуска счётчик не уменьшается». - **Свойство** — проверяет класс случаев. Для валидации — «любая оценка вне 1..5 отклоняется». Для авторизации — «любой запрос без сессии возвращает 401». - **Контракт** — формальная связка «при условии X, действие Y приводит к Z». - **Ручной факт** — то, что человек проверяет глазами или руками. Обязателен для UI, потому что автоматическая проверка визуальной иерархии часто неполноценна. Цель матрицы — не превратить её в обязательный чек-лист, а помочь увидеть, что вы пропустили. Если фича типа «миграция данных» проходит только с примером, без инварианта — это сигнал переписать `validation.md`. ## Структура `validation.md` Пример: ```markdown # Проверка — форма обратной связи ## Набор фактов ### F1 — TypeScript компилируется - Команда: `npm run typecheck` - Ожидание: код выхода 0 - Ответственный: автоматическая проверка - Статус: черновик ### F2 — тесты проходят - Команда: `npm test` - Ожидание: код выхода 0 - Ответственный: автоматическая проверка - Статус: черновик ### F3 — пустое сообщение отклоняется - Команда: `npm test -- feedback` - Ожидание: POST /feedback с пустым сообщением возвращает 400 - Ответственный: автоматическая проверка - Статус: черновик ### F4 — корректная обратная связь сохраняется - Команда: `npm test -- feedback` - Ожидание: корректный POST /feedback добавляет одну строку и перенаправляет на /feedback - Ответственный: автоматическая проверка - Статус: черновик ### F5 — страница остаётся удобной на мобильном экране - Проверка: открыть /feedback при ширине 375px - Ожидание: поля формы и кнопка отправки видны без горизонтальной прокрутки - Ответственный: ручная проверка - Статус: черновик ## Критерии готовности - Все автоматические факты проходят. - Ручные факты проверены. - Факты, которые нельзя реализовать, удалены из границ или возвращены в черновик с объяснением. - Дорожная карта и журнал изменений обновлены до слияния. ``` Жизненный цикл помогает не смешивать намерение и доказательство: - `черновик`: факт предложен, но ещё не закреплён; - `обязательный`: факт принят как обязательный для фичи; - `реализован`: есть тест, команда или ручное подтверждение; - `отложен`: факт осознанно перенесён в будущую фазу. ## Начните с различий ```bash git status --short git diff --stat main...HEAD ``` Попросите Qwen Code проверить не только соответствие спецификации, но и статусы набора фактов: ```text /clear Сравни эту ветку с @specs/2026-05-01-hello-hono/validation.md. Покажи: 1. факты, которые реализованы и проходят; 2. факты, которых не хватает; 3. факты, которые неоднозначны и требуют переписывания; 4. решения в реализации, не описанные в requirements.md; 5. устаревшие утверждения спецификации. Пока не изменяй файлы. ``` Если Qwen Code не может определить прошёл/не прошёл для пункта проверки, это не факт, а пожелание в прозе. Перепишите его. ## Проверка с участием человека Агент может найти механические несоответствия, но человек должен оценить продуктовые и архитектурные: - соответствует ли страница миссии; - не слишком ли разрослись границы задачи; - нет ли неоговоренной зависимости; - понятно ли новому разработчику, почему файлы организованы так; - есть ли факт для каждого рискованного поведения; - не остались ли важные решения только в чате. Типичный пример: после первой реализации часто становится понятно, что структура страницы нужна не как один монолитный компонент, а как набор `Header`, `Main`, `Footer`. Это не просто «поправить код»: нужно обновить `plan.md` и факты в `validation.md`, чтобы будущая сессия не вернулась к старой интерпретации. ## Запрос для исправления кода, спецификации и фактов вместе ```text Реализации нужна более ясная структура страницы. Обнови @specs/2026-05-01-hello-hono/plan.md и потребуй: - компонент Layout; - компонент Header; - компонент Main; - компонент Footer; - подключение static/style.css из Layout. Обнови @specs/2026-05-01-hello-hono/validation.md фактами, которые проверяют: - ответ содержит ориентиры header/main/footer; - /static/style.css отдаётся сервером; - npm run typecheck завершается с кодом 0. Затем обнови реализацию, чтобы она соответствовала новому плану и фактам. Держи изменения в границах этой фичи. ``` Так вы одновременно предотвращаете отклонение спецификации и отклонение фактов. ## Автоматические проверки Минимум: ```bash npm run typecheck ``` Если тесты уже есть: ```bash npm test ``` Если есть сервер разработки: ```bash npm run dev curl -s http://localhost:3000 curl -s http://localhost:3000/static/style.css ``` В `validation.md` записывайте точные команды и ожидаемые результаты. Не пишите «проверить, что работает», если можно написать команду и ожидаемый код выхода. ## Ручные факты Ручные факты не слабее автоматических, если они конкретны. Слабая ручная проверка: ```text Проверь интерфейс. ``` Нормальный ручной факт: ```text При ширине 375px страница /feedback показывает поле имени, поле сообщения, кнопку отправки и три последние записи без горизонтальной прокрутки и наложений. ``` Ручные факты полезны для тона, визуальной иерархии, базовой проверки доступности и отсутствия расползания границ. Но если ручной факт повторяется в каждой фиче, подумайте, как автоматизировать его через Playwright или модульные и интеграционные тесты. ## Проверка в CI Факты должны доходить до допуска к слиянию. Для маленького проекта достаточно локальных команд. Для команды лучше добавить CI: ```text Запрос на слияние нельзя принимать, пока: - npm run typecheck не проходит; - npm test не проходит; - обязательные тесты маршрутов не проходят; - набор фактов в validation.md не обновлён. ``` Это и есть практический ответ на критику «спецификации интерпретируются». Текстовая спецификация направляет агента, но слияние решают факты. ## Пакет доказательств для слияния Когда фича подходит к слиянию, у ревьюера должен быть один компактный артефакт, по которому он понимает: что именно реализовано, какие факты прошли, какие отложены. Этот артефакт удобно называть «пакет доказательств» (в англоязычных источниках — `evidence bundle`). Это не отдельный новый файл — это формат описания запроса на слияние. Хороший пакет доказательств состоит из: - ссылки на папку спецификации (`specs/YYYY-MM-DD-feature/`); - списка фактов из `validation.md` со статусами: `подтверждён`, `провален`, `отложен`; - следов запуска команд: имена команд, коды выхода, последняя строка вывода или короткая выдержка; - результатов ручных проверок: что именно проверял человек, на каком экране, что увидел; - списка решений, принятых по ходу реализации, которых не было в исходной спецификации; - ссылок на коммиты, в которых эти решения отражены. Шаблон такого описания запроса на слияние есть в Приложении C. Главная идея: ревьюер не должен заново запускать всё, чтобы убедиться в готовности. Он должен суметь по пакету доказательств понять, что именно автор делал и проверял, и при сомнениях точечно перезапустить нужные команды. Если в пакете доказательств появляется пункт «факт изменён после провала», это не повод его прятать. Наоборот: явно объяснённое изменение факта — нормальная часть SDD, скрытое изменение — антипаттерн (см. часть 20). ## Обновление дорожной карты После прохождения набора фактов: ```markdown ## Фаза 1: Hello Hono (завершена) - [x] Установить Hono и tsx. - [x] Создать маршрут GET /. - [x] Вернуть минимальный HTML с серверным рендерингом. - [x] Добавить скрипт проверки типов. ``` Коммит: ```bash git add specs/roadmap.md specs/2026-05-01-hello-hono git commit -m "Validate Hello Hono feature" ``` Слияние: ```bash git checkout main git merge phase-1-hello-hono git branch -d phase-1-hello-hono ``` ## Практика 1. Запустите все автоматические факты. 2. Попросите Qwen Code сравнить код с `validation.md`. 3. Перепишите неоднозначные пункты проверки как факты. 4. Исправьте код или спецификацию, если они расходятся. 5. Отметьте статусы фактов. 6. Отметьте фазу в дорожной карте. 7. Сделайте слияние. ## Контрольные вопросы 1. Почему текстовая спецификация не должна быть единственным допуском к слиянию? 2. Чем факт отличается от пожелания в проверке? 3. Когда ручную проверку можно считать фактом? 4. Что делать, если тесты проходят, но реализация не соответствует `requirements.md`? 5. Почему «спецификации направляют, факты допускают к слиянию» лучше, чем просто «пишите спецификации лучше»?