Pular para o conteúdo principal

FASE 28 — a pasta do usuário

Este documento é o recorte da fase: o que ela persegue, o que ela recusa, e em que ordem. As decisões moram nos ADR 0146 e ADR 0147 — o que estiver aqui e não estiver lá é intenção, não decisão.

O problema​

O produto tem três modos de execução desde o ADR 0104, e nenhum deles entrega a coisa mais simples que um usuário espera: abrir o editor e ver o trabalho acontecendo numa pasta dele.

modopasta do usuárioo que custa
containernão existeo código mora num volume que ele nunca abre
mountedsim, sob BRABO_PROJECTS_BASEinvisível na prática: sem a variável o modo nem é oferecido, e nada no produto pede que ela seja configurada
runnersim, pasta realbinário, chmod +x manual, pareamento, e Docker como pré-requisito duro (RN-507/508)

O mounted é o caso mais instrutivo. O ADR 0141 construiu a base única e o ADR 0142 tornou possível sugerir um caminho que ainda não existe — mas nenhuma das duas coisas chegou à tela. GET /workspaces/:workspaceId/projects-base existe, é consumido por ninguém no web, e por isso o modo que já estava pronto continua fora do alcance de quem instala o produto.

O desenho, em três camadas​

Cada camada com um mecanismo só, e nenhuma inventando autoridade nova:

camadaondemecanismo
consentimentopnpm bootstrap, uma vez, no hostescolhe a base, grava BRABO_PROJECTS_BASE, prova o compartilhamento do Docker Desktop
pasta vivadentro da basebind por identidade (ADR 0141) — zero sincronização
destino fora da baseopcional, por projetocapacidade espelho do agente local: uma direção, nunca apaga

A terceira camada existe por uma razão física: bind mount não atravessa rede nem alcança caminho fora do que foi montado. Para escrever numa pasta arbitrária do host só há três saídas — rodar api/engine nativamente, ter um processo vivo no host, ou o navegador escrever pela File System Access API. A segunda é a única contínua, e o brabo-runner já é esse processo.

Consentimento de configuração ≠ aprovação de ação​

O ponto que os dois ADRs precisam declarar, e que decide o desenho do espelho:

A escrita do espelho não passa por proposed_action, porque não é um agente pedindo para agir — é o sistema escrevendo num caminho que o próprio usuário declarou ao criar o projeto. Sincronizar por comando de terminal seria o contrário: cairia no escopo de caminho do ADR 0055, viraria fila de aprovações rotineiras, e corroeria justamente o teto que dá sentido ao clique.

Numeração — contada, não herdada​

As contagens em prosa do repositório estão em deriva conhecida. CONTRIBUTING.md diz "68 ADRs"; o frontmatter de docs/adr/index.md diz "108". Nenhuma sessão desta fase confia em número escrito em prosa.

O método, que toda sessão desta fase repete antes de reservar qualquer número:

# 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

Medido no início da sessão 1, contra dev: 144 ADRs (maior 0145, com um buraco em 0062) e 371 RNs (maior 509, zero âncoras duplicadas). Daí 0146 e 0147.

A faixa reservada para a fase é RN-511 a RN-520, e o salto sobre a RN-510 é deliberado. Contar apenas dev devolveria 510 — mas a RN-510 já estava alocada numa branch de trabalho que ainda não tinha mergeado. Reservá-la aqui recriaria exatamente a colisão que aconteceu com a RN-505, quando duas PRs alocaram o mesmo número com dois minutos de diferença e a âncora {#rn-505} ficou duplicada por semanas.

Vale registrar por que aquela colisão sobreviveu tanto tempo: docs:build não pega âncora duplicada. onBrokenAnchors: 'throw' reprova link para âncora que não existe; com duas âncoras iguais o Docusaurus resolve a primeira e a segunda fica inalcançável, em silêncio. Contar só o que está em dev é condição necessária e não suficiente — é preciso contar também o que está em voo.

As sessões​

Uma entregável cada, na ordem. O prompt completo de cada sessão é escrito ao final da anterior, com o que ela tiver descoberto.

#entregáveldepende de
1este documento + ADR 0146 + ADR 0147 + faixa de RN reservada—
2base consentida no bootstrap.sh/preflight.mjs, .env.example, prova de compartilhamento1
3wizard oferece mounted por padrão quando a base existe2
4broker no compose local conforme o veredito do ADR 01462
5capacidades declaradas no protocolo do agente local (sem espelho ainda)1
6capacidade espelho: escopo próprio, uma direção, nunca apaga5
7instalação como serviço de usuário, parar/remover, revogação server-side6
8estado do espelho na web (última sync, contagem, erro)6
9E2E, docs:check, docmap e CHANGELOG fechados8

O que a fase entregou, e onde ela desviou do plano​

A tabela acima é o plano. O que segue é o registro do que aconteceu — as diferenças estão aqui porque um plano que se reescreve para bater com o resultado deixa de ensinar qualquer coisa.

#RN/ADRcomo saiu
1ADR 0146, ADR 0147como planejado; faixa RN-511..520 reservada, com o salto sobre a RN-510 (já alocada em branch não mergeada)
2ADR 0146 pontos 1–2como planejado — base padrão $HOME/projetos-brabo, gravada por script Node porque item de menu do bootstrap.sh roda com stdin em /dev/null e por construção não consegue perguntar
3RN-513as duas metades já eram regra (RN-500/RN-501) e nunca tinham chegado à tela; NOVO foi só a pré-seleção
4RN-512, ADR 0146 ponto 3o broker sai do profiles no compose LOCAL e só nele — os dois composes divergem de propósito
5RN-514, ADR 0147 ponto 1como planejado; a recusa por capacidade faltante nasceu implementada e testada sem disparar — só a RN-516 lhe deu o primeiro disparo real
6RN-515/RN-516, ADR 0147 pontos 2/3/4/8partida em duas metades (destino × cópia), decisão tomada durante a sessão
7RN-518, RN-519/520, ADR 0147 pontos 5–6entregue depois da sessão 8 — as duas dependiam só da 6, e nada as ordenava entre si
8RN-517, ADR 0147 ponto 7como planejado
9—fechamento: o E2E que faltava, a regra de docmap para apps/runner/** e este registro

Três coisas saíram diferentes do que o plano supunha, e as três estão declaradas onde importa:

  • A sessão 7 rodou depois da 8. As duas dependiam da 6 e de mais nada, e nenhuma dependia da outra — a ordem da tabela era leitura, não precedência.
  • BRB-031 não morreu. O ADR 0147 esperava que a instalação como serviço matasse o chmod +x manual do ADR 0118. Não mata: quem chega a rodar brabo-runner service install já precisou tornar o binário executável para chegar ao comando. O efeito colateral dependia de a instalação vir do bootstrap versionado do ADR 0146, que esta fase não ligou.
  • A revogação alcança {projeto, usuário}, nunca {chave}. O ponto 6 do ADR não prometia precisão por credencial, mas era a leitura natural dele. Ela não existe: runner_socket_tickets guarda project_id/user_id/kind, e a identidade da credencial morre no PatAuthGuard. O custo está escrito na RN-520 e em security-surface.md em vez de disfarçado.

O que esta fase NÃO toca​

Declarado nos dois ADRs, e repetido aqui porque é o que dá sentido ao resto:

  • decide.ts e os tetos absolutos — nenhum teto ganha exceção, nem configurável
  • proposed_action como origem de todo efeito externo de agente
  • a imutabilidade do event log
  • o portão da imagem nos três modos (ADR 0135)
  • as cinco operações e a spec computada do broker (ADR 0128/0130)
  • o enum project_execution_mode — nenhum modo novo nasce nesta fase

Lacunas que a fase encosta e não resolve​

  • credencial de git descartada no caminho docker exec em modo runner — apps/runner/src/index.ts:318-320; packages/docker-port não tem campo env, por decisão do ADR 0130. Declarada nos ADRs 0130/0145, não corrigida aqui.
  • symlink de dentro do projeto apontando para fora não é detectado por decide(), que é puro e não faz I/O — ADR 0055. apps/runner/src/guard.ts:9-31 declara-se best-effort pelo mesmo motivo.
  • BRB-005 — os artefatos publicados (imagens do GHCR, binários do runner) não são assinados. Um instalador de uma linha sem assinatura é pior que o download atual, e é por isso que a sessão 7 não ganha instalador de uma linha.
  • BRB-031 — chmod +x manual na configuração pelo navegador. Some como efeito colateral se a instalação passar a vir do bootstrap versionado.
  • exclusividade {project_id, machine_id} adiada até haver um segundo dev simultâneo real — ADR 0137.