Skip to content

feat(frota): a camada que INTEGRA — fila por risco, ledger entre turnos e travas no PATH - #583

Draft
rubenmarcus wants to merge 7 commits into
mainfrom
claude/frota-24-7
Draft

feat(frota): a camada que INTEGRA — fila por risco, ledger entre turnos e travas no PATH#583
rubenmarcus wants to merge 7 commits into
mainfrom
claude/frota-24-7

Conversation

@rubenmarcus

Copy link
Copy Markdown
Member

A casa produz mais rápido do que integra. Ao abrir isto: 47 PRs abertos, 13 em conflito, 21 vermelhos, contra uma main que anda sozinha. Mais braço em paralelo piora — cada merge na main empurra os conflitantes para longe. Faltava a camada que integra, e o 00-PROMPT-ORQUESTRADOR.md já a descrevia pressupondo um humano acordado colando prompt em 28 lanes.

O achado que reordena a prioridade da casa

A fila reportava 7 PRs prontos. Só 3 estavam. Os mapas formam duas correntes empilhadas — #540→#541→…→#551 e #554→#555→…→#565 — e os PRs de cima têm como base outro PR aberto, não a main: não podem entrar enquanto a raiz não entrar.

As duas raízes estão em CONFLITO e seguram 16 dos 47 PRs: #554 destrava 9, #540 destrava 7. É o trabalho de maior alavanca do repositório, e a fila anterior o escondia atrás de uma fileira de verdes convidativos que não podiam ser mesclados.

O que entra

arquivo papel
fila.mjs classifica e ordena por risco de apodrecer; CONFLITO primeiro porque é a única dívida que fica mais cara enquanto ninguém mexe nela
tarefas.mjs a classe do PR decide a missão — sem lanes.json, que mentiria em 48 h
ledger.mjs memória entre turnos: o próximo agente continua, não recomeça
lock.mjs "um único agente roda browser" vira mecanismo, com validade para não virar deadlock
shim/git, shim/gh a trava de verdade
runner.mjs monta o prompt do estado real, roda o agente na worktree, fecha o ledger
selftest.mjs cada trava + a mutação que a faz ficar vermelha

A trava é o PATH, não o prompt

Turno desassistido não para para pedir permissão, e prompt não segura isso. tools/frota/shim/ entra na frente no PATH do turno; o git/gh de lá recusam com saída 97 exatamente o que o dono tirou da mesa: push na main, --force puro, --no-verify, gh pr merge/close e a label safe-automerge — que é o botão de merge, porque o csbrasil-bot-automerge mescla sozinho quando ela aparece.

Deixam passar de propósito git checkout origin/main -- <arquivo>: trazer um arquivo da main é resolução de conflito legítima, e há caso no selftest cobrando isso.

O lock nasceu sem morder

Guardava process.pid, que na CLI morre no mesmo segundo — o lock nascia com o dono morto e o pedido seguinte o roubava em vez de ser negado. O teste negativo pegou. M1 congela o código original inteiro: desfazer só uma ponta deixava a outra segurando a trava, e o mutante passava em verde fingindo que a primeira carregava peso sozinha.

Réguas

node tools/frota/selftest.mjs20 passaram, 0 falharam; 5 mutantes reprovaram como deviam.
npm run check:deploy — verde (pre-push, 23 s).

Dois achados operacionais de brinde

  1. node_modules inconsistente entre worktrees — várias têm zero pacotes, inclusive o checkout principal. Sem sharp e @gltf-transform/core, 5 réguas do check:deploy reprovam por ambiente e parecem defeito de código. Toda worktree da frota precisa de npm ci antes de virar lane.
  2. O pre-push reporta o portão errado. Ele extrai a falha com grep -m1 -E '✗|FALHA|error', e eval:error-console casa com error — então ele acusou um teste que passou. Não corrigido aqui para não misturar frentes.

Fora de escopo, de propósito

Nada entra na main sem o dono: a frota prepara, ele integra. Nenhum merge, nenhuma label aplicada.

https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9

47 PRs abertos, 13 em conflito, 21 vermelhos e 7 verdes parados contra uma main
que anda sozinha. O gargalo desta casa não é hora de agente, é integração — e
mais braço em paralelo piora, porque cada merge na main empurra os conflitantes
para longe.

A ordem não é por número de PR nem por data: CONFLITO vem primeiro porque é a
única classe que fica MAIS CARA enquanto ninguém mexe nela. Depois VERMELHO, que
bloqueia mas tem custo estável. PARADO nunca é despachado: abandonar PR é decisão
do dono.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
…ente só

O prompt do orquestrador já dizia "se o agente parar por limite, o próximo deve
ler o ledger, sem recomeçar" — e o ledger que a frase pressupunha nunca existiu.
Append-only em JSONL, fora da árvore: heartbeat de agente não é história do
projeto.

O lock existe porque "um único agente roda browser" precisa de mecanismo quando a
frota roda 24/7 em várias worktrees. Ele nasceu SEM MORDER: guardava
`process.pid`, que na CLI morre no mesmo segundo, então o lock nascia com o dono
morto e o pedido seguinte o roubava em vez de ser negado. O teste negativo pegou.
Agora o relógio manda, e a morte do PID só libera depois da graça.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
Turno desassistido não para para pedir permissão, e prompt não segura isso: um
agente que "sabe" que não deve tocar na main toca na main no turno em que a
instrução ficar longe o suficiente no contexto.

`shim/git` e `shim/gh` entram na frente no PATH do turno e RECUSAM a chamada com
saída 97: push na main, `--force` puro, `--no-verify`, `gh pr merge`, `gh pr
close` e a label `safe-automerge` — que É o botão de merge, porque o
`csbrasil-bot-automerge` mescla sozinho quando ela aparece.

Deixam passar de propósito `git checkout origin/main -- <arquivo>`: trazer um
arquivo da main é resolução de conflito legítima, e só a troca de branch é recusada.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
…trava

Um `lanes.json` escrito à mão mente em 48 h — o PR sai do conflito, a worktree é
podada, a main anda, e o registro segue mandando rebasear o que já foi rebaseado.
Registro desatualizado é pior que registro nenhum porque o agente confia nele.
Aqui a classe do PR escolhe o texto da missão a cada turno.

O selftest roda cada trava duas vezes: no código real (tem que passar) e num
mutante que a desfaz (tem que reprovar). M1 congela o código original INTEIRO —
as duas pontas do conserto do lock, porque desfazer só uma deixava a outra
segurando a trava e o mutante passava em verde fingindo que a primeira carregava
peso sozinha. Foi o próprio M1 que mostrou isso ao passar quando não devia.

16 passaram, 0 falharam; 4 mutantes reprovaram como deviam.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
Monta o prompt do turno a partir do estado real (fila + ledger + git), roda o
agente headless na worktree certa e fecha o turno no ledger — dê certo ou não.
O modelo é parâmetro: Opus 5 pela autenticação normal, GLM-5.3 apontando
ANTHROPIC_BASE_URL para a z.ai, que o ZCode publica em formato Anthropic.

Roda com --dangerously-skip-permissions porque sem isso o turno trava no
primeiro pedido de permissão às 3 h da manhã e a frota para calada. Isso só é
defensável porque o freio não é o prompt: o PATH do turno começa em
`tools/frota/shim/`, e o git/gh de lá recusam com saída 97 exatamente o que o
dono tirou da mesa — push na main, merge e a label safe-automerge.

Teto de 45 min por turno e de 12 turnos seguidos sem mudar de classe: laço que
não sai do lugar vira BLOQUEADO para o dono olhar, em vez de queimar a noite.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
…stá pronto

Achado em campo, não em teoria. A fila reportava 7 PRs "prontos para mesclar".
Só 3 estavam: #555, #556, #562, #563 e #564 têm como base outro PR aberto, não a
main, e não podem entrar enquanto a raiz não entrar. Os mapas formam duas
correntes — #540#541→…→#551 e #554#555→…→#565 — e as duas raízes estão em
CONFLITO segurando 16 dos 47 PRs abertos.

Isso reordena a prioridade da casa: #554 destrava 9 PRs e #540 destrava 7. É o
trabalho de maior alavanca do repositório, e a fila anterior o escondia atrás de
uma fileira de verdes convidativos que não podiam ser mesclados.

`depoisDaBase()` é estável e tolera base já fechada e ciclo de dependência — os
dois têm caso, porque uma fila que trava em laço infinito de madrugada é pior
que uma fila que ordena errado.

selftest: 20 passaram, 0 falharam; M5 prova que sem `depoisDaBase` o dependente
volta a subir na frente da raiz.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
csbrasil Ready Ready Preview Sep 11, 2026 11:09am UTC

Request Review

@csbrasil-BOT csbrasil-BOT added the target:main PR apontando para a branch main label Sep 10, 2026
@csbrasil-BOT

csbrasil-BOT commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

csbrasil-bot classification

Note

@rubenmarcus abriu 10 arquivo(s) · +1352 −0, base main.

O que mudou

área arquivos + amostra
🧪 arnês 10 +1352 −0 GUARDRAILS.md, README.md, fila.mjs

Labels

label por quê
target:main vai direto pra main, sem fila de integração

…vore

Achado em campo hoje. As réguas da casa provam que mordem injetando mutação no
arquivo e restaurando depois; processo morto no meio deixa a mutação. O turno
seguinte roda o portão, vê vermelho e vai consertar defeito que não existe.

`eval:docsautoria` injeta "linha intrusa que o gerador não escreveu" em
docs/docs/colaborar.md. Um check:deploy interrompido deixou a linha, e
docs:check + eval:docsautoria passaram a reprovar numa árvore que minutos antes
dera 35/35.

Isso morde a frota com força porque o runner mata o turno em 45 min com SIGTERM —
exatamente o cenário. A trava manda conferir `git status` contra o ledger antes de
acreditar em qualquer vermelho.

Agent: Claude Code (Opus 5)
Claude-Session: https://claude.ai/code/session_01CKo8ixRptzgH3LeGhT6XN9
Signed-off-by: rubenmarcus <rubenmarcus.dev@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

target:main PR apontando para a branch main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants