Algumas decisões são grandes demais para confiar a uma única voz. O Council é a diretoria completa de modelos de IA do harness — membros com pesos e camadas que debatem, votam e sintetizam um veredito, com tetos de custo e uma trilha de auditoria completa. É o irmão peso-pesado do fusion. E sob ambos há uma peça discreta de encanamento que esta lição também desmonta: a família de adapters de três camadas — camadas bash, mjs e TypeScript que comandam as mesmas CLIs de agentes a partir de uma única fonte da verdade comprovada.
A esta altura você já viu o loop entregar um trabalho a um único assistente, e já viu o fusion fazer a mesma pergunta a um pequeno painel de uma vez e ter um juiz a fundir as respostas. O Council é o próximo degrau acima. É uma diretoria permanente de modelos de IA — um grupo configurado, cada um com um papel nomeado, um peso de voto e um orçamento de custo — convocada para decidir as questões importantes demais para deixar a uma só voz.
O formato é deliberadamente parecido com um comitê de verdade. Cada membro lê a pergunta e a evidência, então registra um voto — GO, PIVOT ou NO-GO. Os votos são ponderados (um membro mais confiável conta mais), pontuados contra as dimensões que a diretoria valoriza, e dobrados por um sintetizador em um único veredito escrito: uma recomendação, o quanto a diretoria está confiante, o quanto houve concordância e — crucialmente — a dissidência que não venceu, anotada de propósito para que nunca se perca. A sessão inteira tenta de novo se um membro tropeça, para se estourar seu orçamento de tempo ou de dinheiro, e é registrada por completo para que um humano possa reproduzi-la depois.
Essa última parte é o ponto do Council, e o que o separa de um painel rápido: ele é feito para decisões em nível de doutrina — as regras e grandes apostas pelas quais um projeto se rege — e para atuar como um Validador peso-pesado de consenso quando estar confiantemente errado sairia muito caro. Uma pessoa convoca a diretoria para esses momentos. O fusion é a segunda opinião do dia a dia; o Council é aquele que você chama quando a resposta vira política.
Pense nisso como… um tribunal, não uma conversa de corredor. O fusion é perguntar de passagem a três colegas afiados e captar o sentimento da sala. O Council é uma bancada completa de juízes: cada um tem assento e peso, cada um escreve seu parecer, a maioria forma uma decisão, e os votos dissidentes ficam registrados nos autos oficiais — porque amanhã alguém vai precisar saber não só o que foi decidido, mas por quê, e quem discordou. Onde a analogia se dobra: aqui os juízes são modelos de IA, a "decisão" é um relatório JSON mais um memorando, e a audiência inteira pode ser reexecutada a partir de sua transcrição.
Uma diretoria é definida por três tipos de arquivo. members/<id>/member.yaml declara cada modelo num contrato de cinco eixos — model (provedor + id + fallback), a chave adapter que sabe como chamá-lo, um voting.weight de 0 a 2, uma cost_policy (tetos por turno e por execução), e as ferramentas e o escopo de sistema de arquivos que ele tem permissão de usar. boards/<id>/board.yaml conecta esses membros a papéis, define thresholds (os cortes de pontuação para GO / PIVOT), declara tension_pairs (as dialéticas que o sintetizador deve testar explicitamente — "velocidade vs diligência", "valor para o usuário agora vs mudança de mercado"), e fixa as constraints de tempo e orçamento. Um workflows/<id>.yaml define a ordem das fases — quantas rodadas, quem sintetiza, quem verifica.
Cada execução emite um decision-report.json num schema fixo: um vote_tracker com a posição de cada membro na rodada 1 e na rodada 2 (e se eles shifted e por quê), um weighted_score, uma recommendation (GO | PIVOT | NO-GO), um nível de confidence e de consensus, os top_tensions com sua resolução, e um array dissent_preserved[]. Um membro observador — o verificador — não vota; ele decompõe as afirmações em unidades atômicas, checáveis por ferramenta, e as classifica numa escada de cinco níveis (PERFECT → VERIFIED → PARTIAL → FEEDBACK → FAILED). "Um PERFECT falso é pior que um PARTIAL honesto", então o verificador classifica de forma conservadora, e uma tensão não resolvida rebaixa a confiança em um nível.
Toda mudança é classificada de T1 a T4 (um tier-classifier.mjs sugere uma a partir de uma descrição). Uma camada baixa roda um workflow rápido e barato; uma camada alta convoca a diretoria completa para mais rodadas. A diretoria só encerra uma deliberação depois de estar além do seu tempo e orçamento mínimos, e o runner aborta o converse() em código — não por pedido educado — no instante em que atinge o máximo. Custo e auditoria são de primeira classe: cost_usd, duration_ms, e um activity.jsonl apenas-acréscimo fazem parte de toda execução.
Aqui está a audiência inteira em uma só imagem. A mesma pergunta vai a cada membro de uma vez. Cada um devolve um voto. Os votos são ponderados e pontuados, um sintetizador os redige em um único veredito, e um verificador à parte checa as afirmações antes de qualquer coisa ser assinada. Repare que o verificador é um observador — ele nunca vota sobre o resultado que está checando.
Os três membros do diagrama correspondem a papéis reais de member.yaml. Cada um declara um voting.weight (0–2), uma default_position, se ele may_abstain, e se ele is_observer (o verificador marca isto como true, então nunca vota). Abaixo está o formato da composição de uma diretoria — valores de exibição, não um despejo de configuração.
advogado
claude-clicético
codex-cliarquiteto
kimi-cliO sintetizador deve tentar resolver ao menos um tension_pair declarado por execução, ou marcá-lo como unresolved em top_tensions[] — e uma tensão não resolvida derruba a confidence em um nível. É assim que a diretoria é forçada a encarar sua discordância mais difícil em vez de varrê-la para baixo do tapete.
O Council é mais poderoso que o fusion, mas poder tem um preço: mais modelos, mais rodadas, mais tempo, mais dinheiro. Então você não recorre a ele por padrão. A pergunta honesta é o quão caro seria estar errado. Se um erro custa alguns minutos para refazer, uma única verificação — ou um painel rápido de fusion — basta. Se um erro fixaria uma regra que o projeto inteiro segue, ou levaria uma longa execução autônoma pelo caminho errado, é aí que você convoca a diretoria.
Uma IA trabalhando dentro do harness faz exatamente essa escolha o tempo todo, e a regra que ela segue é justamente essa: escolha a ferramenta mais leve que sirva ao que está em jogo. Uma unidade rotineira recebe um assistente. Uma bifurcação arriscada ou genuinamente ambígua recebe o fusion — um painel cego cuja concordância é o sinal barato mais forte que você pode obter. Uma decisão em nível de doutrina, ou um veredito que precisa resistir a escrutínio depois, recebe o Council. Gastar o orçamento de uma diretoria numa pergunta trivial é tão erro quanto carimbar uma mudança de doutrina com uma única opinião.
Pense nisso como… decidir quão séria uma consulta médica precisa ser. Um arranhão é um curativo — resolva você mesmo. Algo incômodo rende uma palavra rápida com um par de médicos que por acaso estão de plantão. Mas um diagnóstico que muda a vida vai para uma conferência de caso completa onde cada especialista opina e a decisão é documentada. Você ajusta a cerimônia ao custo de errar — não ao quão interessante a pergunta parece.
A referência do loop traça a linha no Portão da Prova. Uma unidade normal é checada uma vez. "Quando uma unidade é arriscada ou genuinamente ambígua e estar confiantemente errado é caro", o Portão da Prova roda como um painel→juiz do fusion — N painelistas cegos pelo roster ao vivo, então um juiz reduzindo a consenso / contradições / pontos cegos e um veredito fundamentado (e, para código, ele roda os dois candidatos e funde o resultado verificado). O Council é a camada acima disso: o mesmo músculo de painel→juiz, mas permanente, ponderado, em camadas, orçado e auditado — para arbitrar doutrina e para um Validador de consenso no trabalho de maior risco.
O próprio tier-classifier.mjs --change "<description>" do Council emite {tier, board, workflow, reason}. Mudanças T1/T2 rodam leve; T3/T4 — doutrina e arquitetura — convocam a diretoria completa para mais rodadas. O classificador é a versão codificada de "escolha pelo que está em jogo": o tamanho da mudança escolhe o peso da audiência.
Escolha uma situação, então pressione Próximo para percorrer a decisão que o harness faz. Cada losango é uma pergunta sim/não sobre o que está em jogo; siga por onde um "não" se desvia e observe em qual ferramenta o caminho aterrissa — um assistente, um painel de fusion, ou a diretoria Council completa.
Comece aqui
Uma unidade surge para revisão
Pressione Próximo para seguir a unidade rotineira pela escolha. Troque a situação acima para ver como uma decisão mais arriscada é encaminhada de forma diferente.
O fluxograma são apenas duas verificações de retorno antecipado: barato por padrão, escala só quando o que está em jogo exige. A ordem importa — você pergunta "estar errado é caro?" antes de perguntar "isto é doutrina?", porque a maioria das unidades reprova na primeira verificação e nunca chega à segunda.
function pickReviewer(unit) { if (!unit.wrongIsExpensive) return 'one-assistant'; // rotineira → verificação única if (!unit.isDoctrine && !unit.mustBeAudited) return 'fusion'; // arriscada/ambígua → painel cego return 'council'; // doutrina / auditada → a diretoria }
Afaste-se do Council por um momento, porque há uma segunda ideia escondida sob ele — e sob o fusion também. Cada um desses sistemas precisa fazer a mesma coisa humilde: de fato chamar o programa de linha de comando de um modelo de IA e ler o que volta. Essa pecinha de encanamento se chama adapter. E neste conjunto não há um adapter, mas uma família deles, em três camadas, todos comandando as mesmíssimas CLIs de agentes.
Por que três? Porque três partes diferentes do conjunto precisam fazer a mesma chamada com quantidades diferentes de blindagem. A camada bash é a que o fusion usa — scripts de shell rápidos que rodam uma CLI e pegam sua saída. A camada mjs é a que o Council usa — a mesma chamada, agora embrulhada com retentativas, controle de custo e um log de auditoria, devolvendo um objeto de resultado organizado. A camada TypeScript vive numa base de código de produto e adiciona a blindagem mais pesada de todas — validação estrita nas bordas, a garantia de que nunca trava, um "disjuntor" que para de martelar um serviço que está falhando, e testes. Mesmo destino, três espessuras de cinto de segurança.
O que reter: todas discam o mesmo número. O jeito exato e comprovado de invocar cada CLI — quais flags, em que ordem — é compartilhado entre as três camadas. Então, quando você aprende como o harness chama o Kimi ou o Codex uma vez, você aprendeu em todos os lugares.
Pense nisso como… três veículos construídos sobre um único motor. Um kart (bash) é leve e veloz e quase não tem equipamento de segurança. Um carro de família (mjs) acrescenta cintos, airbags e uma luz de revisão. Um carro de corrida com telemetria completa (TypeScript) acrescenta uma gaiola de proteção, um botão de desligamento e uma equipe de boxes vigiando cada leitura. Trabalhos diferentes, proteções diferentes — mas o mesmo bloco do motor, e se você retoca o motor, retoca os três.
run_*.sh do fusion — roda uma CLI, captura o stdout. Acrescenta um timeout em perl (o macOS não tem timeout), um pty para CLIs sem TTY, guardas anti-vazio, e um workdir descartável.adapters/*.mjs do Council — invoke(opts) → Result. Acrescenta retry/backoff, contabilidade de custo, um log de auditoria, e um resultado estruturado de sucesso/falha.local-cli.ts do Alembic — ModelAdapter.run(input) → ModelRunResult. Acrescenta uma fronteira validada por Zod, um invariante de nunca-lançar, um disjuntor, e testes.bash — ~/.claude/skills/fusion/scripts/run_*.sh: shell, captura o stdout; acrescenta o helper de timeout em perl (sem gtimeout no macOS de fábrica), um pty para CLIs que não emitem nada sem um TTY, retentativas anti-vazio, e uma cópia descartável do workdir para que as escritas de um painelista nunca toquem o seu checkout.
mjs — scripts/council/adapters/*.mjs: cada um exporta invoke({ memberId, prompt, system, model, maxTokens, runId, timeoutMs, cwd, signal }) → Result. Um Result de sucesso é { success:true, response, cost_usd, latency_ms, model_version_used, raw }; uma falha é { success:false, error, retryable, status_code?, reason?, latency_ms }. Um _lib.mjs compartilhado fornece withRetry, isTransient, writeAudit, estimateCost e runSubprocess.
ts — appfy/alembic/packages/adapters/src/local-cli.ts: um ModelAdapter.run(input) → ModelRunResult numa fronteira validada por Zod. "A chamada inteira é embrulhada para que NUNCA lance: erros de spawn, saídas não-zero e timeouts viram todos falhas classificadas." Um disjuntor para de chamar um serviço que continua falhando, e o comportamento é coberto por testes.
Eis por que a família importa, contado como um pequeno desastre. O jeito exato de chamar uma CLI é cheio de detalhes — o Kimi, por exemplo, recusa algumas combinações de flags e falha em silêncio com outras. Uma vez, aquela invocação exata derivou: alguém a ajustou no adapter do Council e, separadamente, na tabela do fusion, e os dois lentamente saíram de sincronia. A correção foi uma regra pela qual o conjunto inteiro agora se rege: há uma única fonte da verdade para as invocações headless comprovadas, e as três camadas devem permanecer em sincronia com ela.
Este é um exemplo perfeito e minúsculo da disciplina anti-suposição do loop aplicada ao encanamento. Você não lembra como chamar uma CLI e torce para que ainda funcione — flags derivam entre versões. Você reprova isso contra a CLI ao vivo, e quando muda uma camada você atualiza as outras duas no mesmo fôlego. Isso é trabalho para um agente: quando uma flag deriva, reprove-a na fronteira real, então propague a correção por bash, mjs e TypeScript para que nunca mais discordem.
Pense nisso como… uma receita pregada numa cozinha compartilhada. Três cozinheiros trabalham a partir dela. No dia em que um cozinheiro rabisca uma mudança na própria cópia, os pratos param de bater. A regra que salva a cozinha: há uma receita-mestra na parede, toda cópia é conferida contra ela, e você só a muda depois de ter de fato cozinhado a nova versão e ela ter funcionado.
Estas são as chamadas headless comprovadas — verificadas na fronteira real em 2026-06-16, espelhadas no run_*.sh do fusion, no *.mjs do Council, e na memória cross-agent-headless-cli-flags. Não as "melhore" sem reprovar contra a CLI ao vivo.
# codex — caminho padrão é cliproxyapi /v1/chat/completions (texto limpo + tokens) # caminho de subprocess (COUNCIL_CODEX_USE_SUBPROCESS=1): codex exec --model <m> --quiet <prompt> # kimi — rode NO cwd. Recusa -p + --yolo; NÃO tem --work-dir/--print/--quiet kimi -p <prompt> --output-format text # grok — -p é --single; --output-format é plain|json|streaming-json (SEM "text") grok -p <prompt> --output-format plain --always-approve --cwd <cwd>
O README detalha a disciplina: (1) reprove a invocação contra a CLI ao vivo rodando o smoke embutido do adapter — flags derivam entre versões; (2) atualize o run_*.sh correspondente (fusion) e a memória cross-agent-headless-cli-flags para que as três camadas fiquem idênticas; (3) lembre da lacuna entre plataformas — o macOS não tem timeout/gtimeout, então cada camada lida com isso à sua maneira (helper em perl no bash, o timeoutMs do runSubprocess no mjs, a política do withRetry no ts).
# cada adapter traz um smoke `node <adapter>.mjs` no fim do arquivo cd ~/Documents/Resources/scripts/council/adapters node codex-cli.mjs # imprime um Result ao vivo — prova, não memória node kimi-cli.mjs
Para tornar a camada do meio concreta, eis o formato de um adapter do Council. O detalhe que importa mesmo de relance: ele nunca apenas devolve texto. Ele devolve um Result — ou um sucesso carregando a resposta, seu custo e quanto tempo levou, ou uma falha que diz o que deu errado e se vale a pena tentar de novo. É essa estrutura que permite a uma diretoria acompanhar seu orçamento e escrever uma trilha de auditoria honesta.
scripts/council/adapters/codex-cli.mjs — invoke() devolve um Resultexport async function invoke({ memberId, prompt, system = '', model = 'gpt-5.5', maxTokens = 128000, reasoningEffort = 'high', runId = null, timeoutMs = 300_000, } = {}) { if (!prompt) return { success: false, error: 'prompt is required', retryable: false }; if (process.env.COUNCIL_CODEX_USE_SUBPROCESS === '1') { // caminho de CLI opcional const bin = CODEX_BIN_CANDIDATES.find(p => existsSync(p)); if (bin) { const r = await withRetry(() => invokeSubprocess({ bin, prompt, system, model, timeoutMs }), { adapter: 'codex-cli:exec', model, runId, maxAttempts: 2 }); if (r.success) return r; } } // padrão: roteia via cliproxyapi, com retry/backoff return withRetry(() => invokeProxy({ prompt, system, model, maxTokens, reasoningEffort, timeoutMs }), { adapter: 'codex-cli', model, runId, maxAttempts: 3 }); }
Os adapters ficam em scripts/council/adapters/ (uma cópia vendorizada também é distribuída em loop-engineering-suite/vendor/council/engine/adapters/). Abra o arquivo e leia o bloco de comentário do topo — cada adapter documenta por que escolheu HTTP vs subprocess e quais flags são essenciais. O contrato do Result de sucesso/falha é definido em _lib.mjs.
cd ~/Documents/Resources/scripts/council/adapters # os helpers do Result + retry/custo/auditoria vivem aqui: sed -n '1,30p' _lib.mjs # o README é a única fonte da verdade para as invocações: sed -n '1,40p' README.md
Repare no withRetry(..., { adapter, model, runId, maxAttempts }) embrulhando os dois caminhos: falhas transitórias (um 5xx, um 429, um timeout) são retentadas com backoff; uma falha dura (prompt ausente, chave ausente) é devolvida imediatamente com retryable:false. Essa é a blindagem da camada mjs — a mesma chamada do bash, agora segura para rodar sem supervisão dentro de uma diretoria.
Você já conheceu as duas ideias desta lição — a diretoria e a família de adapters — então aqui está como os três tipos de participante as usam, reunidos num só lugar. A divisão é a mesma que percorre o harness inteiro.
Um humano convoca uma diretoria para as maiores decisões — uma mudança de doutrina, uma aposta com que o projeto vai conviver — e então recua para a observabilidade: lê o veredito, a confiança e a dissidência preservada na trilha de auditoria, mas não participa da deliberação. Uma LLM trabalhando no loop escolhe o instrumento certo para o que está em jogo: um assistente para trabalho rotineiro, o fusion para uma bifurcação arriscada ou ambígua, o Council quando a resposta vira política ou precisa resistir a escrutínio depois. E um agente que mantém o conjunto guarda o encanamento: quando uma flag de CLI deriva, ele reprova a invocação na fronteira real e atualiza as três camadas — bash, mjs e TypeScript — para que nunca discordem.
Pense nisso como… a governança de uma empresa. Os membros do conselho (humanos) decidem as grandes votações e leem a ata depois. O gerente de plantão (a LLM) decide, hora a hora, quais decisões precisam de uma conversa rápida e quais precisam do conselho completo. E a equipe de manutenção (o agente) mantém a fiação do prédio dentro das normas — reconferindo após cada mudança para que nada falhe no meio da reunião.
A única coisa para lembrar
O Council é o fusion crescido: uma diretoria ponderada, em camadas e auditada para decisões em nível de doutrina e um Validador de consenso. Ambos rodam sobre uma família de adapters — bash, mjs, TypeScript — que disca as mesmas CLIs a partir de uma única fonte da verdade comprovada. Ajuste a cerimônia ao que está em jogo; mantenha as três camadas em sincronia.
Três perguntas rápidas. Escolha uma resposta para ver se ela acerta — e por quê.
Q1Quando você deve convocar o Council em vez de um painel rápido de fusion?
Q2O que o voting.weight de um membro do Council faz?
Q3Uma flag de CLI deriva. O que um agente de manutenção faz?
decision-report.json de uma diretoria real, ou assistir o smoke de um adapter imprimir um Result ao vivo? Peça a ele para abrir scripts/council/adapters/README.md e rodar node codex-cli.mjs com você. A seguir — o cinto de ferramentas do dia a dia ao qual o loop recorre: Bright Data, Computer Use e ultragoal.