Самая неприятная ошибка помощника не обязана выглядеть как ошибка. Diff компилируется, стиль ровный, название функции понятное, а happy path даёт ожидаемую строку. Затем выясняется, что invalid input превратился в новый default или duplicate branch успевает выполнить side effect. Цена решения здесь выше, чем один фикс: команда теряет прежний контракт и тратит review на восстановление того, что не было зафиксировано до генерации.
Причина в смешении пяти разных предметов. Model output — это предложенный текст. Repository contract — правило о входе, выходе и запретах. Reviewer evidence — объяснение, почему diff попадает в scope. Test evidence — наблюдение конкретной ветки. Unknowns — то, чего эти данные не показывают. Пока они лежат в одном слове «проверено», правдоподобный код получает полномочия, которых у него нет. В январе 2025 GitHub прямо предупреждал, что код может выглядеть валидным, но не соответствовать намерению разработчика; поэтому процесс должен делить доказательства, а не усиливать уверенность в одном ответе.
Пять слоёв, которые нельзя склеивать
| Слой | На какой вопрос отвечает | Пример fixed synthetic evidence | Чего не доказывает |
|---|---|---|---|
| Model output | Какой текст был предложен? | Ветка возвращает empty string для invalid marker. | Что это допустимо по контракту. |
| Repository contract | Какой результат и запрет согласованы? | Invalid marker должен вернуть fixed invalid-note result. | Что diff действительно соблюдает правило. |
| Reviewer evidence | Почему scope и риск приняты? | Path внутри задачи, owner подтвердил расширение. | Что сценарий исполнился в среде. |
| Test evidence | Какой конкретный input-output или side effect наблюдался? | Duplicate key даёт error и zero write calls. | Что покрыты все callers, права и нагрузки. |
| Unknowns | Какая граница ещё не установлена? | Нет real schema, traffic или customer compatibility. | Что риска нет. |
Короткая последовательность проверки
- Отделите candidate. Он не является готовым решением.
- Сверьте contract и paths. У changed branch есть allowed и forbidden result, а scope подтверждён owner.
- Свяжите test с branch. Назовите input, output и forbidden side effect.
- Оставьте unknown. Отсутствие evidence не доказывает security или compatibility.
Model output: кандидат, а не свидетель
Model output можно читать как черновик: он показывает, какую гипотезу стоит проверить. Он не говорит, какие правила приняты в данном repository, кто владеет последствиями и какой тест подтверждает boundary. Даже подробное объяснение не меняет этого статуса: explanation может хорошо описывать локальный алгоритм и одновременно выдумать смысл отсутствующего поля. Поэтому не нужно спорить, «умный» ли ответ. Нужен более полезный вопрос: каким контрактом и каким evidence мы проверим каждое новое поведение?
Рассмотрим fixed synthetic formatter. В одном видимом случае non-empty note форматируется правильно. Candidate diff возвращает empty string для invalid marker, потому что так проще объединить ветки. Код выглядит коротко, а тест happy path остаётся зелёным. Ошибка не в синтаксисе. Ошибка в том, что модель выбрала семантику за владельца контракта. До merge reviewer должен увидеть запрещённый результат рядом с changed branch, а тест — отличить invalid marker от blank input.
// Fixed synthetic contract, not repository code.
const rows = [
{ input: 'note', expected: 'formatted-note' },
{ input: 'blank', expected: 'absent-note' },
{ input: 'invalid-marker', expected: 'invalid-note' },
];
// A plausible-looking candidate is rejected if it maps 'invalid-marker'
// to '' instead of 'invalid-note'.
// The table describes a contract question; it does not call a model or a real formatter.
Repository contract: правило до реализации
Контракт не обязан быть длинной спецификацией. Для маленького изменения достаточно назвать scope, входы, допустимые выходы, forbidden result и owner. Главное — чтобы contract существовал до того, как diff создаст удобную новую интерпретацию. Если задача допускает изменение контракта, это можно сделать, но тогда в карточке появляется отдельное решение: какие consumers затронуты, кто принимает migration и почему новая форма совместима. Нельзя спрятать это решение в строке, которую ассистент добавил между двумя тестами.
Код может ломать контракт не только возвращаемым значением. Частый случай — скрытый side effect. В teaching case duplicate key должен вернуть fixed error и не вызвать write helper. Candidate способен вернуть верный error после ненужного вызова. Без явного запрета и test evidence reviewer видит только label и считает ветку безопасной. Для таких вещей контракт должен называть не только результат, но и то, чего ветка не делает.
Reviewer evidence: связь между diff и задачей
Review evidence отвечает на вопрос «почему мы принимаем этот scope». В него входят paths, связь с задачей, owner и явное решение о расширении. Тест может пройти для parser и не объяснить access helper; согласие owner на файл не проверяет runtime behavior. GitHub описывает review как просмотр commits, files и diff с исходами comment, approve или request changes. Поэтому запись должна быть точной: «path X вне prompt-card — request changes», а не «looks good».
Test evidence: наблюдать изменённую границу
Наличие теста не равно evidence. Если candidate меняет duplicate branch, а test проверяет unique branch, проверки изменения нет. Для каждой changed branch нужны input, expected output и forbidden side effect.
| Ошибка | Почему код кажется убедительным | Проверка | Действие |
|---|---|---|---|
| Scope mismatch | Полезный hunk соседствует с неназванным файлом. | Сверить каждый changed path с prompt-card. | Остановить и отделить расширение scope. |
| Contract mismatch | Happy path совпадает с ожиданием. | Прочитать fixed invalid и absent rows. | Вернуть diff владельцу контракта. |
| Test mismatch | Есть зелёный test, но для другой ветки. | Связать changed branch с input и forbidden effect. | Добавить focused negative case. |
| Unknown treated as pass | Нет явной ошибки или данных о конфликте. | Отметить, чего evidence не покрывает. | Не обещать security, compatibility или completeness. |
Линтер проверяет часть формы, но не знает доменный запрет, если он не закодирован в rule. Unit и integration test наблюдают свои scenarios. Model output добавляет гипотезу. Ни один слой не гарантирует correctness или security для всего system.
Unknowns — не пустая ячейка
Unknown не означает провал review. Он означает, что нельзя назвать risk закрытым без evidence. В synthetic package неизвестны consumers, schema, права, concurrency, deployment и security context; пример не притворяется production investigation. В реальной задаче unknown может потребовать сузить diff, позвать owner или не применять assistant к чувствительной области. Явная граница дешевле ложного обещания compatibility.
Воспроизводимая модель разделения evidence
Ниже используется только fixed in-memory module текущего пакета. Он принимает три заранее заданных case id, строит canonical report и отклоняет extra keys, sparse arrays, forged decisions и cyclic JSON. Это не evaluator модели и не прогон CI. Проверка полезна как защита структуры: красивый report нельзя выдать за другой case, а неявный unknown key не пройдёт в review draft.
import {
createFixedSyntheticAiCodingInput,
inspectSyntheticAiCodingAssistant,
planSyntheticAiCodingReview,
stopSyntheticAiCodingReview,
runAiCodingAssistantFixture,
} from './upgrade-2025-01.mjs';
const report = inspectSyntheticAiCodingAssistant(
createFixedSyntheticAiCodingInput('fixed-test-mismatch-v1'),
);
const draft = planSyntheticAiCodingReview(report);
const stopped = stopSyntheticAiCodingReview(draft);
if (!Object.values(runAiCodingAssistantFixture().assertions).every(Boolean)) {
throw new Error('fixed synthetic fixture failed');
}
console.log({ decision: report.decision.code, stopped: stopped.stopped });
// Fixed objects in memory only.
// No model call, real prompt, customer code, repository, secret, file, Git, network, CI, clock, production, metric or evaluation result is accessed.
// stopped records a teaching boundary; it is not a branch rollback or production action.
PASS fixture означает только сохранение exact input/report/draft contracts и stop boundary для трёх fixed cases. Он ничего не сообщает о реальной модели, prompts, source code, CI, метриках или production behavior.
Ограничения и следующий проверяемый шаг
Материал не утверждает, что один тест, линтер, review или помощник гарантируют correctness, security или отсутствие регрессий. GitHub рекомендует review и тестирование generated code, но это рекомендация по снижению риска, не сертификат результата. NIST SSDF задаёт рамку для интеграции secure-development practices в SDLC, но не знает доменный контракт вашего сервиса. Все examples и verdicts в статье — fixed synthetic literals без внешнего ввода и IO.
Следующий шаг: в следующем AI-assisted pull request заведите пять явных полей из первой таблицы. Для каждого changed branch заполните хотя бы один contract row и одно test evidence. Затем отдельной строкой напишите unknown. Ожидаемый результат — reviewer сможет либо показать точное несоответствие, либо принять ограниченный diff с названной границей, а не доверять коду потому, что он выглядит готовым.
Историческая граница января 2025
Использованы только источники, существовавшие к январю 2025: immutable GitHub Docs snapshot от 31 января 2025 для limitations, prompts and pull-request review, а также NIST SP 800-218 Version 1.1. В тексте нет поздних model features, современных evaluation claims или корпоративных результатов. Термины model output, contract, review evidence, test evidence и unknowns служат для разделения fixed synthetic data, а не для описания реального pipeline.
Проверяемые источники
- GitHub Docs snapshot: Copilot Chat limitations (immutable GitHub Docs commit 6a92295d, 31 January 2025) — Документация ограничивает область помощника контекстом и прямо предупреждает: код может выглядеть валидным, но не соответствовать намерению разработчика; для чувствительного к безопасности кода нужны review и тестирование. Граница: Это описание ограничений конкретного продукта и интерфейса. Оно не измеряет качество любого помощника, не доказывает корректность конкретного diff и не задаёт процесс merge.
- GitHub Docs snapshot: improving Copilot Chat performance (immutable GitHub Docs commit 6a92295d, 31 January 2025) — Документация рекомендует держать запрос в рамке задачи, использовать помощник как инструмент, а не замену инженера, и проверять сгенерированный код через secure coding и code review. Граница: Рекомендация не означает, что хороший prompt, линтер или один тест дают гарантию correctness, security или совместимости с конкретным репозиторием.
- GitHub Docs snapshot: reviewing proposed pull-request changes (immutable GitHub Docs commit 6a92295d, 31 January 2025) — Pull request review рассматривает commits, files и diff, позволяет оставить комментарии, approve или request changes; diff удобно просматривать по файлам. Граница: Документация описывает механизм review в GitHub. Она не утверждает, что просмотренный diff или approval сам по себе доказывает отсутствие дефектов.
- NIST SP 800-218: Secure Software Development Framework Version 1.1 (final publication, 3 February 2022) — SSDF задаёт набор практик безопасной разработки, которые можно встраивать в конкретный SDLC, чтобы снижать число уязвимостей и влияние невыявленных проблем. Граница: SSDF — высокоуровневая рамка. Он не заменяет знания предметного контракта, тестовые данные, human review или решение владельца об acceptable risk.