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

ФАЗА 28 — папка пользователя

Этот документ — срез фазы: к чему она стремится, что отвергает и в каком порядке. Решения живут в ADR 0146 и ADR 0147 — то, что есть здесь и чего нет там, — это намерение, а не решение.

Проблема​

У продукта три режима исполнения начиная с ADR 0104, и ни один из них не даёт самого простого, чего ожидает пользователь: открыть редактор и видеть, как работа происходит в его папке.

режимпапка пользователячего стоит
containerне существуеткод живёт в томе, который он никогда не открывает
mountedда, под BRABO_PROJECTS_BASEневидим на практике: без переменной режим даже не предлагается, и ничто в продукте не просит её настроить
runnerда, реальная папкабинарник, ручной chmod +x, сопряжение и Docker как жёсткая предпосылка (RN-507/508)

mounted — самый поучительный случай. ADR 0141 построил единую базу, а ADR 0142 сделал возможным предлагать путь, которого ещё не существует, — но ни одна из двух вещей не дошла до экрана. GET /workspaces/:workspaceId/projects-base существует, никем в web не потребляется, и поэтому уже готовый режим остаётся вне досягаемости того, кто устанавливает продукт.

Дизайн, в три слоя​

У каждого слоя один механизм, и ни один не изобретает новых полномочий:

слойгдемеханизм
согласиеpnpm bootstrap, один раз, на хостевыбирает базу, записывает BRABO_PROJECTS_BASE, доказывает общий доступ Docker Desktop
живая папкавнутри базыbind по тождеству (ADR 0141) — ноль синхронизации
назначение вне базынеобязательно, по проектувозможность espelho локального агента: одно направление, никогда не стирает

Третий слой существует по физической причине: bind mount не пересекает сеть и не достигает пути вне смонтированного. Чтобы писать в произвольную папку хоста, есть только три выхода — запускать api/engine нативно, иметь живой процесс на хосте или чтобы браузер писал через File System Access API. Второй — единственный непрерывный, и brabo-runner уже является этим процессом.

Согласие на конфигурацию ≠ одобрение действия​

Пункт, который оба ADR должны объявить и который определяет дизайн зеркала:

Запись зеркала не проходит через proposed_action, потому что это не агент, просящий действовать, — это система, пишущая по пути, который сам пользователь объявил при создании проекта. Синхронизация командой терминала была бы противоположностью: попала бы в область пути ADR 0055, стала бы очередью рутинных одобрений и разъела бы как раз тот потолок, который придаёт смысл клику.

Нумерация — подсчитанная, а не унаследованная​

Подсчёты в прозе репозитория в известном дрейфе. CONTRIBUTING.md говорит «68 ADRs»; frontmatter docs/adr/index.md говорит «108». Ни одна сессия этой фазы не доверяет числу, написанному в прозе.

Метод, который каждая сессия этой фазы повторяет перед резервированием любого номера:

# ADRs — arquivos, não prosa
ls docs/adr/ | grep -cE '^[0-9]{4}-.*\.md$'

# RNs — cabeçalhos, nos três arquivos que compartilham a numeração
git grep -h -E '^### RN-' -- 'docs/business-rules.md' 'docs/business-rules/*.md' \
| grep -oE 'RN-[0-9]+' | sort -u | wc -l

Измерено в начале сессии 1, против dev: 144 ADR (наибольший 0145, с дырой на 0062) и 371 RN (наибольшая 509, ноль дублированных якорей). Отсюда 0146 и 0147.

Диапазон, зарезервированный за фазой, — RN-511 по RN-520, и прыжок через RN-510 намеренный. Подсчёт только dev дал бы 510 — но RN-510 уже была выделена в рабочей ветке, ещё не слитой. Её резервирование здесь воссоздало бы ровно то столкновение, что произошло с RN-505, когда два PR выделили один и тот же номер с разницей в две минуты, и якорь {#rn-505} оставался дублированным неделями.

Стоит зафиксировать, почему то столкновение жило так долго: docs:build не ловит дублированный якорь. onBrokenAnchors: 'throw' проваливает ссылку на якорь, которого не существует; при двух одинаковых якорях Docusaurus разрешает первый, а второй становится недостижимым, молча. Подсчёт только того, что в dev, — условие необходимое, но не достаточное: нужно считать также то, что в полёте.

Сессии​

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

#результатзависит от
1этот документ + ADR 0146 + ADR 0147 + зарезервированный диапазон RN—
2база с согласием в bootstrap.sh/preflight.mjs, .env.example, доказательство общего доступа1
3мастер предлагает mounted по умолчанию, когда база существует2
4broker в локальном compose согласно вердикту ADR 01462
5возможности, объявленные в протоколе локального агента (пока без зеркала)1
6возможность espelho: собственная область, одно направление, никогда не стирает5
7установка как пользовательский сервис, остановка/удаление, отзыв на стороне сервера6
8состояние зеркала в web (последняя синхронизация, счётчик, ошибка)6
9E2E, docs:check, docmap и CHANGELOG закрыты8

Что фаза поставила и где она отклонилась от плана​

Таблица выше — это план. Ниже — запись того, что произошло, — различия здесь, потому что план, переписываемый под результат, перестаёт чему-либо учить.

#RN/ADRкак вышло
1ADR 0146, ADR 0147как запланировано; диапазон RN-511..520 зарезервирован, с прыжком через RN-510 (уже выделенную в неслитой ветке)
2ADR 0146, пункты 1–2как запланировано — база по умолчанию $HOME/projetos-brabo, записываемая скриптом Node, потому что пункт меню bootstrap.sh выполняется со stdin в /dev/null и по построению не может спрашивать
3RN-513обе половины уже были правилом (RN-500/RN-501) и никогда не доходили до экрана; НОВЫМ был только предвыбор
4RN-512, ADR 0146, пункт 3broker выходит из profiles в ЛОКАЛЬНОМ compose и только в нём — два compose намеренно расходятся
5RN-514, ADR 0147, пункт 1как запланировано; отказ из-за недостающей возможности родился реализованным и протестированным без срабатывания — только RN-516 дала ему первое реальное срабатывание
6RN-515/RN-516, ADR 0147, пункты 2/3/4/8разделена на две половины (назначение × копия), решение принято во время сессии
7RN-518, RN-519/520, ADR 0147, пункты 5–6поставлена после сессии 8 — обе зависели только от 6, и ничто не упорядочивало их между собой
8RN-517, ADR 0147, пункт 7как запланировано
9—закрытие: недостающий E2E, правило docmap для apps/runner/** и эта запись

Три вещи вышли не так, как предполагал план, и все три объявлены там, где это важно:

  • Сессия 7 прошла после 8. Обе зависели от 6 и больше ни от чего, и ни одна не зависела от другой — порядок таблицы был порядком чтения, а не предшествованием.
  • BRB-031 не умер. ADR 0147 ожидал, что установка как сервис убьёт ручной chmod +x из ADR 0118. Не убивает: тот, кто доходит до запуска brabo-runner service install, уже должен был сделать бинарник исполняемым, чтобы добраться до команды. Побочный эффект зависел от того, чтобы установка шла из версионируемого bootstrap ADR 0146, который эта фаза не подключила.
  • Отзыв достигает {проект, пользователь}, никогда {ключ}. Пункт 6 ADR не обещал точности по учётным данным, но это было его естественное прочтение. Её нет: runner_socket_tickets хранит project_id/user_id/kind, а идентичность учётных данных умирает в PatAuthGuard. Цена записана в RN-520 и в security-surface.md, а не замаскирована.

Чего эта фаза НЕ касается​

Объявлено в обоих ADR и повторено здесь, потому что именно это придаёт смысл остальному:

  • decide.ts и абсолютных потолков — ни один потолок не получает исключения, даже настраиваемого
  • proposed_action как источника каждого внешнего эффекта агента
  • неизменяемости event log
  • затвора образа в трёх режимах (ADR 0135)
  • пяти операций и вычисляемой spec broker (ADR 0128/0130)
  • enum project_execution_mode — в этой фазе не рождается ни одного нового режима

Пробелы, которых фаза касается и не решает​

  • учётные данные git, отбрасываемые на пути docker exec в режиме runner — apps/runner/src/index.ts:318-320; у packages/docker-port нет поля env, по решению ADR 0130. Объявлено в ADR 0130/0145, здесь не исправлено.
  • symlink изнутри проекта, указывающий наружу, не обнаруживается decide(), который чист и не делает I/O, — ADR 0055. apps/runner/src/guard.ts:9-31 объявляет себя best-effort по той же причине.
  • BRB-005 — опубликованные артефакты (образы GHCR, бинарники runner) не подписаны. Установщик одной строкой без подписи хуже текущей загрузки, и поэтому сессия 7 не получает установщика одной строкой.
  • BRB-031 — ручной chmod +x при настройке через браузер. Исчезает как побочный эффект, если установка начнёт идти из версионируемого bootstrap.
  • исключительность {project_id, machine_id} отложена до появления второго реального одновременного разработчика — ADR 0137.