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

ADR 0056 — Engine работает в удалённом репозитории

Статус​

Принят — реализован и доказан тестом в Фазе B backlog (RN-076).

Открытие при реализации, зафиксированное потому, что оно меняет размер проблемы: двум из четырёх потребителей учётные данные никогда не были нужны. Engine.Gates.Diff и Engine.Harness.ProjectContext используют лишь ИМЯ ветки по умолчанию — они останавливались на удалённом provider как побочный ущерб функции, которая возвращала больше, чем они просили. Разделение default_branch/1 и remoto_de_trabalho/1 разблокировало обоих без какого-либо токена.

Контекст​

Engine.Projects.ProjectRepository.get_local_repo_path/1 возвращает {:error, {:unsupported_provider, "github"}} для всего, что не local. От него зависят четыре потребителя, и все останавливаются вместе:

потребительчто перестаёт работать
Engine.Dev.WorktreeManagerу dev agent нет worktree — он не пишет код
Engine.Actions.TerminalExecutorбез workspace любая команда падает
Engine.Gates.Diffу QA и SecOps нет diff, который они судят
Engine.Harness.ProjectContextагент собирает контекст без репозитория

Корень — асимметрия: api говорит с GitHub по HTTP — создала репозиторий, закоммитила файлы bootstrap, создала ветки, — тогда как engine работает в файловой системе и знает только локальный bare repo. Проект на GitHub проходит разговорную половину и bootstrap и останавливается на половине построения.

Именно это сегодня не даёт FASE 13b существовать как написано: она требует измеренного выполнения в проекте, ПРИНЯТОМ из fork через удалённый GithubProvider, а это выполнение никак не может дойти до первой команды.

Что уже готово и не требует изменений​

Engine.Actions.Workspace.init_from_bare!/3 делает git init + remote add origin <origem> + fetch + checkout. Это уже универсально: то, что <origem> является локальным путём, — случайность provider local, а не замысла. Замена источника на URL решает весь путь без его переписывания.

Не хватает, следовательно, не git-сантехники, а учётных данных.

Решение​

1. Engine запрашивает у api рабочий remote и никогда не хранит учётные данные​

Токен живёт зашифрованным в user_credentials, с envelope encryption, а мастер-ключ принадлежит api. Engine не получает мастер-ключ и не сохраняет токен: он запрашивает у api по уже существующему каналу /internal/* с service token рабочий remote проекта в момент, когда ему нужно получить или отправить.

Передача мастер-ключа engine расширила бы радиус поражения самого чувствительного секрета продукта ради одного HTTP-вызова меньше.

2. Токен НИКОГДА не попадает в .git/config​

Это самое важное решение, и оно — прямое следствие ADR 0055.

Очевидный путь — remote add origin https://x-access-token:TOKEN@github.com/… — записывает учётные данные открытым текстом внутри папки проекта. А RN-075 только что дала dev agent автоодобряемое чтение внутри этой папки: cat .git/config вернул бы токен без какого-либо одобрения, и он уехал бы к LLM-provider на следующем ходе, внутри истории цикла.

Область путей защищает от чтения агентом за пределами проекта. Она не защищает — и не может защитить — от секрета, который сам продукт поместил внутрь.

Поэтому: origin хранит чистый URL, а аутентификация внедряется на каждый вызов, живя только во время использующего её процесса git. Никаких учётных данных в файле, ни в .git/config, ни в сохранённом helper.

3. Учётные данные — владельца (OWNER) workspace​

То же правило, что RN-058 уже установила для ключа LLM, по той же причине: платит и авторизует владелец workspace, а не агент и не тот, кто открыл сессию.

4. provider: local остаётся прежним​

Это не обратная совместимость из вежливости: локальный provider — то, что используют контрактные тесты, и то, благодаря чему pnpm dev работает вообще без учётных данных. Рабочий remote локального проекта — это путь bare repo, а остальной путь разницы не знает.

5. Сбой учётных данных — это infra, и он называется​

Отсутствующий, просроченный токен или токен без прав на репозиторий отклоняется с источником infra — не modelo, не código. Это правило CLAUDE.md об источнике сбоя, а находка T этого раунда показывает, что оно нарушается именно на путях ошибок, которые никто не задействует.

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

Что разблокируется. Проект на удалённом provider получает worktree, терминал, diff для gate и контекст — то есть половину построения. Это предусловие FASE 13b: без этого не существует измеренного выполнения в удалённом репозитории.

Что становится дороже. Engine начинает зависеть от api для работы в файловой системе. fetch теперь может упасть из-за сети, просроченного токена или недоступности api — три режима сбоя, которых у локального bare repo не было. Отсюда пункт 5 — решение, а не деталь.

Чего этот ADR НЕ решает. Изоляция остаётся открытой проблемой ADR 0055: токена больше нет на диске, но агент по-прежнему работает в том же контейнере, что и монорепозиторий Brabo. env процесса git в окне, пока он работает, — меньшая поверхность, чем постоянный файл, но не нулевая.

Рассмотренные альтернативы​

Дать мастер-ключ engine. Убирает HTTP-вызов и удваивает число мест, откуда может утечь любой секрет продукта. Отвергнуто.

Токен в URL origin. Так делает почти каждый туториал, и это ровно то, что ADR 0055 сделал опасным: запись секрета туда, где у агента есть автоодобряемое чтение. Отвергнуто — и зафиксировано здесь, потому что соблазн "упростить" до этого вернётся.

Зеркалировать remote в локальный bare и синхронизировать. Оставило бы engine прежним, но создало бы второй источник истины репозитория с тихим расхождением, когда кто-то отправляет напрямую в provider. Отвергнуто.

Оставить только local и отложить. Это текущее состояние, и именно оно мешает FASE 13b.

Ссылки​

  • Находка N из achados-execucao-real.md, Фаза B backlog.
  • ADR 0055 — область, которая делает решение 2 обязательным.
  • RN-058 — чьи учётные данные тратит агент.
  • apps/engine/lib/engine/projects/project_repository.ex, apps/engine/lib/engine/actions/workspace.ex, apps/engine/lib/engine/actions/git_executor.ex.