
Наш самый тяжёлый сквозной тест мог ждать агента 40 минут. В первой версии таймаут был 80 минут. Если прогон переставал делать полезную работу, я нередко узнавал об этом, лишь прождав большую часть этого срока.
У меня из головы не шёл Дуцзянъянь. По описанию ЮНЕСКО, это ирригационная система, которую начали строить в III веке до н. э. Она управляет течением реки без плотины. Ею пользуются до сих пор, и её по-прежнему обслуживают.
Для одного решения в моём тесте это сравнение оказалось полезным. Мне нужен был сигнал от работающего процесса, а не очередная догадка о том, сколько он должен длиться.
Тест управляет OwlMeans через браузер. Он регистрирует пользователя, отправляет промпт, ждёт спецификацию и подтверждает её. Для проекта поднимается новая песочница в Kubernetes. Потом агент разрабатывает первую пользовательскую историю.
В прогонах, на которых основана эта июньская статья, последний шаг занимал от 10 до 40 минут.
Если подменить модель моком, получишь ответ на другой вопрос. А здесь я хотел знать, смогут ли браузер, конвейер и окружение проекта пройти весь сценарий вместе.
Браузерная обвязка — это @owlmeans/test-ui поверх Playwright. Тестовый вход в ней идёт через наш механизм супервизора, поэтому именно этот тест не доказывает, что работает настоящий вход через Google.
Песочницу создаёт @owlmeans/kephemeral. Пакет выделяет временное пространство имён, хранилище, рабочую нагрузку и сетевую политику. А ещё следит за рабочей нагрузкой и умеет восстановить упавший под.
Такое восстановление помогает окружению оставаться доступным. Но верен ли ответ агента, тест из этого не узнает.
Цикл ожидания
Пока агент работает, он расходует токены. Изменения баланса в учёте я считал признаком того, что прогон ещё активен.
Тест снимал показания баланса каждые 20 секунд. Пока баланс менялся, детектор разрешал ждать дальше, а если баланс 10 минут стоял на месте, завершал прогон с ошибкой: возможно, тот завис.
Это была эвристика активности для нашего конвейера. Меняющийся баланс может означать и бесполезную работу. А неподвижный — и оправданное ожидание, и запоздалый учёт, и проблему за пределами модели.
Проверки ожидаемого состояния истории и поведения браузера мне всё равно были нужны. Расход токенов их не закроет.
Пороги я подбирал под июньский сценарий. Навязывать их другой команде как значения по умолчанию я бы не стал. Запиши, какие сигналы на самом деле подаёт твоя долгая операция, и уже потом решай, после какой паузы пора разбираться.
Рекомендации Playwright по тестированию тоже делают упор на изоляцию тестов. Наше медленное ожидание было лишь одним из источников ненадёжных прогонов.
Брошенная песочница
Прерви тест после выделения ресурсов — и он может оставить после себя проект. Тогда следующий прогон наткнётся на окружение, которое тест создавать не собирался.
Мы раз за разом использовали один и тот же тестовый профиль: до и после каждого прогона обвязка удаляла его тестовые проекты. Уборка перед прогоном была важна, потому что предыдущий процесс мог так и не дойти до сворачивания.
Область удаления должна оставаться узкой. Уборка, которая задевает посторонние проекты, была бы проблемой куда серьёзнее нестабильного теста.
Сообщай о сбоях уборки. Если ресурсы остались, а следующий тест стартует так, будто уборка удалась, полезные улики пропадают из виду. Прежде чем идти дальше, проверь, в каком состоянии всё на самом деле.
Так в этой конфигурации исчез повторяющийся источник помех. Последующие сбои всё равно приходилось расследовать.
Дорога домой

Кластер работал на сервере у меня дома. У домашнего подключения к интернету не было публичного адреса, пригодного для входящих соединений.
Маршрут строился на облачной виртуальной машине с зарезервированным публичным IP, прокси на Nginx и WireGuard. Трафик шёл от Cloudflare к промежуточному серверу, по зашифрованному туннелю — на домашний сервер, а оттуда — в сервис Kubernetes.
Это была наша схема с промежуточным сервером. Не путай её с вариантом, где весь транспорт держится на коннекторе Cloudflare Tunnel.
Маршрут настраивали Terraform, Ansible и shell-скрипты, а сторожевой процесс каждые 60 секунд проверял правила файрвола и исправлял те расхождения, которые умел распознать. Отдельные правила он мог восстановить. Сеть всё равно могла отказать.
Зарезервированный адрес должен был переживать сносы инфраструктуры, описанные в конфигурации. Состояние у провайдера, учётные данные и конфигурацию всё равно нужно было поддерживать.
Теперь сбой сети должен давать мне именно сбой сети, который можно изучить. Если дольше ждать агента, сломанный маршрут к его окружению не починится.
После этой работы тест стало проще диагностировать. Я мог отличить друг от друга затихшего агента, брошенную песочницу и оборванный путь в кластер, а не давать всем трём ещё час.
Обходной путь для входа, библиотеку выделения ресурсов, права на уборку, учёт токенов и сетевую конфигурацию по-прежнему нужно поддерживать. Изменение любого из них может сломать одно из допущений теста.
В Услуги OwlMeans может входить такая инфраструктура для поставки и тестирования. Работа должна учитывать реальный стек и то, как он на самом деле отказывает; июньская конфигурация — не обещание развернуть любую систему за несколько дней.
Ждать я стал меньше, а поддерживать обвязку — больше. Для этого сценария такой обмен того стоил.
Впервые опубликовано 23 июня 2026 года. Отредактировано 15 октября 2026 года: детектор активности отделён от проверок правильности, а утверждения об инфраструктуре снабжены оговорками.