Lição 18 · Curso de Fusão · Council e verifier
Alembic × Hermes · O Curso de Fusão · Parte 3 · A arquitetura

Council e verifier: debater, pontuar, provar

O motor de decisão L2 é um kernel em camadas: um DebateEngine que preserva a divergência, uma pontuação quantitativa 0–10, um Verifier maker-checker independente e um painel de N lentes que é o gate de emissão a partir de T3. O truque que o torna confiável: contrarian por último e verificação read-only são garantidos pela estrutura, não por prompts. Fonte: packages/council/src/{debate,scoring,consensus,verifier,verifier-panel,board}.ts, governado pela ADR-0003.

Cada bloco tem uma visão Simples e uma Técnica. Abra conforme a sua fome.
00

O que você vai dominar

Suposições tolas (o que presumimos de você)
  • Você já viu a cintura estreita (Lição 14): cada adapter.run() devolve um ModelRunResult discriminado em oknunca lança.
  • Você já viu as quatro invariantes (Lição 16); a quarta é “o verificador é read-only”. Esta lição mostra onde ela ganha corpo.
  • Você sabe o que são tier (T1…T4) e Result. Não vamos reapresentá-los.
Ao final desta lição, você será capaz de…
  • Explicar por que “o contrarian fala por último” é sequenciamento real, garantido no carregamento do board, e não uma instrução de prompt.
  • Ler a pontuação 0–10 corretamente — incluindo por que risk = 10 significa baixo risco e não há inversão.
  • Prever o veredito de um quorum esparso (a regra MIN_VALID_AGENTS = 3, fail-closed).
  • Descrever por que o Verifier é read-only por arquitetura, e como o painel de 3 lentes (quorum e veto) vira o gate de emissão T3+.
A fonte primária (leia esta antes de tudo)
packages/council/src/debate.ts

O comentário de cabeçalho de debate.ts diz, em código, tudo o que esta lição expande: fases seriais, membros paralelos, contrarian por último por ordenação. Quando a leitura terminar, volte e confira cada afirmação aqui contra o arquivo.

01

A grande ideia — quem decide não é quem aprova

O Council junta duas coisas que de propósito não confiam uma na outra. De um lado, um debate qualitativo entre membros — o maker, probabilístico, que chama modelos e tem opiniões. Do outro, uma verificação determinística sobre a evidência — o checker, que não chama modelo nenhum e só sabe pesar fatos estruturados. É essa assimetria que permite ao Alembic afirmar “decisão verificada, com dissent preservado” — não apenas “o LLM achou que era uma boa ideia”.

Analogia: um júri delibera (o maker), mas é o juiz que confere se o procedimento foi válido (o checker). O juiz não re-julga o mérito gritando mais alto; ele verifica regras objetivas — houve quórum? o veredito bate com as provas? Quem grita não é quem carimba.
Diagrama em duas metades: à esquerda o maker probabilístico (membros A, B e o contrarian por último, em fases seriais) entrega DebateResult mais ContextPack em modo só-leitura ao checker à direita, onde um Verifier read-only e as três lentes COHERENCE, FAITHFULNESS e DOMAIN avaliam por quorum de 2 de 3, com um hard failure em qualquer lente vetando o quorum.

O Council em uma imagem: o maker (terracota) debate e entrega DebateResult + ContextPack em modo só-leitura ao checker (verde-oliva), que prova com oráculos e um painel de lentes.

visão geral · o handoff é só-leitura MAKER · probabilístico membro A membro B contrarian (por último · vê tudo) debate, votos, axis scores DebateResult + ContextPack (RO) CHECKER · determinístico Verifier — read-only COHERENCE FAITHFUL. DOMAIN oráculos puros · quorum + veto
O maker entrega ao checker uma view somente-leitura. O checker nunca recebe um ModelAdapter — por isso não pode “re-debater”.

Por que a separação é estrutural

A confiança não vem de “pedir ao modelo para ser rigoroso”. Vem dos tipos. O maker produz um DebateResult; o checker (verifyDecision / verifyPanel) aceita só esse resultado mais o ContextPack, ambos readonly. Não há parâmetro ModelAdapter na assinatura do checker, então é impossível, no nível do tipo, ele chamar um modelo para mudar de ideia. A garantia é compilada, não prometida.

Esse é o mesmo princípio da Lição 16 (invariante ④): o poder de verificar e o poder de agir ficam em superfícies diferentes. Aqui, “agir” é “chamar o modelo / mudar o veredito” e essa superfície simplesmente não existe no checker.

02

O DebateEngine — fases seriais, membros paralelos

As fases rodam na ordem declarada pelo board (seriais), e os membros dentro de uma fase rodam em paralelo. Como as fases são seriais, uma fase posterior recebe as contribuições acumuladas das anteriores. É isto que faz “o contrarian vê tudo e fala por último” ser sequenciamento real, e não uma afirmação de prompt. O próprio comentário de cabeçalho do motor diz:

// packages/council/src/board.ts:23-24 — a intenção, verbatim
// members WITHIN a phase in parallel; "contrarian last" is enforced by placing
// the contrarian phase last here, not by a prompt instruction.
Preveja antes de revelar

Um board é carregado com a fase contrarian no meio da lista (não por último). O que acontece no carregamento?

É um erro duro de carregamento: buildBoard retorna { kind: 'contrarian_not_last' } e o board é rejeitado. A regra não é checada em runtime do debate — é checada uma vez, na hora de montar o board (board.ts:157-162). Um board mal-ordenado nunca chega a debater.
serial → (dentro de cada fase: ‖ paralelo) Fase 1 membro A ‖ B produz statements Fase 2 vê a Fase 1 priorPhases acumulam Contrarian (última) vê TUDO contrarian_not_last = erro duro de board-load agregar votos ponderados ConsensusResult cada adapter.run() obedece o never-throws: o motor ramifica em ModelRunResult.ok, sem try/catch ao redor de run()
Membros dentro de uma fase: Promise.all (paralelo). Entre fases: serial — por isso o contrarian recebe o acumulado.
O never-throws no debate. As chamadas de adapter obedecem a invariante da Lição 14: cada run() resolve para um ModelRunResult discriminado em ok, então o motor ramifica no resultado em vez de embrulhar a chamada num try/catch. Um membro que falha ao fazer bind, dá erro, ou devolve um voto não-parseável é registrado como um MemberFailureReason — nunca uma exceção lançada.
Para leigosSerial = um depois do outro (as fases). Paralelo = ao mesmo tempo (os membros de uma fase). Mistura-se os dois de propósito: paralelo para ser rápido, serial onde a ordem importa (o contrarian precisa ouvir todo mundo antes de discordar).
03

A pontuação — 0–10, e risco não é invertido

Cada membro emite scores por eixo sobre SCORING_AXES (feasibility / revenue / cx / ttm / risk), com pesos .25/.25/.20/.15/.15. GO_THRESHOLD = 7, PIVOT_THRESHOLD = 5. O detalhe de correção não-óbvio: o eixo risk é pontuado de modo que 10 = risco baixo, somando na mesma direção de todos os outros eixos — não há normalização 1 - risk para inverter por engano.

// packages/council/src/scoring.ts — a direção é uniforme; os nomes dos eixos são fonte única de verdade
export const SCORING_AXES = ['feasibility', 'revenue', 'cx', 'ttm', 'risk'];  // um typo não pode zerar um eixo em silêncio
export const GO_THRESHOLD = 7;   // score alto → GO
export const PIVOT_THRESHOLD = 5; // entre 5 e 7 → PIVOT
// risk 10 = risco BAIXO; SEM inversão — todos os eixos somam do mesmo jeito
cinco eixos · pesos somam 1.00 · todos na mesma direção feasibility.25 revenue.25 cx.20 ttm.15 risk*.15 * risk: 10 = risco baixo (sem inversão) régua de decisão 0 5 · PIVOT 7 · GO 10 <5 NO_GO 5–7 PIVOT ≥7 GO
Os pesos somam exatamente 1.00 (um teste cobre isso). A régua: ≥7 GO, 5–7 PIVOT, <5 NO_GO.
Exemplo resolvido — agregando um voto
1
Um membro vota: feasibility 8, revenue 6, cx 7, ttm 5, risk 9 (lembre: 9 = risco baixo).
2
Multiplique pelos pesos: 8·.25 + 6·.25 + 7·.20 + 5·.15 + 9·.15 = 2.00 + 1.50 + 1.40 + 0.75 + 1.35.
3
Some: 7.00. Como 7.00 ≥ GO_THRESHOLD (7) → este voto aponta GO.
4
Agora você: refaça com risk = 2 (risco alto) em vez de 9. O total cai para 7.00 − (9−2)·.15 = 5.95 → vira PIVOT. Veja como “risco alto” derruba o score na mesma direção que todos os outros eixos.
Um membro reporta risk = 10. Para a decisão, isso empurra na direção de…
(b). Não há inversão 1 - risk: 10 significa risco baixo, então soma a favor do GO, igual a feasibility ou revenue alto. Inverter isso viraria toda decisão de cabeça para baixo — por isso a direção é mantida uniforme.
04

Quorum fail-closed — um board degradado nunca aprova

aggregateConsensus devolve NO_GO antes de consultar qualquer score quando há menos de MIN_VALID_AGENTS = 3 votos válidos. “Um board esparso ou degradado nunca pode dar sinal verde.” Um board que perdeu dois membros para falhas de adapter não consegue passar por acidente com um único voto otimista.

// packages/council/src/consensus.ts:104-115 — fail-closed PRIMEIRO
// Fail-closed FIRST: if fewer than MIN_VALID_AGENTS votes are present…
if (votes.length < MIN_VALID_AGENTS) {
  return { decision: 'NO_GO', reason: 'insufficient-quorum' }; // score nem é olhado
}
a ordem importa: quorum é checado ANTES do score votos chegamalguns podem ter falhado ≥ 3 votosválidos? sim pesar o scoreGO / PIVOT / NO_GO não NO_GO (fail-closed)
Se o quorum falha, o score nunca é consultado. A única saída de um board degradado é NO_GO.
Cuidado“Médias altas vencem.” Não — primeiro o quorum. Dois votos com média 9 ainda dão NO_GO, porque 2 < 3. A média só é olhada depois que o quorum passa.
05

O Verifier — read-only por arquitetura

O Verifier maker-checker é a encarnação da invariante ④ (Lição 16). Ele aceita só views readonly, não expõe nenhuma superfície de adapter ou mutação, e prova afirmações atômicas com oráculos determinísticos (ClaimOracle) sobre a evidência estruturada — nunca sobre a prosa do maker:

// packages/council/src/verifier.ts:22, 35, 49 — a arquitetura, verbatim
// READ-ONLY BY ARCHITECTURE: the verifier accepts only readonly views — the
// debate result and the ContextPack. There is no ModelAdapter parameter.
export type ClaimOracle = (evidence: VerifierEvidence) => {
  readonly proven: boolean;   // um oráculo puro: evidência → provado?
  readonly evidence: string;
};
export const MAX_LOOPS = 3; // loopCount > 3 → parked em T4, não retry infinito
  • Afirmações duras (quorum presente, verdict↔score consistente, auto-consistência do voto) que falham → rejected.
  • Loops limitados: quando a verificação não estabiliza, loopCount > MAX_LOOPS (3) → park em T4 (escalate-after-N), não um retry infinito.
claims → oráculos puros → provado / rejeitado / escalar claims atômicas quorum presente verdict ↔ score voto consistente ClaimOraclepuro · sem modelo verified rejected loopCount > 3 → parkedTier = T4escalate-after-N (humano decide)
Travar com elegância (park em T4) é melhor que carimbar uma decisão fraca ou girar para sempre.
Flashcard · vire
in?
O que o Verifier recebe na assinatura?
clique para ver
DebateResult + ContextPack, ambos readonly. Nenhum ModelAdapter — por isso não pode re-chamar o modelo.
Flashcard · vire
f(e)✓?
O que é um ClaimOracle?
clique para ver
Uma função pura: evidence → { proven, evidence }. Pesa fatos estruturados, sem chamar modelo nem ler a prosa do maker.
Flashcard · vire
T4
O que acontece em loopCount > 3?
clique para ver
escalate-after-N: a decisão não é forçada — parkedTier = T4 manda para um humano. Sem retry infinito.
06

O painel de N lentes — o gate de emissão T3+

Para T3 e acima, a emissão é barrada por verifyPanel / isPanelEmissionApproved — três lentes de perspectiva diversa agregadas por quorum com um veto preserve-dissent:

Máquina de decisão do gate T3: a decisão do Council passa pelas três lentes COHERENCE, FAITHFULNESS e DOMAIN, converge num teste de quorum maior ou igual a 2 de 3 que pode EMITIR em T3, mas um hard failure em qualquer lente dispara o veto preserve-dissent que rejeita tudo; se o loop não fecha acima de MAX_LOOPS igual a 3, a decisão é parked em T4.

O gate de emissão T3: três lentes convergem para um teste de quorum, mas qualquer hard failure dispara o veto preserve-dissent.

LenteVerifica
COHERENCE= verifyDecision reusado — quorum, verdict↔score, auto-consistência
FAITHFULNESSevidência presente e acima dos pisos: confiança média ≥ 0.5 e força de pico ≥ 3
DOMAINum GO precisa de um sinal de validation; a evidência não é monocultura
Quorum E um veto — os dois, de propósito

O painel aprova por quorum (padrão DEFAULT_PANEL_QUORUM = 2 de 3) mas um hard rejection em qualquer lente isolada veta o painel inteiro (preserve-dissent), e o escalate-after-N se propaga. Então uma maioria confiante de duas lentes ainda pode ser barrada por uma lente que achou um defeito fatal. Essa conjunção — maioria para passar, qualquer-veto para barrar — é da que depende o verified-GO do funil (Lição 15). Passar é deliberadamente mais difícil do que num voto simples.

três cenários · só o primeiro emite EMITE (T3) COH ✓ FAI ✓ DOM ✗ 2/3 quorum ✓ sem hard failure ✓ a falha de DOM foi “soft” VETO (rejeita) COH ✓ FAI ✓ DOM ✗! 2/3 quorum ✓ hard failure em DOM ✗ o veto vence o quorum NÃO EMITE COH ✓ FAI ✗ DOM ✗ 1/3 quorum ✗ abaixo de 2 → barra nem chega ao veto
Caso 1 emite (quorum + nenhum veto). Caso 2 tinha quorum, mas um hard failure vetou. Caso 3 nem alcançou o quorum.
Duas das três lentes aprovam, mas a lente DOMAIN emite um hard rejection (GO sem sinal de validation). O painel aprova a emissão?
(d). O painel precisa de quorum e nenhum veto. A regra preserve-dissent faz uma lente que achou um defeito fatal barrar a emissão mesmo com as outras passando. É a conjunção da qual o verified-GO do funil depende.
07

Explorar o pipeline ao vivo

Clique pelos quatro estágios — makercheckergate T3park T4 — e veja qual parte do desenho acende, com o arquivo-fonte de cada um:

maker · debate membros ‖ · contrarian último checker · verifier read-only · oráculos gate T3 · 3 lentes quorum + veto park T4 · humano loopCount > 3

Estágio aceso: o maker.

maker

Debate qualitativo (o maker)

packages/council/src/debate.ts

O gate, com as suas mãos

Mude o estado de cada lente e ligue/desligue o hard failure. Veja a regra “quorum E sem veto” decidir a emissão em tempo real:

COHERENCE · verifica FAITHFULNESS · verifica DOMAIN · verifica
Sob o capô“verifica” incrementa o quorum; o checkbox simula um HARD failure. A regra é exatamente !hardFailure && pass >= 2 — a mesma de isPanelEmissionApproved.
08

Confusões comuns

“Score de risco maior tem que significar mais risco.” Não — nesta pontuação, risk = 10 significa risco baixo, então soma na mesma direção de feasibility/revenue. Não há, de propósito, um passo 1 - risk; errar isso inverteria toda decisão, então os eixos são mantidos uniformes e os nomes dos eixos são fonte única de verdade.
“O Verifier é só mais um membro do council.” Não — ele não tem adapter e nenhuma superfície de mutação. É read-only por arquitetura e prova afirmações com oráculos determinísticos sobre a evidência estruturada, não perguntando a um modelo. É isso que o torna um check, não mais uma opinião.
“Quorum no painel é a palavra final.” Não — quorum é necessário, mas não suficiente. Um hard failure em qualquer lente veta tudo (preserve-dissent). E quorum no debate (MIN_VALID_AGENTS = 3) é uma coisa diferente do quorum no painel (DEFAULT_PANEL_QUORUM = 2 de 3 lentes): o primeiro conta votos válidos de membros; o segundo conta lentes que verificam.

Pense em duas portas em série. A primeira porta (debate) só abre se houver gente suficiente votando — e de modo coerente. A segunda porta (painel) só abre se a maioria das lentes aprovar e nenhuma puxar o freio de emergência. Para um sinal sair, as duas portas precisam abrir. É de propósito difícil.

porta 1 · debatequorum ≥ 3 + coerente porta 2 · painelquorum 2/3 + sem veto sinal emitido as DUAS portas precisam abrir

Em código: aggregateConsensus faz o gate de quorum (< MIN_VALID_AGENTS → NO_GO) antes de pontuar; verifyDecision prova as claims duras; e verifyPanel/isPanelEmissionApproved exige quorum (≥ DEFAULT_PANEL_QUORUM) e ausência de qualquer HARD failure entre as lentes. O escalate-after-N (MAX_LOOPS = 3) garante terminação: nada gira para sempre — vira park em T4.

09

Recapitulando — em cinco slides

Slide 1 · A ideia

Quem decide não é quem aprova

O Council separa o maker (debate, probabilístico) do checker (verifier + painel, determinístico). É essa assimetria que sustenta “decisão verificada”.

maker checker
1
Slide 2 · O debate

Fases seriais, membros paralelos

Membros falam em paralelo; as fases são seriais para que o contrarian, forçado por último pelo board loader, veja tudo. contrarian_not_last é erro de carregamento.

fase 1 ‖ fase 2 ‖ contrarian (último)
2
Slide 3 · A pontuação

0–10, e risco não inverte

GO_THRESHOLD = 7, PIVOT_THRESHOLD = 5. risk = 10 é risco baixo, somando como os outros eixos. Quorum primeiro: < 3 votos → NO_GO.

5 PIVOT 7 GO 010
3
Slide 4 · O gate T3

Três lentes, quorum, veto

COHERENCE + FAITHFULNESS + DOMAIN. Quorum padrão 2/3 — mas um hard failure em qualquer lente rejeita tudo (preserve-dissent). Loop > 3 → park T4.

COH FAI DOM EMITE veto barra ✗
4
Slide 5 · A frase

Read-only por arquitetura

O verifier não recebe modelo, então não pode ser “convencido”. Por isso o Alembic afirma “decisão verificada, com dissent preservado” — não “o LLM achou bom”.

read-only · sem adapter
5
Slide 1 / 5 use
As cinco coisas para nunca esquecer do L2
  1. Maker ≠ checker. Debate decide; verifier + painel aprovam — e não confiam um no outro.
  2. Contrarian por último, por ordenação. contrarian_not_last é erro de board-load, não um pedido de prompt.
  3. Risco não inverte. risk = 10 = risco baixo; sem passo 1 - risk.
  4. Quorum antes do score. < MIN_VALID_AGENTS (3) → NO_GO, sem olhar a média.
  5. Quorum E veto. O painel precisa de maioria e nenhum hard failure; senão, preserve-dissent veta ou park T4.
10

Verifique seu entendimento

Quiz de revisão — 3 perguntas
1. Como “o contrarian fala por último” é garantido?
(c). Membros dentro de uma fase são paralelos, mas as fases são seriais e a do contrarian é a última, então os statements anteriores já existem antes dos seus inputs serem montados. O board loader rejeita um board onde o contrarian não é o último — sequenciamento real, no carregamento, não um prompt (a, b). Não é “sem ordem” (d).
2. Um board produz só 2 votos válidos (um membro falhou no bind). Qual é o veredito?
(b). O quorum é checado primeiro: menos de 3 votos válidos é NO_GO automático. Um board degradado nunca pode dar verde (a, c), e a falha de bind é registrada como MemberFailureReason, não um throw (d).
3. Por que o Verifier é um check, e não apenas mais uma opinião do council?
(a). Read-only por arquitetura: a assinatura aceita só DebateResult + ContextPack, sem adapter — então não pode chamar modelo (b, d) nem é um “voto” (c). Oráculos determinísticos pesam a evidência.
Acertos: 0/3
Em uma frase, para você mesmo: “O Council separa ____ de ____; o checker é ____ porque ____; e a emissão T3 é barrada quando ____.” Se você preenche as cinco lacunas, está pronto para a Lição 19 · O swarm.
Próximo: Lição 19 · O swarm — como o Alembic paraleliza trabalho entre muitos workers. Depois de L2 (decisão confiável), L3 mostra como escalar a execução sem perder os gates. Siga em “Próxima”.