Files
progcode/editorial/agent-rewrites/103.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
16 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 103,
"slug": "editorial-2025-02-field-ai-code-verification",
"title": "Как проверить AI-предложение кода до merge",
"excerpt": "Зелёный основной сценарий не доказывает сохранность API, входного состояния и правил доступа. Разбираем три границы, отрицательные проверки и критерий готовности изменения.",
"contentHtml": "<p>Проблема: основной тест проходит, но изменение возвращает не то поле, меняет входной объект или пропускает запрос без роли; цена ошибки — повторная отладка, задержка релиза и риск отказа пользователю.</p>\\n<p>Тезис простой: проверяйте не убедительность AI-предложения и не число зелёных статусов, а границы контракта. Для каждой границы нужна короткая цепочка доказательств: что обещает код, что наблюдает проверка, какой отрицательный путь рассмотрен и какое действие следует из результата. Если сигналы относятся к разным контрактам, merge нельзя считать готовым.</p>\\n<h2>Механизм: один сигнал закрывает только свой вопрос</h2>\\n<p>Контракт описывает неизменяемое условие. Это форма результата, сохранность аргумента или список разрешённых ролей. Статический анализ показывает структуру кода. Позитивный тест показывает разрешённый путь. Негативный тест показывает отказ. Review связывает эти наблюдения с контекстом вызова. Ручной сценарий проверяет эффект на границе системы.</p>\\n<p>Ни один сигнал не даёт общего вердикта. Линтер не знает, какое имя поля ждёт потребитель. Тест строки не видит мутацию объекта. Успешный сценарий редактора не доказывает запрет для пустой роли. Поэтому сначала назовите риск как наблюдаемый разрыв, затем выберите проверку, которая может его опровергнуть.</p>\\n<h2>Пример 1. Значение верно, форма API нарушена</h2>\\n<p>Mapper получает <code>subtotalCents</code> и <code>taxCents</code>. Он правильно складывает их. Потребитель ожидает объект с полем <code>amountCents</code>, но предложенный код возвращает <code>total</code>. Тест арифметики остаётся зелёным: число не изменилось. Ошибка появляется на границе потребителя.</p>\\n<p>Проверка должна сравнить не только значение, но и публичную форму. Нужны assertion на ключи результата и тест, который читает объект так же, как реальный потребитель. Если переименование действительно нужно, его оформляет владелец API. Нельзя переписать тест под новое поле и объявить проблему решённой: так тест закрепит решение, которого ещё никто не принял.</p>\\n<h2>Пример 2. Preview выдаёт правильный текст, но меняет состояние</h2>\\n<p>Функция с именем <code>preview</code> должна вычислить результат и вернуть его. Внутри она выполняет <code>draft.status = 'normalized'</code>. Caller передал свой объект и ожидает, что после preview он останется прежним. Проверка ответа не замечает побочный эффект, потому что строка выглядит правильно.</p>\\n<p>Здесь нужен снимок входа до вызова и сравнение после вызова. Статическое правило может искать присваивание аргументу, но оно не заменяет проверку владения данными. Исправление — создать производное значение без мутации или вынести изменение в отдельную явно названную операцию. Название функции не является доказательством поведения.</p>\\n<h2>Пример 3. Позитивный путь не защищает правило доступа</h2>\\n<p>Контракт разрешает только роль <code>editor</code>. Условие <code>actorRole !== 'viewer'</code> блокирует <code>viewer</code>, но пропускает запрос без роли. Тест для editor сообщает только один факт: разрешённый путь работает. Даже тест для viewer не подтверждает, что отсутствие значения отклоняется.</p>\\n<p>Явный allow-list делает правило проверяемым:</p>\\n<pre><code>function canEdit(actorRole) {\\n const allowedRoles = new Set(['editor']);\\n return allowedRoles.has(actorRole);\\n}\\n\\nexpect(canEdit('editor')).toBe(true);\\nexpect(canEdit('viewer')).toBe(false);\\nexpect(canEdit()).toBe(false);</code></pre>\\n<p>Это учебный пример малого контракта. Он не проверяет токены, identity provider, tenant boundary, сессии или threat model реального приложения. Он показывает другое: разрешённое значение задано явно, а отрицательная ветка входит в контракт. Для production нужны отдельные проверки всей цепочки авторизации.</p>\\n<figure><img src=\"/assets/editorial/2025/ai-code-verification-2025-evidence-chain.svg\" alt=\"Цепочка проверки AI-предложения кода от контракта до решения\" /><figcaption>Цепочка связывает контракт, статический анализ, позитивный и негативный тесты, review и ручной сценарий. Каждый этап отвечает только за свою границу.</figcaption></figure>\\n<h2>Симптом → причина → проверка → действие</h2>\\n<table><thead><tr><th>Симптом</th><th>Причина</th><th>Проверка</th><th>Действие</th></tr></thead><tbody><tr><td>Число верно, потребитель не находит поле</td><td>Проверили значение, но не форму результата</td><td>Сравнить ключи и выполнить тест через потребитель</td><td>Вернуть имя поля или принять отдельное решение о совместимости</td></tr><tr><td>Preview меняет draft</td><td>Вычисление смешано с мутацией входа</td><td>Сравнить объект до и после; проверить присваивания аргументу</td><td>Создать производное значение или назвать мутацию отдельной операцией</td></tr><tr><td>Запрос без роли проходит</td><td>Условие перечисляет исключение, а не разрешённые роли</td><td>Добавить проверку отсутствующей роли и прочитать ветви от allow</td><td>Оставить явный allow-list и default-deny</td></tr><tr><td>Все статусы зелёные, но scope различается</td><td>Проверки относятся к разным контрактам</td><td>Сопоставить вход, выход и границу каждого сигнала</td><td>Заблокировать merge до единого scope или решения владельца</td></tr></tbody></table>\\n<h2>Порядок проверки перед merge</h2>\\n<ol><li><strong>Зафиксируйте контракт.</strong> Запишите форму результата, инвариант входа или точный allow-list. Формулировка «код должен быть качественным» не подходит для проверки.</li><li><strong>Определите scope.</strong> Укажите вход, выход, потребителя и границу изменения. Сверьте, что статический анализ, тест и review смотрят на один scope.</li><li><strong>Проверьте основной путь.</strong> Убедитесь, что разрешённый сценарий даёт ожидаемый результат и не меняет состояние сверх контракта.</li><li><strong>Проверьте отрицательный путь.</strong> Добавьте отказ, пустое значение, неверный тип или запрещённую роль — тот случай, который может опровергнуть исходное предположение.</li><li><strong>Проверьте побочные эффекты.</strong> Сравните входное состояние до и после. Для функций <code>preview</code>, <code>format</code> и <code>calculate</code> отдельно подтвердите отсутствие мутации.</li><li><strong>Сработайте stop при расхождении.</strong> Сохраните наблюдение и не продолжайте подготовку merge. Не подменяйте этот шаг автоматическим revert или rollback.</li><li><strong>Сформулируйте вопрос владельцу.</strong> Назовите выбор, недостающее доказательство, полномочие принимающего решение и условие снятия блокировки.</li><li><strong>Повторите проверки после правки.</strong> Новый diff должен пройти тот же контракт, отрицательные сценарии и проверку побочных эффектов.</li></ol>\\n<h2>Что означает stop, а что — revert и rollback</h2>\\n<p>Stop означает только одно: не продолжать подготовку merge, пока доказательства расходятся. Он не меняет историю системы и не восстанавливает доставленное состояние. Revert отменяет конкретное изменение в истории версий. Rollback возвращает уже работающую систему к прежнему состоянию и требует отдельной проверки данных, scope и полномочий. Называть любой block rollback нельзя: это создаёт видимость готового пути восстановления.</p>\\n<p>Human approval нужен после ясной формулировки выбора. Например, владелец может сохранить старое поле ради совместимости, принять переименование с обновлением потребителей или потребовать исправить правило доступа. Approval не закрывает пробел в доказательствах. Если reviewer не может назвать контракт, границу пользователя и остаточный риск, вопрос ещё не готов.</p>\\n<h2>Ограничения</h2>\\n<p>Примеры в статье учебные. В них нет настоящего репозитория, CI, токенов, пользователей, telemetry или production-нагрузки. Имена полей, ролей и функций подобраны для объяснения механизма. Из зелёного учебного теста нельзя вывести отсутствие уязвимости в реальной авторизации. Из одной цепочки доказательств нельзя вывести готовность релиза.</p>\\n<p>Официальная документация инструментов тоже имеет границы. GitHub описывает code review как комментарии и рекомендации, а обязательное решение об изменении остаётся за процессом команды. NIST задаёт практики безопасной разработки, но не выбирает ваши контракты и владельцев риска. OWASP подчёркивает пользу ручной проверки там, где автоматические средства не видят бизнес-логику и контекст. Эти источники поддерживают многослойную проверку, но не подтверждают конкретный учебный пример.</p>\\n<h2>Проверяемый критерий готовности</h2>\\n<p>Изменение готово к approval, если для каждой затронутой границы выполнены четыре условия: контракт записан; основной и отрицательный пути проверены; входное состояние не меняется без явного разрешения; каждый сигнал относится к тому же scope, что и diff. В записи есть вердикт <code>merge</code>, <code>revise</code> или <code>block</code>. Для merge указан владелец решения. Для block указано доказательство, которое снимет блокировку. Если хотя бы одно условие не выполнено, готовность не доказана.</p>\\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://docs.github.com/en/copilot/how-tos/use-copilot-agents/request-a-code-review/use-code-review\" target=\"_blank\" rel=\"noopener\">GitHub Docs: Using GitHub Copilot code review</a></li><li><a href=\"https://www.nist.gov/publications/secure-software-development-framework-ssdf-version-11-recommendations-mitigating-risk\" target=\"_blank\" rel=\"noopener\">NIST: Secure Software Development Framework (SSDF) Version 1.1</a></li><li><a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Secure_Code_Review_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP: Secure Code Review Cheat Sheet</a></li></ul>"
}