SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-24

Навык Old Coder: как проверять EVIDENCE вместо кода

Метод Old Coder заменяет чтение diff проверкой SPEC и отчетов EVIDENCE. Узнайте, почему мутационное тестирование эффективнее покрытия кода и как проверять отчеты агента.

Что на самом деле меняет навык Old Coder

Навык Old Coder заменяет проверку кода проверкой документации. Ваш агент по написанию кода составляет SPEC до того, как написать хоть одну строку кода; вы утверждаете этот документ, и только после этого агент приступает к реализации. Затем он прогоняет свою работу через фиксированный набор автоматических проверок, называемый gauntlet, и предоставляет вам отчет EVIDENCE, содержащий точные команды и фактические показатели. Вы читаете два документа. Вы никогда не читаете diff.

Этот обмен работает только в том случае, если оба документа вызывают такое же доверие, какое раньше вызывал diff. SPEC вызывает доверие, потому что вы утвердили его до появления кода, поэтому он не мог быть подогнан под уже написанный агентом код. Отчет EVIDENCE вызывает доверие, потому что каждое число в нем получено в результате выполнения команды, которую вы можете запустить самостоятельно и увидеть, что она выдает тот же результат. Если любая из этих частей становится недостаточно строгой, вы меняете проверку на краткое изложение проверки, что хуже, чем чтение diff, так как создает ложное ощущение завершенности.

Этот навык представляет собой обычный markdown, поэтому он работает с любым агентом, который следует письменным инструкциям: Claude Code, Codex CLI, Cursor или вашим собственным циклом. Он относится к тому же семейству, что и навык персонажа ленивого старшего разработчика Ponytail. Если формат файла для вас в новинку, в том, что такое навык агента и как агент его загружает описаны технические аспекты.

SPEC — это единственное решение, которое вы принимаете самостоятельно

SPEC — это план тестирования, составляемый до написания кода. Файл спецификации должен содержать четыре элемента.

  • Конкретные сценарии: входные данные, ожидаемые результаты, граничные случаи, ошибки. divide(1, 0) raises ZeroDivisionError with message X, а не «обрабатывает некорректный ввод».
  • Негативные ограничения: что не должно измениться, например, существующие тесты и сигнатуры публичных API.
  • План настройки: каждый инструмент и каждая новая зависимость с кратким пояснением, зачем они нужны.
  • Абсолютный путь к файлу, чтобы его можно было открыть без поиска.

План настройки демонстрирует дату отсечки обучающих данных модели, так как инструмент или зафиксированная версия из памяти могут устареть на год. Поэтому полезно предоставить агенту собственный экземпляр SearXNG для поиска в сети и заставить его проверять актуальные версии перед тем, как предлагать их.

Утвердите спецификацию, а затем закоммитьте её. Спецификация, которую можно редактировать после утверждения, не является контрактом, а коммит позволяет позже проверить, что доказательства соответствуют тому, что вы подписали.

Это единственное решение «да» или «нет», которое остаётся за вами; в этом заключается его суть и риск. Относитесь к этому как к внесению изменений в production, потому что это именно то, чем оно является. Если вы уже используете явный шлюз подтверждения перед действиями агента, утверждение спецификации встраивается в то же самое место вашего рабочего процесса.

Отчет EVIDENCE: цифры и соответствующие команды

Отчет EVIDENCE — это то, что вы читаете в конце. Данный навык требует, чтобы каждое поведение по спецификации было сопоставлено с тестом, который его проверяет, каждый уровень проверки был представлен вместе с выполненной командой и ее фактическим результатом, каждое число было взято из одного свежего запуска после последнего изменения кода, а каждый пропущенный уровень был указан с пояснением причины. Прилагательные не являются доказательством. «Все 41 тест пройдены, покрытие 49/49 операторов» — это результат. «Хорошо протестировано» — нет.

Демонстрационный отчет в репозитории, demo-rate-limiter/evidence.md, определяет свое исходное состояние как коммит плюс sha256 tree hash. Эта строка важнее, чем кажется, поскольку она указывает, какие именно байты привели к этим цифрам. Без нее отчет может незаметно описывать рабочее дерево, которое больше не существует.

Три правила против манипуляций поддерживают честность отчета, и файл навыка определяет их как абсолютные. Никогда не ослабляйте тест, чтобы он прошел: никаких расширенных утверждений, никаких повышенных допусков. Никогда не редактируйте тест и реализацию на одном и том же шаге для достижения статуса green, так как одновременное редактирование скрывает, что именно было неверным. Никогда не сообщайте об уровне, который не был запущен: «пропущено, инструмент недоступен, вместо этого ручная мутация» сохраняет доверие, а выдуманный результат разрушает всю схему.

Установка навыка и фиксация используемого коммита

Репозиторий находится по адресу AmazingAng/old-coder и распространяется по лицензии MIT. В файле README приведена команда для установки в одну строку через CLI навыков:

npx skills add https://github.com/amazingang/old-coder

Эта команда устанавливает текущую версию, которая находится в main на момент запуска, при этом сам CLI постоянно обновляется. Перед тем как считать навык активным, проверьте, куда были скопированы файлы:

ls ~/.claude/skills/old-coder/

Вы должны увидеть SKILL.md и каталог references/. Если их там нет, значит, навык находится не в той директории, где его ожидает найти Claude Code. В старых версиях CLI навыков запись производилась в ~/.agents/skills/ без создания ссылок в ~/.claude/skills/, поэтому файлы присутствовали на диске, но агент их не загружал. Запуск npx skills@latest add ... позволяет избежать проблем с устаревшей версией CLI.

Отдавайте предпочтение ручной установке, так как это позволяет зафиксировать, что именно вы установили:

git clone https://github.com/AmazingAng/old-coder.git
cd old-coder
git checkout acc5a89
git rev-parse HEAD
mkdir -p ~/.claude/skills
cp -r skills/old-coder ~/.claude/skills/

acc5a89 — это состояние ветки main на 17 августа 2026 года. Выберите нужный вам коммит и запишите его. Репозиторий активно развивается, а ссылки на тесты, шаблоны и протокол верификации уже перемещались между файлами. Если в ваших отчетах EVIDENCE не указано, какая версия навыка использовалась для оценки, вы не сможете отличить изменения в вашем коде от изменений в правилах. Храните хеш коммита рядом с файлом SPEC, в том же репозитории, где находится управляемый им код.

Для агента, который не считывает ~/.claude/skills, добавьте skills/old-coder/SKILL.md и skills/old-coder/references/gauntlet.md в его системный промпт или файл правил. На этом интеграция завершена.

Что выполняется внутри gauntlet

Gauntlet — это набор уровней, запускаемых после того, как все проверки спецификаций пройдены успешно. В методике они называются так:

  • Полный набор тестов для поиска регрессий. Допускается ноль новых ошибок, при этом все существующие ранее ошибки сначала фиксируются как базовый уровень.
  • Статическая типизация, линтинг и форматирование для предотвращения целых классов ошибок и отклонений в коде.
  • Покрытие измененных строк кода, которое должно завершаться с ненулевым кодом выхода, если порог не достигнут.
  • Мутационное тестирование для проверки тестов, которые фактически ничего не подтверждают.
  • Тестирование на основе свойств (property-based testing) для поиска граничных случаев, которые никто не предусмотрел.
  • Бюджет сложности, один реальный запуск программы, сканирование цепочки поставок и секретов, а также запуск набора тестов в случайном порядке для проверки их независимости.
  • Предметные уровни, выбранные исходя из рисков задачи: стресс-тестирование конкурентности, проверка совместимости API, репетиция отката и бенчмарки задержек.

В демо-версии они объединены в один скрипт demo-rate-limiter/tools/gauntlet.sh, использующий зафиксированный набор инструментов в requirements-dev.txt: pytest, pytest-cov, coverage, hypothesis, mypy, ruff, pip-audit и pytest-randomly. Каждая утилита закреплена за конкретной версией (например, pytest 9.1.1, ruff 0.16.0 на август 2026 года). Запустите его:

cd demo-rate-limiter
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e .
./tools/gauntlet.sh

Скрипт выводит баннер для каждого уровня, например === tests + coverage === и === mutation ===, и завершается выводом === gauntlet: all layers green ===. Он выполняется под set -e, поэтому первый же сломанный уровень останавливает выполнение, и финальный баннер не выводится. Появление финального баннера — это и есть проверка: он означает, что каждый уровень выше завершился с кодом 0.

Одна деталь в этом скрипте заслуживает того, чтобы скопировать её в свой собственный. Уровень покрытия кода написан так:

pytest -q --cov=ratelimiter --cov-report=term-missing --cov-fail-under=100

Без --cov-fail-under утилита pytest --cov выведет процент покрытия и завершится с кодом 0, независимо от того, насколько сильно упало покрытие. Это пример уровня, который не блокирует выполнение внутри скрипта, первая строка которого обещает остановиться при первой же ошибке. Уровень gauntlet, который не может завершиться с ошибкой, — это просто декорация.

Что мутационное тестирование дает сверх покрытия кода

Покрытие кода отвечает на один вопрос: выполнил ли набор тестов эту строку. Оно не отвечает на вопрос, который действительно важен: сработало бы хоть одно утверждение (assertion), если бы в этой строке была ошибка. Тест, который вызывает функцию, но ничего не проверяет, все равно показывает полное покрытие всех затронутых строк. Покрытие выявляет нетестируемый код. Оно не выявляет тесты, которые ничего не проверяют.

Мутационное тестирование напрямую отвечает на второй вопрос. Оно намеренно изменяет код, внося по одному небольшому исправлению за раз, и перезапускает набор тестов. Если тесты падают, мутант считается «убитым» — значит, какое-то утверждение отслеживало это поведение. Если тесты проходят успешно, мутант выжил: строка была выполнена, но результат никто не проверил.

Демонстрация выполняет это с помощью tools/mutants.py. Она вносит пронумерованные единичные изменения в src/ratelimiter/__init__.py, запускает pytest после каждого из них, а затем восстанавливает файл. Эти изменения имитируют ошибки, которые совершает уставший человек: >= превращается в > или удаляется возвращаемое значение.

Правило «убийства» в этом исполнителе — это то, в чем ошибается большинство самописных скриптов для мутационного тестирования. Только код завершения pytest, равный 1, считается «убийством», так как 1 означает, что тесты были запущены и хотя бы один из них провалился. Код завершения 0 означает, что мутант выжил. Любой другой код — ошибка сбора или отсутствие найденных тестов — означает, что ничего не было проверено, и это нельзя учитывать. Скрипт, который считает любой «ненулевой» код «убийством», засчитывает собственные сбои как успехи, и этот показатель будет только расти.

В том же файле есть еще две важные детали. Один мутант, M11, исключен из списка, так как он эквивалентен исходному коду: удаление одной истекшей записи вместо всех истекших записей дает тот же наблюдаемый результат при монотонных часах, поэтому ни один тест не может его «убить». Кроме того, исполнитель устанавливает PYTHONDONTWRITEBYTECODE=1, пока «перчатка» (gauntlet) предварительно удаляет все __pycache__, потому что два мутанта одинакового размера, созданные в одну и ту же секунду, могут использовать общий кэш .pyc, и тогда второй мутант унаследует вердикт первого.

Эта опасность — причина, по которой «перчатка» запускает негативный контроль перед основным проходом мутаций:

.venv/bin/python tools/mutants.py --negative-control
.venv/bin/python tools/mutants.py

Контроль запускает двух мутантов с зафиксированным временем модификации: одного, который должен быть убит, и одного, который строго эквивалентен и должен выжить. Если оба возвращаются как «убитые», значит, кэш байт-кода просочился между запусками, и каждый показатель «убийств» в отчете завышен. Это правило мастерства в отношении проверок, воплощенное в жизнь. pytest и mypy годами оттачивали свое поведение при сбоях. Скрипт, который вы написали на прошлой неделе, этого не делал, поэтому докажите, что он может выдавать ошибку, прежде чем доверять ему при успешном прохождении, и зафиксируйте это доказательство в EVIDENCE.

ChartMutants killed by each suite run alone (demo-rate-limiter evidence.md, August 2026)
The data behind this chart
[
  {
    "label": "Scenario tests",
    "mutants_killed": 22,
    "mutants_run": 22
  },
  {
    "label": "Property tests",
    "mutants_killed": 3,
    "mutants_run": 22
  }
]

Собственный отчет EVIDENCE в демонстрации показывает, почему единое агрегированное число все еще скрывает детали. Набор сценариев убил 22 из 22 мутантов. Тесты на основе свойств (property-based tests), запущенные отдельно против тех же мутантов, убили 3. «Убийство» приписывается тому тесту, который падает первым, поэтому идеальный общий результат подтверждает набор тестов в целом, но ничего не говорит о каждом отдельном уровне внутри него. Эти свойства все равно занимают свое место, так как они находят такие формы входных данных, которые никто не перечислил вручную. Они не несут основную нагрузку по проверке корректности, и вы узнаете об этом, только измеряя каждый уровень отдельно.

Запускайте проверку на сервере, а не на локальном компьютере

Отчет EVIDENCE — это утверждение о том, что определенные команды привели к получению конкретных числовых показателей. Это утверждение можно проверить только в том случае, если кто-то другой сможет получить те же результаты, а локальный компьютер — худшее место для таких попыток. Ваша версия Python может отличаться патч-релизом, а ваш PATH содержит инструменты, которых не будет на другой машине. Мутационное тестирование усугубляет ситуацию, так как оно перезапускает весь набор тестов для каждого мутанта, поэтому один только список демонстрации означает 22 дополнительных запусков набора тестов.

Поместите всё в контейнер на VPS. Контейнер фиксирует операционную систему и интерпретатор, закрепленный requirements-dev.txt фиксирует инструменты, а VPS предоставляет машину, на которой не запущен ваш браузер.

FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
      python3 python3-venv git ca-certificates \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /work
docker build -t gauntlet:24.04 .
docker run --rm -v "$PWD:/work" -w /work/demo-rate-limiter gauntlet:24.04 \
  sh -c 'python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e . && ./tools/gauntlet.sh'

Успешный запуск завершается на === gauntlet: all layers green ===. Два этапа требуют исходящего сетевого соединения: установка инструментов через pip и слой pip-audit, который проверяет ваши зависимости через сервис поиска уязвимостей. Автономный запуск не пропускает этот слой молча. Он завершается ошибкой, что и требуется от системы контроля качества.

Для запуска при каждом push тот же скрипт становится одним из шагов CI-задания. Репозиторий выполняет собственную проверку в GitHub Actions на ubuntu-latest с использованием Python 3.12. Укажите runs-on на self-hosted runner, и задание будет выполнено на вашем VPS:

name: gauntlet
on:
  push:
    branches: [main]
jobs:
  gauntlet:
    runs-on: self-hosted
    defaults:
      run:
        working-directory: demo-rate-limiter
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e .
      - run: ./tools/gauntlet.sh

Оставьте триггер только для push в ветки, которые вы контролируете. Self-hosted runner, который также собирает pull requests из форков, запускает чужой код на вашем сервере с учетными данными вашего runner. Тот же принцип применим и к самому агенту: предоставьте ему одноразовую виртуальную машину, которую можно удалить, вместо вашей рабочей станции. Если вы хотите, чтобы результат проверки попадал туда, где обсуждаются изменения, подключите его к агенту для проверки PR, размещенному на собственном сервере.

Где этот подход не работает

Первый сбой носит структурный характер, и никакие инструменты его не устранят. Gauntlet превращает ограничения в вашей SPEC в исполняемые доказательства. Он не может подтвердить, что SPEC была верной. Если вы утвердите спецификацию, содержащую неверные требования, вы получите безупречный отчет EVIDENCE для неверной программы: полное покрытие, каждый мутант уничтожен, каждый уровень отмечен зеленым, а программное обеспечение делает не то, что вам нужно. Каждый час, сэкономленный на чтении diff, должен быть потрачен на чтение спецификации.

Второй сбой связан с проверками. Репозиторий честно сообщает об этом в своем файле доказательств. Протокол независимой верификации выполнил шесть раундов, шестой вернул failed, а исправления, внесенные после этого раунда, не проходили повторную проверку. Таким образом, состояние, которое поставляется (ships), не было верифицировано от начала до конца. Уровень проверки shell-скриптов отмечен как недоступный, а не как пройденный. В ходе предыдущих раундов были обнаружены реальные дефекты поведения и ненадежный механизм генерации мутантов, работавший для состояний, которые уже были помечены как успешные. Зеленый статус gauntlet не является самодостаточным подтверждением корректности.

Третий сбой — это область охвата. Список мутантов, составленный вручную, покрывает только те ошибки, которые кто-то догадался внедрить, а каждый эквивалентный мутант, удаленный из этого списка, — это решение, которому вы доверяете. Готовые инструменты мутационного тестирования (mutmut, cosmic-ray, Stryker, PIT) генерируют мутантов систематически, и их использование является более предпочтительным вариантом по умолчанию для любого языка программирования, где они доступны.

Соотнесение усилий с уровнем риска

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

  • Уровень 1, тривиальный: опечатка, комментарий, значение конфигурации. Полный набор проверок плюс линтинг, новые тесты не требуются, необходимо лишь одно предложение с пояснением, почему они не нужны.
  • Уровень 2, обычный: исправление ошибки или небольшая функциональность. Полный цикл разработки; исправление ошибки должно начинаться с написания падающего теста, воспроизводящего проблему, чтобы вчерашняя ошибка стала завтрашним регрессионным тестом.
  • Уровень 3, критический: финансовые операции, аутентификация, потеря данных, конкурентность, публичный API. Начните с составления модели отказов, перечисляющей способы, которыми данные изменения могут нанести вред. Добавьте уровень защиты для каждого сценария, затем выполните полный цикл разработки, включая property-based тесты, мутационное тестирование и один этап целенаправленной атаки на реализацию с использованием некорректных входных данных.

Для уровня 3 также предусмотрен экспериментальный этап. Второй агент с «чистым» контекстом получает только контракт задачи, утвержденную SPEC и состояние исходного кода. Он пытается нарушить работоспособность готового решения до того, как будет подписан EVIDENCE. Он не вносит исправлений, а только составляет отчет, который затем оценивается человеком. Это снижает корреляцию, возникающую из-за общего контекста задачи. Это не снижает корреляцию, возникающую из-за использования одной и той же модели.

Если вы предпочитаете создать собственный навык, а не использовать этот, в разделе написание собственного навыка агента описана структура файлов и поле description, определяющее, когда агент загружает этот навык.

FAQ

Что обнаруживает мутационное тестирование, чего не видит покрытие кода?

Покрытие кода фиксирует факт выполнения строки. Оно не может определить, сработала бы какая-либо проверка (assertion), если бы эта строка содержала ошибку. Поэтому тест, который вызывает функцию, но ничего не проверяет, всё равно отчитывается о полном покрытии. Мутационное тестирование намеренно изменяет код, внося по одному исправлению за раз, и перезапускает набор тестов. Выживший мутант означает, что строка была выполнена, но результат её работы ничем не проверялся. Именно поэтому в навыках указано правило «никогда не гнаться за цифрами покрытия» как абсолютное, а мутационное тестирование названо уровнем, который выявляет манипуляции с метриками.

Нужно ли мне по-прежнему читать код, который пишет мой агент?

В рамках этого рабочего процесса вы читаете SPEC до начала написания кода и отчёт EVIDENCE после, а также выборочно проверяете отчёт, повторно запуская указанные в нём команды. Просмотр diff становится необязательным. Важный нюанс: теперь всё ваше суждение опирается на один документ, так как спецификация с неверным требованием приведёт к успешному прохождению тестов для программы, которая вам не нужна. Потратьте сэкономленное время на работу со спецификацией.

Могу ли я запускать набор тестов Old Coder в CI на собственном сервере?

Да, и это лучший вариант для него. Установите зафиксированную версию инструментария из requirements-dev.txt внутри контейнера и запускайте скрипт тестирования проекта как один из шагов сборки. В GitHub Actions установите runs-on: self-hosted и зарегистрируйте runner на вашем VPS. Ограничьте запуск триггером на push в ветки, которые вы контролируете, так как self-hosted runner, собирающий pull requests из форков, выполняет недоверенный код с правами доступа вашего runner.

Какую версию навыка мне следует установить?

Зафиксируйте одну версию. Репозиторий находится в стадии активной разработки, его справочные файлы уже были разделены и перемещены, поэтому отчёт, созданный в прошлом месяце, мог быть оценён по другим правилам. Клонируйте репозиторий, переключитесь на конкретный коммит, скопируйте skills/old-coder в ~/.claude/skills/ и запишите хеш этого коммита рядом с вашим SPEC. Тогда изменение в ваших доказательствах (evidence) будет означать изменение в вашем коде.