███████╗██╗ ██╗ ██╗██╗ ██╗
██╔════╝██║ ██║ ██║╚██╗██╔╝ ██╗
█████╗ ██║ ██║ ██║ ╚███╔╝ ╚═╝
██╔══╝ ██║ ██║ ██║ ██╔██╗ ██╗
██║ ███████╗╚██████╔╝██╔╝ ██╗ ╚═╝
╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝
da ideia ao merge, sem trocar de ferramenta
Família de comandos globais e context-agnósticos que cobre o ciclo inteiro de trabalho num repo: da telemetria de produção ao código, do código ao review, do review ao merge, do merge à comunicação.
grippado.github.io/flux: a landing com o ciclo, a instalação por harness e as versões publicadas. O selo de release acima aponta sempre para a última, sem ninguém precisar lembrar de atualizar este arquivo. A landing, porém, só muda quando a release é aprovada: o merge de uma versão nova dispara o workflow, mas ele espera um clique (ver Publicar uma versão).
O Flux é um corpo de workflows com adaptadores por harness. O mesmo plugins/flux/ (as skills, os agents, os shared) serve Claude Code, Cursor e Codex: não há fork de comportamento nem arquivo duplicado.
/plugin marketplace add grippado/flux
/plugin install flux@flux
git clone https://github.com/grippado/flux ~/code/flux
~/code/flux/scripts/install-cursor.sh
# encerrar o Cursor por completo e abrir de novoOs verbos ficam como /flux-peek, /flux-review, /flux-iterate. Confira em Settings → Customize → Plugins.
O script existe por dois motivos que não dá para resolver no README:
O Cursor não segue symlink em ~/.cursor/plugins/local/. Um symlink registra o plugin e não carrega nada, sem erro em lugar nenhum. Tem que ser diretório real, então o script copia.
O Cursor não prefixa skill de plugin. O nome invocável sai do campo name do frontmatter, e sem namespace: name: peek viraria /peek, no mesmo espaço onde o próprio Cursor já tem uma skill nativa chamada review. Prefixar na fonte não serve, porque o Claude Code usa o mesmo campo e viraria /flux:flux-peek. Então o prefixo é aplicado na cópia instalada, pelo script. O repo mantém um nome só por verbo.
Não há hot-reload: a cada git pull, rode o script de novo e encerre o Cursor por completo antes de reabrir. Fechar a janela normalmente deixa o app rodando, e ele volta com a versão antiga em memória — o sintoma é confuso, porque o elo funciona, só que com o comportamento da versão anterior.
Em plano Teams/Enterprise dá para importar o repo em Dashboard → Plugins e distribuir pelo marketplace do time, com auto-refresh.
O Flux já pode ser instalado do marketplace do próprio repositório:
codex plugin marketplace add grippado/flux
codex plugin add flux@fluxAbra uma sessão nova depois da instalação. O repositório fornece o catálogo em
.agents/plugins/marketplace.json, que aponta para o pacote nativo
plugins/flux/.codex-plugin/plugin.json. O catálogo público universal ainda depende da submissão e
aprovação da OpenAI; até lá, este marketplace Git é o caminho instalável e compartilhável.
A forma de invocar uma skill é a que o Codex registrar; use @Flux ou o nome exibido pela sessão,
nunca presuma /flux:.
O adaptador Codex usa a delegação nativa de subagentes para o fan-out. Como o Codex não registra
os nomes customizados de subagent_type de Claude/Cursor, ele resolve arquivos de instrução
legíveis e entrega o path ao subagente nativo; o banner e a cobertura mostram a fonte que de fato
rodou. MCP, vault, Linear, Slack e specialists são capacidades opcionais: quando ausentes, o
preflight declara a degradação e o Flux continua no perfil genérico.
A CLI resolve o preflight fora de qualquer sessão de IA e já roda o Claude Code com o prompt
armado, eliminando a etapa manual de copiar e colar. Em vez de abrir o Claude e digitar
/flux:review meu-repo, você roda flux review meu-repo no terminal — por padrão na aba
atual (equivalente ao antigo --here, agora o comportamento default).
flux review meu-repo # roda na aba atual, prompt já armado
flux review 31 --repo flux --dry # só monta e mostra o comando, não executa
flux build meu-repo --new # abre aba nova (iTerm2/Terminal.app) em vez de rodar aquiSem nenhum argumento, num terminal de verdade, a CLI entra em modo interativo: um menu
navegável por seta (↑↓ ou j/k, número ou Enter, Esc cancela) pergunta o comando — com uma
descrição curta de cada um —, depois o alvo (PR/URL/ticket/path, opcional), o repo (opcional) e
se quer rodar numa máquina remota.
$ flux
flux — modo interativo (sem argumentos). Qual comando?
(setas ou j/k pra navegar, número ou Enter pra confirmar, Esc cancela)
review revisão formal de PR/doc (specialists + reviewer)
❯ build implementa um ticket, entrega PR draft
peek relance rápido e read-only de PR/diff/doc
...
Prévia do banner antes de disparar. Com terminal interativo (e sem --dry), antes de abrir o
Claude Code a CLI mostra o banner completo que vai ser enviado e um menu de seta com três opções:
enviar como está (Enter no primeiro item), anexar um comentário extra ao banner, ou cancelar.
--yes/-y pula essa prévia — útil pra quem já confia no fluxo e não quer o passo extra toda vez.
Rodar numa outra máquina, via SSH. --remote <alias> reencaminha o comando inteiro pra um
alias já configurado no seu ~/.ssh/config — herda o terminal atual do outro lado, como se você
tivesse aberto uma sessão SSH e rodado o flux de lá. Sem valor (--remote sozinho), a CLI lista
os Host alcançáveis agora e pergunta qual usar (mesmo menu de seta); um comentário
# flux:ignore na linha acima de um Host tira aquele alias da lista — útil pra servidor de
produção que só está no ~/.ssh/config por conveniência, não pra rodar sessões do flux.
flux review 31 --repo flux --remote worzix # dispara direto no alias "worzix"
flux review 31 --repo flux --remote # pergunta qual máquina alcançável usarLimitação declarada: a abertura automática de aba (--new) suporta só o Claude Code, e só
funciona no iTerm2 e no Terminal.app (macOS); em qualquer outro emulador, ou fora do macOS, a CLI
imprime o comando no stdout e avisa no stderr, para você colar e rodar na mão.
Permissões: por default a sessão abre com claude --dangerously-skip-permissions — a CLI existe
para despachar trabalho, e uma aba que para no primeiro prompt de permissão não despacha nada. Quem
preferir a sessão com os gates de permissão do harness passa --safe. Para usar um wrapper próprio no
lugar do claude (um alias de conta, um launcher com sync), exporte FLUX_CLAUDE_CMD com o comando
completo: ele é usado verbatim e as flags passam a ser responsabilidade dele.
cd cli
bun run setup # builda, re-assina (macOS) e instala em ~/.local/bin/fluxbun run setup é o atalho de dev: builda o binário, re-assina no macOS (necessário em máquinas com
MDM/EndpointSecurity — sem isso o primeiro exec depois de cada build pode morrer com "killed" e um
load code signature error no log do sistema, sem indicar nada de errado no binário em si), e
copia pra ~/.local/bin/flux. Preferindo fazer na mão, ou instalar em outro lugar:
cd cli
bun run build # gera o binário ./flux
codesign --force --sign - flux # macOS com MDM/EndpointSecurity; opcional nos demais casos
mv flux ~/.local/bin/flux # ou outro diretório no seu PATHPermissão de automação no primeiro uso do --new (macOS): ao rodar o primeiro flux <verbo> --new, o macOS pode exibir um diálogo pedindo permissão para o terminal controlar o iTerm2 ou o
Terminal.app via AppleScript. Aceite o diálogo; a permissão fica salva em Preferências do Sistema →
Privacidade e Segurança → Automação. Sem --new (o padrão), essa permissão nunca é pedida.
FLUX_HOME: se você não instalou o flux como plugin de nenhum harness, defina FLUX_HOME
apontando para o diretório plugins/flux do seu checkout. A CLI usa essa variável como fonte
explícita do FLUX_ROOT antes de tentar a heurística.
export FLUX_HOME=~/code/flux/plugins/flux| Flag | Efeito |
|---|---|
--repo <slug> |
repo alvo, quando não dá pra inferir do alvo ou do cwd |
--dry |
só imprime o comando/prompt montado, sem executar nada nem mostrar a prévia do banner |
--safe |
roda com os gates de permissão do harness, em vez de --dangerously-skip-permissions |
--new |
abre aba nova (iTerm2/Terminal.app) em vez de rodar na aba atual (o padrão) |
--remote [alias] |
roda na máquina do alias (~/.ssh/config); sem valor, pergunta interativamente |
--yes, -y |
pula a prévia do banner antes de disparar |
--record |
só em flux review <PR>, no modo here: grava um run local em ~/.flux/runs/<run_id>/ (run.md, 01-review.md, outcome.md), privado e opt-in. Recusa --new e --remote. Com --record, um preflight que aborta (exit 3) impede o disparo, o que sem a flag não acontece. Ver run.md e a PRIVACY.md |
flux resolve . --json # inspeciona o contexto resolvido no cwd
flux review 31 --repo flux --dry # monta o prompt sem executar nada
flux review meu-repo # roda na aba atual, /flux:review meu-repo pronto
flux build meu-repo --new # mesma coisa, mas abrindo aba nova
flux review 31 --repo flux --remote # escolhe interativamente uma máquina acessível via SSH
flux # modo interativo: pergunta comando, alvo, repo e remotoDepois de instalar, os verbos ficam disponíveis em qualquer repo Git. No Claude Code, a forma
é /flux:peek; no Cursor, /flux-peek; no Codex, use o nome que o Plugin Directory registrar.
Uma ressalva honesta sobre o Codex: o flux:land e o flux:chain despacham um irmão, e para isso
precisam resolver o prefixo de invocação da família — coisa que o Codex ainda não expõe de forma
verificável. O land aborta a fase de despacho; o chain valida a gramática e imprime o plano, mas
não executa os elos. Nenhum dos dois degrada para uma iteração fora do contrato. map e refine
também perdem uma capacidade lá, e o iterate perde o watch: faz a passada, declara a degradação e
o usuário reinvoca o verbo para a próxima rodada. Detalhe em
shared/codex-compat.md.
Requisitos reais: git (duro — sem ele o preflight aborta) e gh autenticado (mole, mas é o que separa "roda em PR" de "roda só na working tree"). O jq é mole e só interessa ao watch do flux:iterate: sem ele o watch troca o gate mecânico pelo modo agendado, com a perda declarada. Nada além disso. Sem manifesto, sem vault e sem specialists, a família roda no perfil genérico e o banner do preflight declara o nível degradado em vez de fingir que está completo.
Dois elos dependem de MCP e degradam sem ele: o flux:reply precisa de um canal de Slack, e o modo doc do flux:review/flux:peek precisa de um canal de documentos. Qual servidor atende cada canal vem do campo mcp do manifesto; sem o campo, o elo procura a capacidade na sessão. Nenhum id de MCP é hardcoded na família — ele depende de como cada máquina instalou o servidor.
Para somar specialists, persistência no vault e integrações do seu time, declare um manifesto de contexto. Exemplos prontos em examples/.
flux: é um ciclo, não um conjunto de utilitários. Cada comando é um elo com fronteira nítida, e o elo seguinte assume onde o anterior parou. Você nunca sai da família para completar uma entrega.
Dois princípios sustentam isso:
- Os comandos são globais e não sabem nada do seu time. Eles vivem na raiz da instalação (
${FLUX_ROOT}/skills/) e funcionam em qualquer repo Git, em qualquer harness. O que é específico de um time (quais reviewers, qual vault, quais repos) vem de um manifesto de contexto —flux-context.json—, nunca hardcoded no comando. - Cada elo delega o trabalho especializado. Review vai para agents reviewers; execução vai para o motor nativo do repo; prospecção de codebase vai para specialists. Os comandos orquestram, não reimplementam.
┌ fora do ciclo, uma vez por máquina / por repo ──────────────────┐
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ flux:map │─────▶│ flux:equip │ │
│ │ levanta a instalação│ │ L0 motor · L2 suite │ │
│ │ e diz o que falta │ │ expõe a L3 │ │
│ └───────────────────────┘ └───────────────────────┘ │
└──────────────────────────┬──────────────────────────────────────┘
┆ sugeridos antes, exigidos por nada
▼
ideia / thread / bug relatado / link de telemetria
│
┌───────────┼───────────┬───────────┐
│(opcional) │(opcional) │ (direto) │
▼ ▼ │ │
┌──────────────┐ ┌───────────────────────┐ │
│ flux:probe │ │ flux:refine │ │ probe: o que a telemetria PROVA
│ dossiê de │▶│ mede o escopo antes │ │ refine: fast SDD numa rodada
│ produção │ │ │ │ escopo grande → recusa e corta
└──────┬───────┘ └───────────┬───────────┘ │
└─────────────┬───────┴──────────────┘
▼
┌───────────────────────┐
│ flux:issue │ fonte livre → issue embasada em código real
└───────────┬───────────┘
▼
┌───────────────────────┐
│ flux:build │ issue → código + PR draft
└───────────┬───────────┘ (despacha ao motor nativo do repo)
▼
┌───────────────────────┐ ┌───────────────────────┐
│ flux:peek │ │ flux:review │
│ relance read-only │◀──ou──▶│ review formal │
│ não posta, não grava│ │ persiste no vault │
└───────────┬───────────┘ └───────────┬───────────┘
└──────────┬─────────────────────┘
▼
┌───────────────────────┐
│ flux:iterate │ threads → correções → push → CI verde
└───────────┬───────────┘ ↻ fica vivo até a PR assentar
▼
┌───────────────────────┐
│ flux:land │ N PRs → toposort → merge-ready → go/no-go
└───────────┬───────────┘ (não mergeia: humano decide)
▼
┌───────────────────────┐
│ flux:reply │ comunica, embasado no que de fato mudou
└───────────────────────┘
Nenhum elo é obrigatório e nenhum chama o próximo sozinho. Cada um termina apontando o elo seguinte e devolvendo o volante para você.
O flux:refine é o ramo opcional da entrada, e por isso
aparece pontilhado: o ciclo funciona inteiro sem ele. Ele existe para o pedido que chegou como ideia
crua, sem ninguém ter escrito por que aquilo importa, onde encosta no código e por onde começar. Numa
rodada ele produz PRD, TRD e plano de slices no mesmo board que o flux:issue consome depois,
então a prospecção acontece uma vez só. E ele mede o escopo antes de trabalhar: pedido grande
demais para uma rodada é recusado com o corte proposto, em vez de virar um refinamento raso com
aparência de completo. O contrato do gate é o scope-gate.md.
Quando o gatilho "decisão de produto em aberto sem dono" do
scope-gate.md é o único sinal duro do 🔴, sem outro sinal duro
concorrente e com no máximo 1 sinal mole, o Caminho grill entra no lugar da recusa seca: busca
evidência real das alternativas que o pedido sugere (precedente no código, ferramenta equivalente já
existente, doc relacionado) e abre um gate para você decidir; sem alternativa sugerida, ele nomeia o
gap e segue para o Caminho vermelho normal, sem gate. A decisão fica registrada na Timeline do board
(ou só no chat, em perfil sem VAULT_ROOT), e o escopo é medido de novo a partir dali. Roda
automaticamente sempre que o gatilho bate, exceto sob --dry, que para no veredito e só nomeia o
gap; --grill só documenta a intenção no comando.
O flux:probe é o outro ramo opcional da entrada, e o único
que começa antes de existir um pedido. Bug de produção não chega refinável: chega como um link, um
contador e uma mensagem de erro que descreve o último passo da falha, quase nunca o primeiro. O probe
agrega os eventos, mede, testa a explicação corrente contra a física dos números e cruza o resultado
com o código que emite aquele sinal.
Ele lê duas famílias de fonte, e elas cobrem lados diferentes da mesma falha. Um rastreador de
erros entrega o alvo já agrupado, e costuma ver o cliente; uma plataforma de observabilidade não
agrupa nada (o alvo é uma pergunta, e o agrupamento se constrói na hora), e vê o servidor. Entrando as
duas na mesma execução, o elo responde a pergunta que nenhuma delas responde sozinha: o servidor viu
o que o cliente relatou? Ausência do lado do servidor fecha meia investigação de uma vez. Ele escreve no mesmo board do refine e do issue, então o
que ele apurou não é reapurado depois. Quando o dossiê mostra que o pedido é grande, o handoff é para
o refine; quando ele já nomeia as correções, é direto para o issue.
Acima do ciclo, e fora dele, moram dois verbos que não tratam de uma entrega:
flux:map— o verbo de sanidade. Levanta a instalação inteira nesta máquina (raízes de agents, manifestos, repos, as três lentes de cada um, colisões de nome), grava o índice que os demais elos consomem, e relata o que está torto com a remediação de cada caso. Executando de novo, mostra o delta: agents novos, repos novos, suites que quebraram. E não para no diagnóstico: item a item, ele despacha oflux:equippara consertar o que você aceitar, vários repos em paralelo, reconciliando o índice no fim. É o candidato natural a primeiro comando numa máquina nova.flux:equip— o verbo de preparo. Equipa um repo com o motor de execução e a suite de specialists que os elos consomem, e expõe a L3 do repo quando ela existe e não está alcançável. Entra quando falta alguma dessas camadas, e sai.
A divisão entre os dois é limpa: map levanta, propõe e despacha; equip é quem escreve o reparo. Nenhum conserto acontece fora do verbo que é dono do gate daquela escrita, e recusar tudo no gate deixa o map sendo o que ele era, um levantamento — a propriedade que faz dele um comando que se roda sem medo.
Nenhum dos dois é pré-requisito de nada. Sem eles, todo elo cai na varredura direta e declara a degradação no banner — o comportamento que a família sempre teve. Eles melhoram o resultado; não o habilitam. Uma família que exige comando de preparo para funcionar deixou de funcionar na máquina de quem acabou de instalá-la.
flux:iterate fecha uma PR por execução. É eficiente rodar até três iterações independentes em
paralelo; quando a entrega passa desse tamanho, flux:land coordena o lote de múltiplas PRs,
ordena dependências e emite o go/no-go. flux:reply é standalone: pode ser chamado em qualquer
ponto para transformar um caso em comunicação embasada, não apenas depois do land.
| Comando | Entrada | Saída | Escreve? |
|---|---|---|---|
flux:probe |
alvo(s) de telemetria (Sentry, Datadog) | dossiê quantificado: distribuições, percentis, plausibilidade, cliente contra servidor e o cruzamento com o código | vault; nunca cria issue, nunca muda estado na fonte |
flux:refine |
ideia, thread do Slack, bug, ticket | PRD + TRD + plano de slices no board, embasados em código real | vault; nunca cria issue |
flux:issue |
thread do Slack, texto livre, PR | issue de alta qualidade, embasada via specialists | rascunho no vault; cria no Linear só após aprovação |
flux:build |
ticket Linear ou descrição + repo | código + PR draft | sim, via motor do repo |
flux:peek |
working tree, branch, range, PR, doc, path | parecer com badges no chat | não (exceto --save) |
flux:review |
PR ou doc | review formal (holístico + specialists reconciliados) | vault; posta quando você manda |
flux:iterate |
PR | correções aplicadas, réplicas postadas, threads resolvidas, CI vigiado | sim (--dry rascunha read-only) |
flux:land |
issue/feature multi-PR | ordem de merge, validação de regressão, go/no-go | mantém PRs merge-ready; nunca mergeia |
flux:chain |
cadeia de elos (review>iterate) + alvo do primeiro elo |
validação da gramática antes de rodar, baton entre os elos, um banner por elo; cada elo mantém os próprios gates | não escreve por conta; escreve o que cada elo escreve. Hoje executa só review>iterate |
flux:reply |
permalink de thread | rascunho Slack-safe + ata no vault | salva rascunho; nunca posta sozinho |
flux:equip |
repo | motor de execução (L0) + suite de specialists local (L2) + L3 exposta | sim, fora do repo alvo, pelo contrato de destino; manifesto só sob gate |
flux:map |
nada (a máquina) | inventário das lentes, delta desde a última execução, relatório de integridade | o índice flux-agents.json; os consertos despacha ao equip, nunca escreve por conta |
flux:equip e flux:map são os dois verbos fora do ciclo: nenhum trata de uma entrega. Os
demais elos consomem coisas que não produzem — o motor que o flux:build despacha e os specialists
que review/iterate/land reconciliam —, e é o equip que as cria quando faltam. Por isso
review, iterate, land e build não geram mais suite por conta própria: quando percebem a
falta, no fim do trabalho, oferecem o equip.
O flux:map é o que enxerga o conjunto. Todo elo verifica, no preflight, o que ele precisa para
aquele trabalho; ninguém olha a instalação inteira. Sem isso, uma suite quebrada, um manifesto
renomeado ou uma colisão de name: só aparece no meio de uma entrega, um elo por vez, sempre como
degradação e nunca como diagnóstico.
reviewvsiterate—reviewproduz o parecer.iterateconsome pareceres (inclusive de bots e humanos), verifica cada alegação contra o código real, aplica o que procede e defende o que não procede.buildvs/workflowdo repo —buildé o dispatcher: resolve repo e motor. O/workflowdo repo é o motor: conhece os próprios testes, gates e padrão de PR.buildnunca reimplementa motor.iteratevsland—iteratefecha uma PR.landorquestra N PRs de uma entrega e delega o merge-ready de cada uma aoiterate.probevsrefine—proberesponde o que está acontecendo em produção, medindo eventos.refineresponde por que isto importa e por onde começar, medindo escopo. Um bug de produção passa naturalmente pelos dois, nessa ordem, e os dois escrevem no mesmo board.refinevsissue—refineresponde por que isto importa, onde encosta e por onde começar, e pode recusar o pedido por tamanho.issueescreve o corpo da issue e a cria no tracker. Rodando os dois, o board é um só e a prospecção não se repete; rodando só oissue, nada se perde além do PRD e do TRD.
flux/
├── README.md ← este arquivo (doc da família)
├── LICENSE MIT
├── examples/ manifestos prontos: solo / time / pessoal
├── scripts/install-cursor.sh instalação no Cursor (copia + prefixa os nomes)
├── .claude-plugin/marketplace.json o marketplace do Claude Code (o /plugin add lê este)
├── .cursor-plugin/marketplace.json o mesmo, para o Cursor
├── .agents/plugins/marketplace.json marketplace Git instalável pelo Codex
└── plugins/flux/ ← ${FLUX_ROOT} quando instalado
├── .claude-plugin/plugin.json manifesto Claude Code
├── .cursor-plugin/plugin.json manifesto Cursor (mesmo corpo, outro harness)
├── .codex-plugin/plugin.json manifesto Codex + metadata de interface
├── shared/codex-compat.md adaptador de delegação nativa e capacidades opcionais
├── scripts/ scripts bash/python que o plugin executa em runtime
├── agents/ os agentes que a família despacha
│ ├── pr-reviewer.md o holístico genérico (default universal)
│ ├── issue-creator.md redige e cria issues aprovadas no tracker (sonnet, fan-out)
│ ├── sentry-prospector.md agrega os eventos de uma issue de telemetria (sonnet, fan-out)
│ └── datadog-prospector.md interroga logs/APM do backend por pergunta (sonnet, fan-out)
├── skills/ ← os verbos (globais, context-agnósticos)
│ ├── probe/ opcional, antes do ciclo: investiga telemetria de produção
│ ├── refine/ opcional, antes do ciclo: fast SDD numa rodada
│ ├── issue/ build/ peek/
│ ├── review/ iterate/ land/ reply/ chain/
│ ├── equip/ fora do ciclo: motor (L0), specialists (L2), expõe L3
│ └── map/ fora do ciclo: levanta a instalação e grava o índice
└── shared/ contratos compartilhados (fonte única, não duplicar nos verbos)
├── preflight.md verificação de pré-requisitos, níveis de capacidade, banner
├── api-first.md transporte API primeiro, MCP como degrau declarado, por canal
├── hitl.md quando o elo para e pergunta, e como pergunta sem o tool preferido
├── flux-context.md resolução de contexto via manifesto
├── agents-index.md mapa das lentes na máquina (o que existe e onde, nunca o que rodou)
├── review-agents.md descoberta + reconciliação de specialists
├── review-legend.md badges canônicos dos findings
├── review-artifact-template.md formato do artefato de review no vault
├── review-body-template.md formato do corpo da review postada no GitHub
├── pr-attribution.md carimbo flux:<verbo>@<versão> na linha de atribuição da PR
├── issue-template.md formato da issue do flux:issue
├── board-template.md formato do board vivo (execução / iterate / delivery / conversa)
├── worktree-discipline.md todo fluxo que escreve opera em worktree dedicado
├── write-destination.md onde artefato gerado pode nascer: cascata + guardas de symlink/git/dotfiles
├── chain.md gramática de chains: verbo, artefato, legalidade, baton, falha no meio
├── scope-gate.md medir o tamanho do pedido antes de gastar tempo com ele: sinais, faixas, corte proposto
├── run.md o registro local de uma execução (flux-run/1): layout, schema, níveis de garantia, privacidade
├── fanout-discipline.md todo trabalho pesado vai para subagente, em paralelo
├── context-budget.md leitura sob demanda, um root por sessão, delegação
└── quality-gate-api.md diagnóstico de gates Sonar via API (consultar em vez de deduzir)
O harness resolve skills/<verbo>/SKILL.md como /flux:<verbo>. Adicionar um diretório em skills/ publica um verbo novo, sem tocar em instalação.
O nome invocável é montado pelo harness, não escrito por nós. O mesmo skills/iterate/SKILL.md vira /flux:iterate num harness e pode virar outra forma em outro. Por isso os elos que despacham um irmão (o flux:land, que roda o iterate por PR dentro de subagente, e o flux:chain, que roda os elos em sequência) escrevem ${FLUX_CMD}iterate, com o prefixo resolvido e verificado pelo Passo 1b do preflight. O rigor é o mesmo do agente holístico: um nome de comando resolvido sem confirmação vira um subagente que não acha o comando e improvisa a iteração fora do contrato.
${FLUX_ROOT} é resolvido pelo preflight na ordem: ${CLAUDE_PLUGIN_ROOT} → ${CURSOR_PLUGIN_ROOT} → ${CODEX_PLUGIN_ROOT}, quando a sessão o define → o primeiro diretório acima da skill com .codex-plugin/plugin.json, que é como o Codex resolve na prática → dois níveis acima do verbo em execução, resolvendo symlink antes de subir (checkout direto, e a instalação local do Cursor) → ${FLUX_HOME} do ambiente. O contrato específico do Codex está em shared/codex-compat.md.
Esses nomes de variável são a única dependência de harness nos contratos compartilhados. Tudo abaixo do Passo 1 do preflight é escrito contra ${FLUX_ROOT} e ${FLUX_CMD}.
Um flux-context.json num .claude/ (ou .cursor/) de workspace ou repo. O comando procura o mais próximo subindo a árvore a partir do cwd, consultando .claude/ antes de .cursor/ em cada nível — mas proximidade sempre vence diretório. Achou → perfil declarado. Não achou → perfil genérico.
{
"name": "acme",
"holistic_reviewer": "acme-pr-reviewer",
"doc_reviewer": "acme-doc-reviewer",
"answerer": "acme-pr-answerer",
"slack_prospector": "acme-slack-prospector",
"slack_answerer": "acme-slack-answerer",
"specialists_root": "~/agents/acme/{repo}/repo-owner.md",
"vault_root": "~/notes",
"vault_context": "acme",
"workspace_root": "~/code/acme",
"linear_org": "acme",
"repos": ["backoffice", "rf-monorepo", "communication-api", "..."],
"exec_command": "workflow",
"exec_fallback": "acme:implement",
"no_emdash": true
}Contrato completo dos campos: shared/flux-context.md.
A família funciona sem configuração nenhuma. Sem manifesto, cada comando cai num default universal:
| Aspecto | Default sem manifesto |
|---|---|
| Reviewer holístico | genérico da família, resolvido pela cascata do preflight (detecta a stack dinamicamente) |
| Specialists | override local do repo: <repo>/.claude/agents/reviewer.md; sem isso, só holístico |
| Persistência | não persiste; imprime no chat (--save <dir> no flux:review) |
| Motor de execução | /workflow do repo; sem ele, o exec_fallback do perfil; sem ele, modo autônomo (worktree + AGENTS.md/CLAUDE.md do repo + checks + PR draft) |
| Travessão | permitido (no_emdash: false) |
Quem instala a família já tem review holístico e execução funcionando em qualquer repo GitHub. Declarar um flux-context.json é o que soma specialists, persistência no vault e integrações do time.
- Falhar bem em vez de rodar mal — todo elo abre pelo
preflight.md: verifica osrequiresdeclarados no frontmatter, resolve e confere a existência do agente holístico, e classifica o nível de capacidade (FULL/REDUCED/THIN/UNAVAILABLE). Faltou requisitohard→ aborta sem efeito colateral. Faltousoft→ roda e declara a perda no banner de perfil, que abre todo output. Um elo nunca improvisa um reviewer inline nem produz artefato fora do contrato de saída. - Badges canônicos — todo finding usa o vocabulário de
review-legend.md:request-change,breaking-change,question,suggestion,praise,note. Cada um ancorado emarquivo:linha(código) ou§seção + trecho verbatim(doc). - Verificar antes de aceitar — nenhuma alegação de review (de bot ou de humano) é aplicada sem ser conferida contra o código real. Defender uma decisão correta é resultado válido.
- Worktree sempre — todo fluxo que escreve código opera em git worktree dedicado à branch, nunca na árvore principal. Ver
worktree-discipline.md. - Escopo medido antes do trabalho — elo que pode gastar minutos num pedido grande demais mede o tamanho dele antes, por sinais lidos do que já está em contexto e sem chamar agente para medir. Três faixas, e o gate propõe o corte em vez de só sinalizar. Ver
scope-gate.md. - Destino de escrita verificado — artefato gerado fora do repo alvo (uma suite de specialists, um motor, um kit — tipicamente escritos pelo
flux:equip) só nasce num destino que passou pela cascata e pelas três guardas dewrite-destination.md: symlink, repositório git e diretório gerido por dotfiles. Sem destino declarado o elo pergunta, não assume; nada existente é sobrescrito em silêncio; e o que foi criado fica registrado, para haver rollback. - Lente que existe é lente que roda — uma suite de specialists em disco e não invocável é dívida acionável, não estado normal. Quando a causa é a âncora (sessão aberta acima do repo ou em árvore irmã, que é o modo de quem trabalha num workspace com vários repos), o elo percorre a escada de alcance antes de degradar: acrescentar o diretório à sessão onde a capacidade existir e não houver colisão de
name:, senão espelho namespaceado via${FLUX_CMD}equip <slug> --expose-l3. O mapa do que existe na máquina vem doagents-index.md— que diz o que oferecer, nunca o que rodou: disponibilidade continua vindo só da lista de agentes da sessão. - Fan-out sempre — o contexto principal de um elo orquestra; investigar código, tocar repo, aplicar correção ou rodar outro
flux:*vai para subagente, e unidades independentes vão em paralelo num único bloco. Na main ficam só parse, metadados baratos, HITL, board e watch. Regra pétrea, par simétrico do worktree: verfanout-discipline.md. - Humano no volante nas fronteiras externas — nada é postado no GitHub, no Linear ou no Slack, nem mergeado, sem aprovação explícita.
- pt-BR com acentuação correta no output; EN no código.
no_emdash— quandotrue, nada que possa acabar publicado (título/corpo de PR, comentário, mensagem de Slack) usa travessão ou en-dash.
A maioria dos elos quer workspace mode (cd <workspace_root> && claude), porque precisa navegar cross-repo. O flux:build funciona nos dois: em workspace mode você passa o repo como primeiro argumento; em repo mode ele infere do cwd.
Um verbo novo entra assim:
- Crie
plugins/flux/skills/<verbo>/SKILL.mdcom frontmattername/description/user-invocable: true. - Abra com um Step 0-context que resolve o perfil via
flux-context.md— nada de path ou agente de time hardcoded. - Declare Out of scope explicitamente. A fronteira de cada elo é o que mantém o ciclo legível.
- Aponte os shared que se aplicam em vez de reescrever a lógica deles.
- Termine com handoff: qual elo vem depois, e por que.
- Registre o verbo na tabela Os comandos e, se ele mudar o ciclo, no diagrama.
Quando um verbo precisa de lógica determinística que não cabe em Markdown (polling, parsing de JSON), ela vira script. A convenção:
- Onde.
plugins/flux/scripts/guarda o que o plugin executa em runtime e viaja com a instalação (sóplugins/fluxé copiado, por exemplo emscripts/install-cursor.sh). Oscripts/da raiz continua sendo manutenção do repo (checks, release, instalação) e não chega ao usuário. - Bash é o default.
#!/usr/bin/env bash,set -euo pipefail, e dependência ausente sai com código2e mensagem em stderr, como emscripts/check-manifests.sh. Rodeshellcheckantes de abrir PR. - Python só quando a lógica justifica (estrutura de dados, parsing que o
jqnão cobre, algo que o bash tornaria frágil). Usepython3, apenas biblioteca padrão, versão mínima 3.8, e declarepython3como requisito. - Invocação explícita. Sempre
bash "${FLUX_ROOT}/scripts/<nome>.sh"oupython3 "${FLUX_ROOT}/scripts/<nome>.py", nunca o caminho direto. Assim o script não depende do bit de execução, que nem toda instalação preserva. Dentro do script, resolva caminhos relativos ao próprio arquivo, não ao cwd. - Requisitos. O script não se autodeclara: o verbo que o chama declara o que ele precisa, no
requires:doSKILL.md(bin: jq,bin: python3, emhardse o verbo não funciona sem ele, emsoftse degrada) e, para o CLI, emVERB_REQUIREMENTSemcli/src/preflight.ts. O preflight avisa antes do verbo rodar; o script ainda confere a própria dependência e sai com2. - Exemplo mínimo:
plugins/flux/scripts/example.sh(exigejq). - Writer do run:
plugins/flux/scripts/run.sh(bash e utilitários POSIX, maisiconv; semjqnemgit; por isso não entra norequires:, verrun.md) é o único que grava o~/.flux/runs/<run_id>/; a CLI e as skills só o chamam. O contrato está emshared/run.mde o teste emscripts/test-run.sh. - Gate de polling do watch:
plugins/flux/scripts/iterate-watch-gate.sh(exigeghejq) faz fora da LLM o poll da PR e só sai quando há algo a fazer; o contrato de argumentos, eventos e códigos de saída está em--help. É lançado pelo watch doflux:iterate.
Propostas de tradução, novos comandos, agents, melhorias de acessibilidade, integrações e novos engines/harnesses são bem-vindas. Antes de implementar uma mudança transversal, abra uma RFC com a tese, escopo, dados ou exemplos que a sustentam, alternativas e critérios de aceitação. Uma RFC pode virar PR, e uma PR pequena também pode ser aberta diretamente quando a decisão já estiver clara.
O Flux é uma ferramenta irmã do ecossistema: Violeet é o produto, Violeeter é o sistema visual, e Flux reutiliza essa linguagem com símbolo e wordmark próprios. [GLabs] é o guarda-chuva que amarra esses produtos, usando a identidade visual compartilhada da família.
Veja a landing page para instalação, ciclo e contribuições.
Antes de abrir PR, rode os checks. Eles pegam uma classe de erro que leitura não pega:
claude plugin validate . # marketplace
claude plugin validate ./plugins/flux # plugin + frontmatter de cada skill
scripts/check-manifests.sh # version e name iguais nos cinco manifests
scripts/check-codex-agent-contract.sh # o adaptador Codex continua descrito nos shared/
shellcheck plugins/flux/scripts/*.sh # recomendado ao tocar em scripts (ver "Scripts auxiliares")
scripts/test-iterate-watch-gate.sh # ao tocar em iterate-watch-gate.sh: gh e sleep falsos, sem rede e sem espera
scripts/test-run.sh # writer do run (run.sh): schema, permissões, recusa de path absoluto; roda em CI
(cd cli && bun test) # CLI, inclusive a paridade entre VERB_REQUIREMENTS e o requires: do review; roda em CI
Os dois validate conferem a forma de cada manifesto isoladamente, e recebem alvos disjuntos, então
nenhum dos dois enxerga .cursor-plugin/ nem .codex-plugin/. É por isso que o check-manifests.sh
existe: um bump que esquece parte dos cinco passa verde nos dois validate, e o mesmo corpo de skills
chega ao usuário com versão diferente conforme o harness. O check-codex-agent-contract.sh faz o
mesmo pelo texto dos shared/: é um canário que falha se a descrição do adaptador Codex sumir de
codex-compat.md, preflight.md ou review-agents.md. Os dois scripts rodam em CI
(checks.yml) a cada PR, então esquecer de rodá-los aqui custa uma
ida ao GitHub, não uma regressão publicada.
Sempre use aspas na
descriptiondo frontmatter. Um:(dois-pontos seguido de espaço) num valor YAML sem aspas quebra o parse, e o skill carrega com metadata vazia, silenciosamente: semname, semdescription, semuser-invocable. O sintoma é um1 error during loadgenérico no/reload-plugins, sem dizer qual arquivo. Ovalidatediz.
Versão nova na main sem aprovação é landing desatualizada. A landing e a página de releases leem o
docs/latest-release.json, e só o workflow release.yml o atualiza.
O merge de uma versão nova dispara o workflow sozinho; o que falta depois dele é um clique de aprovação. Com dois bumps esperando, aprove um de cada vez, o
mais antigo primeiro: aprovados juntos, eles disputam o selo Latest e o docs/latest-release.json.
Antes era uma tag criada à mão, e foi esquecendo dela que as versões 1.32.0 a 1.38.0 saíram sem release
e a landing ficou parada em 1.31.0.
O fluxo completo:
- Na PR, bumpe a versão nos cinco manifests (
.claude-plugin/marketplace.json,.cursor-plugin/marketplace.jsone os três deplugins/flux/) e rodescripts/check-manifests.sh. Se quiser um resumo próprio na landing, ponha no corpo da PR as linhasSummary-en:eSummary-pt:, cada uma no começo da linha e fora de bloco de código, em até 180 caracteres e sem travessão. O limite e o travessão são convenção: o workflow copia a linha como está, sem validar nem cortar. - Faça o merge na
main. O push que toca oplugins/flux/.claude-plugin/plugin.jsondispara o workflowrelease, e o jobplanconfere se a tagv<versão>já existe. Existindo, a run termina ali, sem pedir nada: o arquivo mudou sem a versão mudar. - Aprove a run no Environment
release(aba Actions, ou o link que o GitHub manda). É o único gate humano da release. O resumo da run mostra a mensagem exata que a tag vai levar: ela é montada antes do pedido de aprovação, e editar a PR depois disso não muda o que será publicado. - Aprovada, a run cria a tag anotada
v<versão>no commit do merge, publica a release e commita odocs/latest-release.jsonnamain. Esse commit só acontece quando a versão é a corrente (guardais_latest); uma versão mais antiga publica a release sem mexer na landing.
A mensagem da tag vira o changelog, e quem a escreve é o workflow:
<título da PR>
PR: <url da PR>
Summary-en: <a linha do corpo da PR, quando existe>
Summary-pt: <a linha do corpo da PR, quando existe>
O scripts/release-meta.sh lê os dois trailers. Sem eles, o resumo da landing cai na primeira linha, que
é o título da PR, igual nos dois idiomas e cortado em 180 caracteres; o valor de um trailer é publicado
sem corte. Num push direto na main, sem PR, a primeira linha é o assunto do commit.
Para conferir depois do merge:
gh run list --workflow release
git pull && cat docs/latest-release.json
O docs/latest-release.json deve trazer a versão nova. Sem essa linha, a landing não mudou.
Três caminhos, e todos passam pela mesma aprovação. Os dois últimos são os gatilhos antigos, que continuam existindo:
- A run foi rejeitada, expirou sem aprovação ou falhou no meio. Reexecute só o que falhou
(
gh run rerun --failed <id>) e aprove. O--failedimporta: um rerun completo refaz oplan, e se a tag já tinha sido criada ele conclui que não há o que publicar. Se o GitHub não deixar mais reexecutar a run (a janela é limitada), crie a tag à mão, como no último item. - A tag existe e a release não, ou a release saiu errada. Republique pelo dispatch:
gh workflow run release -f version=<x.y.z>. O dispatch não cria tag: ele publica a que existe. - Tag de catch-up, ou mensagem escrita à mão. Crie a tag anotada e publique; o push dela dispara o workflow. Tag leve é recusada de propósito, e quem recusa é o workflow (o passo que busca o objeto da tag anotada), não o script isolado.
git tag -a v<x.y.z> <sha-da-main> -F <arquivo-com-a-mensagem>
git push origin refs/tags/v<x.y.z>
Uma tag por versão. Se versões ficaram sem tag, uma tag de catch-up na versão mais recente cobre as puladas: o changelog dela deve dizer o que entrou desde a última tag.
O gate é um Environment
do GitHub com revisor obrigatório, e ele precisa existir antes de o workflow rodar: um job que
referencia um Environment inexistente o cria sem proteção nenhuma e publica sem pedir aprovação. Num
fork, crie o seu antes do primeiro bump (Settings, Environments, release, Required reviewers), com
"Prevent self-review" desligado se você é o único mantenedor.
Duas regras que valem para qualquer contribuição:
- Nada de contexto de time hardcoded. Se o seu time precisa de algo, isso vira campo do manifesto, nunca literal dentro de um verbo. O contrato está em
shared/flux-context.md. - Degradar bem em vez de rodar mal. Toda capacidade nova entra com o caminho de ausência definido e declarado no banner de perfil.
- Violeet — terminal macOS que roda vários agentes de IA como abas de uma janela só, com o HITL na sidebar. O fan-out da família fica visível ali: uma aba por agente.
MIT.