- Зачем:
- закрыть отложенный заход эксперимента: внешнее ревью результата
автономной петли моделью другой родословной (дизайн-линия, не Кодекс).
- Что:
- добавлена секция об итогах ревью: прогноз по находке A не оправдался,
петля её поймала (try→fresh + регрессионный тест в коммите 640050e).
- зафиксировано, что внешний взгляд добавил сквозные находки (расхождение
живого/восстановленного путей, форма распределения длины визита).
- уточнена гипотеза о пользе reviewer-а другой родословной.
- Проверка:
- git show --stat HEAD; чтение docs/research/2026-06-11-subagent-coordinator-experiment.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
32 KiB
Эксперимент с координатором субагентов
Status: Research Дата: 2026-06-11
Зачем проводили эксперимент
Проверяли рабочую схему, где верхнеуровневый агент не реализует задачу сам, а управляет цепочкой субагентов:
- выдаёт одному worker-субагенту один локальный issue;
- требует от worker-а работать через
/goalи/tdd; - получает отчёт;
- просит того же worker-а сделать саморевью;
- классифицирует находки;
- запускает исправления только по важным пунктам;
- при необходимости подключает отдельного reviewer-субагента;
- сам выполняет финальную проверку, разделяет коммиты и принимает решение о переходе к следующей задаче.
Цель эксперимента была не только закрыть задачи генератора, но и понять, можно ли из такого процесса сделать повторяемый агентный навык.
Контекст
Эксперимент проводился в ветке feature/data-generator на последних трёх
срезах переработки steady-stream генератора:
.scratch/feature-data-generator/issues/05-intensity-and-flow-calibration.md.scratch/feature-data-generator/issues/06-state-v2-and-restart.md.scratch/feature-data-generator/issues/07-service-integration-and-docs.md
Задачи были зависимыми и меняли близкие места генератора, поэтому параллелить их было нельзя. Каждая следующая задача должна была стартовать только после коммита предыдущей.
Итоговые рабочие коммиты:
8f1e997 feat(generator): откалиброван поток steady-stream генератора640050e feat(generator): добавлено состояние версии 2 для рестартов6c4e0f4 feat(generator): подключена новая модель к steady-stream сервису
Отдельные docs-коммиты фиксировали ход и выводы эксперимента.
Начальная гипотеза
Ожидалось, что полезное разделение ролей такое:
- Координатор держит порядок, границы задач, проверки, коммиты и решение, какие замечания действительно надо чинить.
- Worker-субагент глубоко погружается в один issue и реализует его через короткие TDD-срезы.
- Саморевью worker-а полезно, потому что у него уже есть подробный контекст реализации.
- Отдельный reviewer-субагент нужен не всегда, а на рискованных переходах или после больших исправлений.
Верхнеуровневому координатору решили не включать /goal: это могло бы сместить
поведение в сторону «закрыть большую цель любой ценой», тогда как задача
координатора — управлять этапами и останавливаться на контрольных точках.
Словарь ролей
- Координатор — верхнеуровневый агент в текущей сессии. Он не пишет основную реализацию, а управляет порядком работ, проверяет границы, классифицирует замечания, запускает дополнительные проверки и делает коммиты.
- Worker — субагент-исполнитель. Он получает один issue, работает в общем рабочем дереве, меняет файлы, запускает тесты, но не коммитит.
- Саморевью worker-а — отдельная фаза после реализации. Worker временно меняет роль с исполнителя на проверяющего свою работу, но сначала только выдаёт находки, не исправляя их.
- Reviewer — отдельный субагент для независимой проверки. По умолчанию он работает только на чтение: анализирует diff, issue, спеки и тесты, но не редактирует файлы.
- Находка — гипотеза о проблеме, а не автоматическое требование правки. Решение принимает координатор.
Важная техническая деталь: все агенты работают с одним рабочим деревом. Поэтому
координатор обязан регулярно проверять git status --short и явно отделять
рабочие изменения задачи от процессных заметок, handoff-файлов и чужих правок.
Протокол, который сложился в эксперименте
Рабочий цикл на задачу:
- Проверить чистоту дерева и, если нужно, отделить процессные правки отдельным docs-коммитом.
- Запустить нового worker-субагента на один issue.
- В задании worker-у указать:
/goal-образную цель;/tddкак методику;- границы задачи;
- запрет на коммит;
- запрет на откат чужих изменений;
- требование финального отчёта: файлы, тесты, проверки, критерии, риски,
git status --short.
- После реализации запросить у того же worker-а саморевью без правок.
- Классифицировать находки.
- Вернуть worker-у только одобренные правки.
- Запустить локальные проверки координатора.
- На сложных местах запустить отдельного reviewer-а.
- Снова классифицировать находки reviewer-а.
- При больших исправлениях выполнить второй reviewer-круг.
- Коммит делает координатор.
- Старый worker закрывается; следующая задача стартует в новом worker-е.
Это описание фиксирует фактический процесс эксперимента. Будущий скилл не обязан механически повторять каждый шаг, но должен сохранить инварианты ниже.
Классификация reviewer-находок
Reviewer не является источником истины. Его выводы работают как гипотезы, которые координатор обязан проверить и классифицировать:
чинить до коммита— реальный дефект, нарушение acceptance criteria или слабое доказательство ключевого поведения;записать как риск— важно помнить, но не блокирует текущий коммит;ложная тревога— reviewer неверно понял код, тест или границы задачи;вне скоупа— может быть полезно позже, но не относится к текущему issue.
Это оказалось критически важным. Без фильтрации reviewer легко превращается в источник расползания задачи.
Наблюдения по задаче 05
Задача 05 была самой дорогой по обратной связи: калибровка статистической модели требует длинных симуляций и не даёт быстрый «один инвариант — один результат».
Worker долго работал без промежуточного отчёта. Координатор сначала поставил мягкий статус-чек, затем прервал агента ради статуса. Это не сломало работу, но показало: для задач со статистическими тестами стоит заранее закладывать контрольные точки.
Саморевью worker-а нашло реальные проблемы:
docker-compose.ymlоставлял старую интенсивностьGEN_LAMBDA_BASE_PER_MIN=200;- тест межсессионной паузы был слишком широким;
- в тестовой конфигурации оставались старые числа;
- README мог быть двусмысленным про
GEN_MIN/MAX_EVENTS_PER_TICK.
Координаторская проверка добавила отдельную ценность: был найден конфликт между
новым GEN_LAMBDA_BASE_PER_MIN=30 и старым нижним пределом
GEN_MIN_EVENTS_PER_TICK=5. При тике 5 секунд это давало минимум 60 событий в
минуту и ломало критерий интенсивности.
Вывод: саморевью хорошо ловит локальные несостыковки реализации, но координатор нужен для проверки связей между дефолтами, обычным запуском, документацией и acceptance criteria.
Наблюдения по задаче 06
Задача 06 показала, зачем нужен отдельный reviewer-субагент между задачами. Саморевью worker-а и отдельный reviewer независимо нашли две существенные проблемы:
- битое state v2 с валидным состоянием ГПСЧ могло пройти
from_dict_safe, а затем уронить сервис уже вrestore_state; - снимок активных визитов сохранялся полными batch-словарями и на верхних лимитах получался порядка мегабайт, хотя спека говорила о компактном состоянии.
Координатор проверил обе гипотезы локально:
restore_stateдействительно падал на битой вложенной структуре;- оценка JSON-снимка при 200 активных визитах и популяции 300 дала около 4.7 MB.
Обе находки были классифицированы как чинить до коммита.
После исправления компактного состояния понадобился второй reviewer-круг. Он
нашёл новый дефект уже в исправленной версии: формально похожий v2-state с
population=[] или строковым pending_visit_births проходил первичную
загрузку, но затем оставлял поток без пользователей или ронял следующий тик.
Координатор подтвердил это локальной проверкой и вернул worker-у как
обязательный пункт второго круга. Затем координатор дополнительно нашёл
парный случай: пользователь с active_click_id, но без соответствующего
active_visit.
Вывод: если исправление по reviewer-находке меняет дизайн, нужен повторный reviewer-круг. Иначе можно закрыть старый дефект и внести новый рядом.
Наблюдения по задаче 07
Задача 07 показала другую пользу reviewer-а: проверку силы доказательства, а не только поиск падений.
Worker добавил сервисный тест с мок-публикацией. Он доказывал, что сервисный
тик публикует связанные сообщения во все четыре топика, но внешний reviewer
заметил: один опубликованный event ещё слабо доказывает уход от старой плоской
модели. Для acceptance criteria было важнее доказать невырожденную модель:
один визит должен дать несколько событий с одним click_id.
Координатор классифицировал это как чинить до коммита. Тест был усилен:
сервисный контур теперь делает несколько тиков и проверяет несколько событий
одного визита с общим click_id, разными event_id и согласованными
location/device/geo.
Reviewer также поймал документационные неточности:
- README слишком широко говорил, что переменные из таблицы проброшены через
compose, хотя
KAFKA_BOOTSTRAP_SERVERSиGEN_DATA_DIRдолжны оставаться безопасными внутренними значениями контейнера; docs/OPERATIONS.mdотставал от новых параметров и метрик.
Вывод: финальная интеграционная задача требует reviewer-а не только по коду, но и по доказательности тестов и честности документации.
Что сработало
- Один worker на один issue хорошо удерживает контекст и границы.
/goal-образное задание worker-у работает как хороший контракт даже тогда, когда координатор сам не находится в goal-режиме.- Саморевью того же worker-а полезно, если явно запретить правки на первом шаге и попросить сначала выдать находки.
- Отдельный reviewer полезен на границах задач и после больших исправлений.
- Координаторская классификация находок обязательна.
- Коммиты должен делать координатор: это последняя точка контроля состава diff.
- Процессные наблюдения лучше фиксировать отдельно от рабочих коммитов.
Что не стоит автоматизировать слепо
- Не стоит всегда запускать reviewer-а после каждой мелкой правки. Это дорого и может раздувать задачу.
- Не стоит принимать все reviewer-находки как правду.
- Не стоит давать координатору верхнеуровневый
/goalна всю цепочку, если от него требуется управленческая осторожность, а не слепое достижение цели. - Не стоит смешивать handoff-наблюдения с рабочими коммитами задач.
- Не стоит требовать реальный Kafka-стек для каждого шага, если acceptance criteria допускают мок-публикацию. Но надо явно фиксировать, что именно не проверялось.
Ограничения эксперимента
Выводы нельзя считать универсально доказанными. Эксперимент прошёл в одном репозитории, на одной ветке и на трёх зависимых задачах одного домена. Это хороший сигнал для класса задач «последовательные инженерные срезы с тестами, документацией и явными acceptance criteria», но не доказательство, что процесс так же хорошо подойдёт для:
- независимых задач, которые можно безопасно параллелить;
- маленьких однофайловых правок;
- задач без тестовой базы;
- продуктовых или исследовательских задач, где итог заранее неясен;
- задач, где субагентам нельзя писать в общее рабочее дерево.
Стоимость процесса тоже заметна. Статистические и state-задачи требуют долгих прогонов, ожидания worker-а, reviewer-кругов и ручной классификации находок. Поэтому будущий скилл должен уметь выбирать облегчённый режим, а не всегда запускать полную схему.
Инварианты будущего скилла
Если на базе эксперимента делать скилл, в него стоит перенести не конкретные команды из этого репозитория, а следующие обязательные правила:
- Один worker — один bounded issue. Цепочка задач ведётся координатором, а не одним долгоживущим исполнителем.
- Reviewer по умолчанию read-only. Он выдаёт находки, но не правит файлы.
- Исправления идут только после классификации. Даже хорошие reviewer-находки сначала проходят фильтр координатора.
- Коммит делает координатор. Это защищает границы задачи и состав diff.
- Большое исправление требует повторной проверки. Если правка меняет дизайн или формат данных, результат самой правки надо ревьюить заново.
- Рабочее дерево — общий ресурс. Каждый цикл должен начинаться и заканчиваться явной проверкой статуса.
- Документация и тесты являются частью доказательства. Reviewer должен проверять не только падения, но и силу тестов, честность README/операционных документов и соответствие acceptance criteria.
Эвристики для будущего скилла
Если делать скилл на базе эксперимента, в нём стоит закрепить такие правила:
- Начинать с чистого дерева. Если есть процессные или чужие правки, отделить их до запуска worker-а.
- Давать worker-у один issue. Не просить закрывать цепочку задач в одном агенте.
- Формулировать task prompt как контракт. В нём должны быть цель, границы, методика, запрет на коммит, запрет на откат чужого и формат отчёта.
- Требовать саморевью без правок. Исправления только после решения координатора.
- Классифицировать находки. Минимальный набор: чинить до коммита, риск, ложная тревога, вне скоупа.
- Запускать отдельного reviewer-а на рискованных точках. Особенно: state/serialization, сервисная интеграция, документация, изменения формата данных, статистические модели.
- Повторять reviewer-круг после больших исправлений. Если исправление изменило дизайн, оно само нуждается в ревью.
- Координатор делает собственный sanity-check. Не полный реимплемент, а
проверка связей: дефолты, compose, документация, acceptance criteria,
git status, тесты. - Коммитить только координатору. Worker не должен фиксировать изменения.
- Закрывать агентов. Завершённые subagents надо закрывать, чтобы не держать лишний контекст и лимиты.
Возможный скелет скилла
- Прочитать issue и зависимости.
- Проверить рабочее дерево.
- Запустить worker.
- Дождаться отчёта или запросить статус, если работа слишком долго молчит.
- Запустить self-review того же worker-а.
- Классифицировать находки.
- Вернуть worker-у только выбранные правки.
- Запустить проверки координатора.
- Если задача рискованная, запустить reviewer-а.
- Классифицировать reviewer-находки.
- При больших исправлениях повторить reviewer-круг.
- Сделать коммит.
- Обновить исследовательскую заметку или итоговый лог, если эксперимент ещё идёт.
Этот скелет должен быть параметризуемым. Например, для простой задачи можно ограничиться worker + саморевью + проверки координатора. Для state/serialization, интеграции сервиса, документации или статистической модели нужен reviewer.
Оставшиеся вопросы
- Как заранее понять, когда нужен отдельный reviewer, а когда достаточно саморевью?
- Стоит ли задавать worker-у обязательные промежуточные отчёты для статистики и долгих тестов?
- Нужно ли явно ограничивать бюджет worker-а или число reviewer-кругов?
- Как лучше формализовать критерий «большое исправление требует второго reviewer-а»?
- Должен ли будущий скилл сам создавать research log, или это должен быть отдельный режим?
Предварительная рекомендация
Эксперимент стоит считать успешным. Процесс дал не только рабочий результат, но и поймал дефекты, которые легко могли пройти обычный одиночный поток:
- конфликт дефолтов интенсивности;
- падение на битом state v2;
- слишком большой state v2;
- неполная валидация компактного state;
- слабый интеграционный тест, не доказывавший уход от старой плоской модели.
Для будущего скилла ядро должно быть не «запусти много агентов», а координационный цикл с классификацией находок и контролем границ.
Главная формула процесса:
worker реализует -> worker сам себя ревьюит -> координатор фильтрует ->
worker чинит выбранное -> reviewer проверяет риск -> координатор фильтрует ->
при большом исправлении повторить reviewer -> координатор коммитит
Внешнее ревью другой моделью (отложенный заход, 2026-06-14)
Отложенное ревью результата петли (его наметил handoff
2026-06-11-generator-time-adr-and-pending-review.md) выполнено. Ревью провела
модель другой родословной — та, что вела дизайн, ADR и спеки, а не линия
Кодекс, писавшая код. Это и есть проверка центральной гипотезы эксперимента:
какой класс дефектов автономная петля систематически не видит без внешнего
взгляда.
Основа проверки: перечитаны ADR-0004/0005 и обе спеки; код прочитан помодульно;
марковская модель путей прогнана симуляцией на 300 тыс. визитов; прогнан весь
набор тестов (113 passed); происхождение обёртки try→fresh прослежено по git.
Прогноз по находке A не оправдался — петля её поймала
Handoff предсказывал, что петля пропустит ссылочную целостность при
restore_state («слепые зоны одной линии Кодекс скоррелированы»). На деле:
- висячая ссылка визит→пользователь ловится валидатором формы (
state.py:88); - висячая ссылка пользователь→словарь (неизвестный
seed_click_id) валидатору формы недоступна, но петля в коммите задачи 06 (640050e) добавила обёрткуtry→freshвокругrestore_stateв сервисе и написала регрессионный тестtest_invalid_restored_v2_state_starts_fresh(профиль сmissing-click-id). Это совпадает с разделом «Наблюдения по задаче 06»: и саморевью worker-а, и отдельный reviewer независимо нашли «битый v2 проходитfrom_dict_safe, но роняетrestore_state».
Вывод: многоролевой цикл (worker + саморевью + отдельный reviewer) поймал ровно тот класс дефекта, который ставился как главный индикатор эксперимента. Прогноз о скоррелированной слепоте по этому пункту ошибочен.
Что внешний взгляд всё же добавил
Дефекты, дожившие до внешнего ревью, оказались не точечными, а сквозными — их не пинит ни один тест, и каждый по отдельности «проходит»:
- Расхождение живого и восстановленного путей. Живой визит берёт
браузер/локацию из случайной сид-сессии и
event_id=_new_uuid(generation.py), восстановленный — из сид-сессии профиля иevent_id=uuid5(runtime.py). У визита, пережившего рестарт, атрибуты «доезжающих» событий меняются на середине. Инвариант «один визит — однородный контекст» нарушается на стыке двух путей, который ни один тест не сводит вместе. - Форма распределения длины визита. Среднее (10.7) и конверсия в
/confirmation(27%) на цели, воронка монотонна — это тесты проверяют и подтверждают. Но форма дальше от сида: спайк 16% на длине 2 (форс минимума в 2 события убирает визиты-отказы из одного события, которые сид допускает), ~6.6% визитов срезаются о потолок 30, медиана 8 сидит на нижней границе тестовой полосы 8–12. Тесты пинят средние и пороги, но не форму распределения.
Уточнённая гипотеза
Главный индикатор A был локальным дефектом — падение в одной точке на битом входе; того же семейства, что и прочие находки задачи 06 (раздутый снимок, неполная валидация). Такие вещи дисциплинированный многоролевой цикл ловит хорошо. А выжили до внешнего ревью свойства межмодульные (инвариант поверх двух путей кода) и распределительные (форма, а не среднее) — ровно там, где слабы и ревью внутри одной линии, и покритериальные тесты.
Отсюда уточнение к открытому вопросу «когда нужен отдельный reviewer»: ценнее всего reviewer другой родословной, и именно там, где (а) свойство тянется через несколько модулей/путей, либо (б) корректность распределительная (форма), а не одно утверждение. Для точечных дефектов на входе хватает саморевью и отдельного reviewer той же линии.
Побочно подтвердилось
Разделение двух линий сработало: ADR-0005 (модельные часы ×K) остался решением на бумаге, код петли честно держался реал-тайм-модели, нечаянного переплетения линий нет. Процессное решение «не мешать брейншторм времени с петлёй 06/07» выдержало.
Связанные артефакты
.scratch/handoffs/2026-06-11-subagent-coordinator-experiment.md— исходный одноразовый handoff и рабочий журнал наблюдений..scratch/feature-data-generator/issues/05-intensity-and-flow-calibration.md.scratch/feature-data-generator/issues/06-state-v2-and-restart.md.scratch/feature-data-generator/issues/07-service-integration-and-docs.mddocs/adr/0003-handoffs-in-scratch.mddocs/adr/0002-specs-as-durable-design-docs.md