Тема
Первый harness
В этой главе начнём с программы, которую можно прочитать целиком: examples/harness-labs/01-manual-runner/runner.py. Она принимает задачу, проверяет наличие нужных файлов, копирует подготовленный результат и запускает тесты. Код можно написать вручную, сверяясь с этим образцом.
Ответ модели здесь заменён fixture, папкой с заранее подготовленными файлами. На этом этапе мы проверяем работу runner: порядок действий, обработку ошибок и передачу результата. Генерацию кода моделью добавим как отдельный backend, когда будем готовы проверять и её.
Прочитайте основной цикл
В main после разбора аргументов выполняются три шага:
python
require_context(workspace, task)
apply_scripted_fixture(workspace, task_path, task)
if run_checks(workspace, task):
print("done: all checks passed")
return 0require_context ищет спецификацию и остальные файлы из задачи. apply_scripted_fixture копирует файлы в рабочую папку, включая замену существующих. run_checks выполняет команды с cwd=workspace и проверяет их коды завершения.
Здесь уже есть решения, которые стоит заметить. Пути fixture отсчитываются от файла задачи. Команды получают аргументы списком и не передаются через shell. После падения одной проверки runner всё равно запускает остальные. Если список checks пуст, функция вернёт успех - это пробел, к которому вернёмся в проверке harness.
Напишите v01 и сравните с образцом
Для собственной версии понадобятся argparse, json, pathlib, shutil и subprocess из стандартной библиотеки. Сначала реализуйте чтение --task и --workspace. Затем проверку наличия контекста, копирование fixture и запуск команд. Не добавляйте автоматические повторы, пока не умеете отличать успешную проверку от неуспешной.
Сравнивать с образцом стоит поведение: что происходит при отсутствии спецификации, что печатается после ошибки теста, какой код получает вызывающий процесс. Имена вспомогательных функций несущественны.
В текущем v01 результат представлен так:
| Код завершения | Сообщение | Что это означает в реализации |
|---|---|---|
0 | done | Все перечисленные команды завершились с кодом 0. |
1 | failed | Хотя бы одна команда завершилась с ненулевым кодом. |
2 | blocked | Исключение, включая отсутствие контекста или fixture. |
Последняя строка слишком широкая: ошибка самого runner тоже попадёт в blocked. При развитии harness такие сбои нужно различать. Также done пока не означает, что все требования спецификации проверены или ревью закончено.
Соберите v02 через v01
Полный порядок команд приведён в практике. Для первого шага нужна чистая рабочая папка со спецификациями harness:
bash
LAB_ROOT=$(mktemp -d /tmp/handbook-labs.XXXXXX)
cp -a examples/harness-labs/harness-seed "$LAB_ROOT/harness-v02"
python3 examples/harness-labs/01-manual-runner/runner.py \
--task examples/harness-labs/tasks/02-build-observable-runner.json \
--workspace "$LAB_ROOT/harness-v02"Запускайте из корня репозитория курса. Если используете собственный v01, подставьте путь к нему вместо пути к образцу.
В рабочей папке появится runner.py версии v02. Задача проверяет его компиляцию через py_compile. Она ещё не проверяет ведение журнала: JSONL появится, когда вы запустите полученный v02 на следующей задаче. Сам v01 выводит сообщения только в терминал.
Что добавляют следующие версии
| Версия | Изменение | Как увидеть его в работе |
|---|---|---|
v01 | Чтение задачи, применение fixture, запуск проверок. | Сообщения в терминале и код завершения. |
v02 | JSONL-журнал событий и вывод проверок в нём. | Выполнить первую задачу ALGI и открыть runs/ рядом с runner. |
v03 | Проверка путей записи и команд по policy. | Передать запрещённую команду и увидеть отказ в журнале. |
Во всех трёх версиях нет изоляции процесса, вызова модели, автоматического ревью, тайм-аута команды и отката частичных изменений. Их нельзя считать готовым окружением для запуска недоверенного кода. Используйте отдельные временные папки и подготовленные учебные fixtures.
Где подключать настоящий backend
При замене fixture на агентский CLI сохраните остальную последовательность. Адаптер должен запустить инструмент в нужной рабочей папке, передать задачу и контекст, дождаться завершения, сохранить доступные результаты. После него runner запускает свои проверки над полученными файлами.
Проверьте также завершение по тайм-ауту и остановку дочерних процессов. Возврат из вызова CLI не всегда означает, что все запущенные им процессы завершились. Ошибки адаптера, проверки продукта и отказ в доступе должны иметь различимые причины в журнале.
Далее добавим проверку доступа. Это позволит разобрать, в какой момент runner может отказать в действии и какие ограничения одной проверкой команды обеспечить нельзя.