Перейти к основному содержимому

ADR 0047 — Эксплуатируемость после dogfooding: закрытие Фазы 12

Контекст​

Фаза 10 была первым реальным запуском Brabo, строящего сам Brabo. Она дала семнадцать находок, сохранённых в O que o primeiro dogfooding ensinou. Три из них касались эксплуатируемости — того, что отделяет эксперимент, проводимый вручную, от системы, с которой живут:

#что это былочем закрыто
1продукт умел только СОЗДАВАТЬ репозиторий; направить проект на существующий репозиторий требовало вручную вставлять строки в две таблицыADR 0044
10dev agent обрабатывал ОДНУ task и останавливался; фаза шла партиями, с перезапуском engine между нимиADR 0045
13продвижение draft → ready было автоматическим при создании — PO сам решал, что попадает в очередь devsADR 0046

У всех трёх уже был собственный тест, когда писался этот ADR. Не хватало другого: доказательства, что они умерли вместе. Система может иметь все три исправления и всё же не быть эксплуатируемой, если они работают только по отдельности, — именно это Фаза 10 выявила о функциях, которые проходили тесты своих фаз и не переживали первого контакта с реальным использованием.

Решение​

Валидация — это исполняемый скрипт, а не прозаический сценарий. apps/api/scripts/validacao-fase-12.ts, по образцу demo-noop-execution.ts: поднимает контекст Nest, вызывает реальные use cases и завершается с ненулевым кодом, когда критерий не выполняется. Альтернатива — чек-лист, по которому кто-то идёт, кликая, — дала бы валидацию, которая молча стареет, так же как таблица наблюдений Фазы 10 осталась пустой.

Свидетельства извлекаются из базы, а не переписываются. Скрипт завершается выводом Markdown-таблицы session_events.id (ULID) самого прогона, готовой к вставке в документ. И отказывается завершиться успехом, если какой-то этап, который он заявил как пройденный, не оставил следа в event log, — без этой проверки неверный запрос дал бы короткую таблицу, и валидация всё равно прошла бы, что является классическим режимом сбоя генерируемого отчёта.

Валидация работает локально и без LLM, и это заявлено в первом абзаце документа. Не в сноске. Две конкретные причины, обе проверенные:

  • fork Фазы 10 так и не был назван — dogfooding-mission.md:135 по-прежнему является TODO(humano), так что нет цели для повторного принятия. Путь принятия одинаков у обоих providers; различается сеть, покрытая smoke adopt-repository.smoke.spec.ts, закрытым реальными учётными данными;
  • суждение gates с локальной моделью не детерминировано, и ADR 0020 уже говорил это. Вердикт входит через RecordGateVerdictUseCase — РЕАЛЬНУЮ воронку, где рождается task.gate_resolved. 12b нужно доказать цепочку вердикт → outbox → wake → claim, а не то, умеет ли 7B читать suite.

Валидация, которая притворялась бы, что покрывает больше, чем покрывает, была бы хуже её отсутствия: объявила бы закрытым то, что не было задействовано.

NoopDevAgentServer вошёл в машину состояний 12b — и это была находка самой подготовки валидации. Фаза 12b изменила только реальный DevAgentServer. Noop продолжал фиксировать status: :working, не подписываясь на Engine.Dev.Wake, обрабатывая одну task и останавливаясь: находка #10 оставалась живой внутри единственного средства, способного проверить фазу без траты токенов. Сквозной прогон с ним провалил бы критерий "ноль перезапусков" из-за дефекта инструмента, а не продукта.

Исправлением было не копирование машины в Noop, а её перенос в Engine.Dev.AgentIo — модуль, который уже существовал именно потому, что "Noop, который переписывал бы эти части, проверял бы копию, а не инфраструктуру". Этот аргумент действовал для worktree и идентичности коммита с Фазы 4a; он стал действовать и для перепланирования. Между двумя агентами различается run_task, и только он, поэтому он входит как функция, а не как behaviour.

Итоги Фазы 10 написаны сейчас, с объявленными пробелами. CLAUDE.md:77 ссылался на docs/explanation/primeiro-dogfooding.md с конца той фазы, а файла так и не было. Он написан из того, что восстановимо, — семнадцать находок с файлом и строкой, повествование о партиях, ручной seed, — и всё, что зависело бы от живого подсчёта, вошло как não medido, никогда как оценка. Таково правило самих итогов (colheita-esqueleto.md:22-24): ни одно число не входит без запроса, который его производит.

В таблице контраста "1 перезапуск на сданную task" фигурирует как свойство, выведенное из кода того времени, а не как наблюдаемое среднее, — различие записано в самой ячейке.

Последствия​

Фаза 12 закрывается с тремя находками P1 по эксплуатируемости, решёнными и доказанными в одном прогоне. Остальные четырнадцать остаются перечисленными и открытыми в итогах; ни одна не исправлена мимоходом, по тому же принципу, который установила миссия Фазы 10: исправление находки вне фазы, которая её адресует, скрывает свидетельство того, почему она существовала.

Три вещи, которые выявила эта фаза и которые входят как backlog, а не исправление:

  1. Инструментирование главной метрики предшествует эксперименту. Находка #17 (P1, открыта) говорит, что proposed_action.approved/.denied идут только в outbox, никогда в session_events. Количественной половины итогов Фазы 10 в значительной мере нет из-за этого. Следующий dogfooding без предварительного решения #17 снова потеряет те же числа.
  2. Нет инструмента редактирования истории. Цикл отказа 12c закрывается пересозданием (create_story), а отклонённая история остаётся в draft с записанной причиной. Это проверяемо, но оставляет остаток в backlog. Зафиксировано в ADR 0046.
  3. В покрытии docmap был пробел ровно там, где эта фаза больше всего меняла. Ни одно правило не наблюдало apps/engine/lib/engine/dev/** и apps/engine/lib/engine/agents/**: машина состояний dev agents и разговорные агенты могли меняться без требования какой-либо документации. Этот ADR закрывает пробел заодно, потому что оставить его открытым значило бы, что сам механизм документирования лжёт о фазе, которая его задействовала.

Чего Фаза 12 не изменила, и стоит сказать явно, потому что это ось продукта: конвейер одобрений остался ровно таким, каким был, и merge в защищённую ветку по-прежнему является ручным решением пользователя. Шаг 6 валидации предлагает merge с автономией auto_approve и разрешающим permissions.json и требует pending в результате. Перепланирование агента — это не предоставление автономии, а лишь то, что он не умирает между задачами.