ФАЗА 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»; frontmatterdocs/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 |
| 4 | broker в локальном compose согласно вердикту ADR 0146 | 2 |
| 5 | возможности, объявленные в протоколе локального агента (пока без зеркала) | 1 |
| 6 | возможность espelho: собственная область, одно направление, никогда не стирает | 5 |
| 7 | установка как пользовательский сервис, остановка/удаление, отзыв на стороне сервера | 6 |
| 8 | состояние зеркала в web (последняя синхронизация, счётчик, ошибка) | 6 |
| 9 | E2E, docs:check, docmap и CHANGELOG закрыты | 8 |
Что фаза поставила и где она отклонилась от плана
Таблица выше — это план. Ниже — запись того, что произошло, — различия здесь, потому что план, переписываемый под результат, перестаёт чему-либо учить.
| # | RN/ADR | как вышло |
|---|---|---|
| 1 | ADR 0146, ADR 0147 | как запланировано; диапазон RN-511..520 зарезервирован, с прыжком через RN-510 (уже выделенную в неслитой ветке) |
| 2 | ADR 0146, пункты 1–2 | как запланировано — база по умолчанию $HOME/projetos-brabo, записываемая скриптом Node, потому что пункт меню bootstrap.sh выполняется со stdin в /dev/null и по построению не может спрашивать |
| 3 | RN-513 | обе половины уже были правилом (RN-500/RN-501) и никогда не доходили до экрана; НОВЫМ был только предвыбор |
| 4 | RN-512, ADR 0146, пункт 3 | broker выходит из profiles в ЛОКАЛЬНОМ compose и только в нём — два compose намеренно расходятся |
| 5 | RN-514, ADR 0147, пункт 1 | как запланировано; отказ из-за недостающей возможности родился реализованным и протестированным без срабатывания — только RN-516 дала ему первое реальное срабатывание |
| 6 | RN-515/RN-516, ADR 0147, пункты 2/3/4/8 | разделена на две половины (назначение × копия), решение принято во время сессии |
| 7 | RN-518, RN-519/520, ADR 0147, пункты 5–6 | поставлена после сессии 8 — обе зависели только от 6, и ничто не упорядочивало их между собой |
| 8 | RN-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.