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

ADR 0135 — Шлюз образа от Arquiteto (RN-105) начинает действовать во всех трёх режимах выполнения

Контекст​

Со времён ФАЗЫ 25 (ADR 0065/RN-105) вкладка Code проекта отвечает 409, пока никто не решил, какой образ контейнера он выполняет, — artifact.project_image, испускаемый Arquiteto (choose_project_image) и, со времён ADR 0133 (RN-491), также Infra, выбирающей среди кандидатов собственной маршрутизации Arquiteto. Порядок задан пользователем: контейнер — это то, что придаёт смысл чтению кода там, и открыть чтение до того, как появится, где выполнять, научило бы тому, что шлюз декоративен.

ADR 0072 (RN-169), пересмотренный ADR 0104 (RN-421), освободил от этого шлюза проекты mounted/runner: поскольку эти два режима не поднимают СОБСТВЕННЫЙ контейнер — код живёт в папке пользователя, смонтированной в уже существующие контейнеры (mounted) или подтверждённой CLI, работающим вне их (runner), — исходное правило вечно отвечало 409 в проекте, где решения Arquiteto никогда бы не случилось. Освобождение позволяло не закрывать вкладку побочным эффектом.

Исходный план этой поставки («Выполнение в настоящем контейнере», Часть 1) объявил это освобождение решением #5 к пересмотру, уже принятым до появления этого PR: «Portão RN-105 passa a valer nos TRÊS modos, container e aba Code. Custo aceito: exp001/exp002 perdem a aba Code até o Arquiteto decidir.» Этот ADR исполняет это решение и фиксирует, чего оно стоит и чего оно НЕ меняет.

Исходное освобождение смешивало два вопроса, которые ADR 0130 (broker контейнеров) и ADR 0133 (выбор Infra) разделили, хотя api за этим не последовала: «поднимает ли этот проект контейнер на СЕРВЕРЕ?» (нет, в mounted/runner — вопрос о МЕХАНИЗМЕ) и «имеет ли смысл требовать, чтобы кто-то решил образ проекта, прежде чем открыть чтение кода?» (да, во всех трёх режимах — вопрос о ПОЛИТИКЕ). Освобождение отвечало на второй вопрос ответом на первый.

Решение​

ВСЕ ТРИ режима выполнения (container/mounted/runner) начинают требовать решённый artifact.project_image, прежде чем откроется вкладка Code. ReadProjectCodeUseCase.portaoDoContainer перестаёт проверять executionMode — шлюз становится безусловным, так же как он уже был безусловен внутри режима container. ProjectCodeTab (web) перестаёт обращаться с mounted/runner как с особым случаем: три режима следуют одному и тому же useQuery загрузка/ошибка/sem_decisao/решено, что УПРОЩАЕТ компонент — ветка modoLocal и условный enabled по режиму исчезают.

Принятая цена, со всей ясностью: каждый СУЩЕСТВУЮЩИЙ проект mounted/runner без решённого artifact.project_image — включая реальные проекты dogfooding этого репозитория, exp001 и exp002, — ТЕРЯЕТ доступ к вкладке Code в момент деплоя этого PR, пока Arquiteto (или Infra) не решит для него образ. Это ДЕЙСТВИЕ ОПЕРАТОРА, требуемое ПОСЛЕ деплоя, а не прозрачное исправление, которого пользователи даже не заметят, — поэтому ветка рождается breaking/, а не bugfix/, хотя по содержанию это исправление пробела: соглашение репозитория (CLAUDE.md) явно говорит, что содержание не решает префикс, когда изменение требует действия оператора. Версия поднимается MAJOR как механическое следствие scripts/ci/version.ts, а не по ручному решению — номер потом не исправляют.

Жизненный цикл контейнера (project_containers) тоже перестаёт отказывать по режиму, но по более узкой причине. RegistrarTransicaoDeContainerUseCase отвечал 400 для любого проекта вне режима container — проверка, которая на самом деле принадлежала broker (который уже отказывает по СОБСТВЕННОЙ и более конкретной причине), а не этому сценарию использования. 400 уходит; таблица начинает регистрировать строку для всех трёх режимов, через ТУ ЖЕ воронку provisioning, которая уже читала решение об образе (теперь также требуемое для mounted/runner).

Что остаётся НЕВОЗМОЖНЫМ, и по намеренному решению, — это чтобы mounted/runner по-НАСТОЯЩЕМУ дошли до running. Это применяется в одном месте — ContainerBrokerPort.start() отказывает с ModoDeExecucaoNaoSuportadoError (apps/broker/src/operacoes.ts): broker работает на СЕРВЕРЕ и не видит папку пользователя, где живёт код mounted/runner, поэтому собрать контейнер там значило бы собрать его из пустой папки. Эта политика НЕ меняется в этом PR — комментарий класса ошибки уже указывал на этот ADR («o PR do portão nos três modos (1.7) revisita as duas ao mesmo tempo»), и пересмотр подтверждает, что она верна как есть.

Исследовано и решено, без изменения кода: ExecuteContainerStartUseCase вызывает ContainerBrokerPort.start() ДО любого RegistrarTransicaoDeContainerUseCase.execute(..., 'provisioning') — если broker отказывает (обычный случай для mounted/runner), сбой становится container.start_failed с названной причиной (BrokerRecusouError, НИКОГДА не crash и не тихий fallback), и переход жизненного цикла так и не вызывается. То есть по реальному пути одобрения (container_start) ни одна строка project_containers не рождается для mounted/runner, даже при удалённом 400 по режиму — broker уже является действующим шлюзом, более точным, чем старый 400 (он знает, МОЖЕТ ЛИ собрать контейнер; сценарий использования знал лишь РЕЖИМ). Поэтому этот PR НЕ добавляет вторую проверку режима ни в propose-action.use-case.ts, ни в ExecuteContainerStartUseCase: дублирование создало бы второе место для расхождения с первым — тот же урок, который уже фиксировал сам комментарий broker.

Также исследовано: ни propose_container_start (инструмент Infra Lead, apps/engine/lib/engine/infra/tools/propose_container_start.ex), ни GetInfraContextUseCase сегодня не ограничивают по executionMode — Infra Lead МОЖЕТ предложить container_start для проекта mounted/runner, и предложение проваливается, только когда кто-то одобряет, а broker отказывает. Это ПРЕДСУЩЕСТВУЮЩИЙ пробел (не внесённый этим PR), который тратит цикл человеческого одобрения на действие, обречённое на провал, — но проваливается оно ясно, с названием, никогда молча. Исправление потребовало бы трогать prompt/инструкцию Infra Lead в engine, вне охвата этого среза (API/домен); он объявлен ниже, а не исправлен мимоходом.

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

exp001/exp002 и любой другой проект mounted/runner без решённого образа теряют вкладку Code, пока кто-нибудь не запустит для них Arquiteto (или выбор Infra). Это принятая цена решения #5 исходного плана, а не побочный эффект, обнаруженный потом.

Реальное сдерживание против подъёма контейнера mounted/runner на сервере не изменилось. Им по-прежнему является broker (ModoDeExecucaoNaoSuportadoError), а не 400 от api, — на самом деле оно СТАЛО точнее, потому что теперь это решает одно место, а не два.

project_containers теоретически может зарегистрировать строку provisioning для mounted/runner, — но ни один путь production сегодня не вызывает RegistrarTransicaoDeContainerUseCase.execute(..., 'provisioning') для этих режимов, не пройдя сначала через broker, который отказывает. Если новый путь когда-нибудь обойдёт ExecuteContainerStartUseCase (вызывая сценарий использования напрямую), он унаследует ту же защиту: obterImagem.execute по-прежнему требует решения, и ничего больше здесь не меняется.

Infra Lead может предложить container_start для проекта mounted/ runner, зная, что оно провалится, — предсуществующий пробел, не внесённый и не закрытый этим PR, объявленный в CLAUDE.md («Estado atual e aberto»), чтобы не потерялся.

Рассмотренная и отвергнутая альтернатива: добавить проверку режима в propose-action.use-case.ts или в ExecuteContainerStartUseCase, чтобы отказывать в container_start для mounted/runner ДО вызова broker. Это избежало бы упомянутого выше напрасного цикла одобрения — но перенесло бы знание о том, КТО может собрать контейнер (сегодня это знает только broker, через PROJECT_WORKSPACES_HOST_ROOT), внутрь api, у которой нет этого контекста и которая повторила бы правило, уже существующее в другом месте. Отвергнуто пока: цена — избегаемый цикл одобрения, а не некорректность, — и сообщение о сбое уже называет причину. Зафиксировано как кандидат в UX, а не как исправление ошибки, если объём бесполезных предложений это оправдает.