Curso / Lição 18
Lição 18 · Motor & método

Council & Verifier: debater, pontuar, provar

O motor de decisão é um kernel em camadas: um DebateEngine que preserva o dissenso, um sistema quantitativo de pontuação 0–10, um Verifier maker-checker independente e read-only, e um painel N-lentes que é o portão de emissão para T3+. O truque que o torna confiável: contrarian-last e verificação read-only são impostos estruturalmente, no carregamento e na arquitetura — nunca por um prompt.

Leia primeiro (fonte no repositório)
packages/council/src/{debate,scoring,consensus,verifier,board,verifier-panel}.ts

Esta lição destila esses arquivos, lidos literalmente, governados pela ADR-0003 (o dissenso é uma propriedade do sistema). Por que importa pra missão: é o gate que decide, com prova determinística, se uma decisão do council pode emitir — o GO-verificado de que o funil (Lição 15) depende.

Objetivos desta lição
  • Explicar por que "o contrarian fala por último" é sequenciamento real (fases seriais), não um claim de prompt.
  • Calcular o agregado 0–10 com os pesos reais e mapeá-lo a GO / PIVOT / NO_GO pelos limiares 7 e 5.
  • Distinguir o quórum fail-closed (MIN_VALID_AGENTS = 3) da pontuação — e por que ele vem primeiro.
  • Descrever o Verifier read-only e o painel N-lentes: quórum e veto de preservação-de-dissenso.
0 camadas
debate · pontuação · verifier · painel
0 votos
quórum mínimo (fail-closed)
0 / 10
limiar de GO (PIVOT ≥ 5)
0 lentes
painel · quórum 2, veto 1

01 · O kernel em quatro camadas

Antes dos detalhes, o mapa. Uma decisão atravessa quatro componentes, cada um num arquivo próprio em packages/council/src. O segredo de design é que cada camada tem um trabalho só e passa adiante uma estrutura imutável — não a prosa do modelo:

O KERNEL L2 · uma decisão flui por quatro camadas, cada uma com um trabalho
debate.ts DebateEngine fases seriais, membros paralelos → CouncilVote[] scoring + consensus Pontuação agrega 0–10, quórum fail-closed → ConsensusResult verifier.ts Verifier read-only; oráculos sobre prova → VerificationReport verifier-panel.ts Painel N-lentes gate de emissão T3+ → emite ✓ / veta ✗ Cada seta carrega uma ESTRUTURA imutável (votos, consenso, report) — nunca a prosa do modelo. O Verifier e o painel são separados do debate de propósito: quem decide não é quem prova (maker-checker).
Analogia simples. Pense num tribunal: o debate são os jurados deliberando; a pontuação é a contagem ponderada dos votos; o Verifier é o escrivão que confere se o veredito segue as regras (quórum presente, voto bate com a nota) — ele não vota, só confere os autos; e o painel é a câmara recursal que, em casos graves (T3+), exige maioria e permite que um único desembargador vete uma falha fatal.

02 · DebateEngine — fases seriais, membros paralelos

As fases rodam na ordem declarada do board (serial entre fases), 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 via buildModelInput. É isso que faz "o contrarian vê tudo e fala por último" ser sequenciamento real — o próprio comentário de cabeçalho do motor diz isso, literalmente:

// packages/council/src/debate.ts:30-44 — o contrato do motor, intenção literal
// Phases are run in the board's declared order (SERIAL between phases) and the
// members within a phase are dispatched IN PARALLEL. … The contrarian phase is
// ordered last by the board loader, so "contrarian sees everything and speaks
// last" is REAL sequencing — earlier phases have already produced their
// statements before the contrarian phase's model inputs are even built.
EXECUÇÃO · fases em série (→), membros em paralelo (‖), contrarian por último, depois agrega
serial entre fases → Fase 1 membro A ‖ B (paralelo) produz contribuições (PhaseContribution[]) Fase 2 vê a Fase 1 acumula no input Contrarian (último) vê TUDO o que veio antes contrarian_not_last = erro duro de board-load aggregateConsensus votos ponderados (weight × trust) → ConsensusResult (decisão + agregado) "contrarian por último" NÃO é um pedido no prompt — é a ordem das fases imposta no carregamento do board. Loop real: debate.ts:205-210 — o acumulador recebe as contribuições de cada fase antes da próxima começar.

Repare na assimetria que dá poder ao desenho: paralelo dentro da fase (rápido — os membros não esperam uns aos outros) mas serial entre fases (correto — a fase seguinte enxerga o que a anterior disse). O contrarian, sozinho na última fase, recebe o debate inteiro como contexto. Tirar a serialidade quebraria a garantia; por isso ela é estrutural, não opcional.

Preveja antes de continuar
Alguém edita o YAML do board e move a fase contrarian para o meio (não é mais a última). O que acontece quando o board é carregado?
Erro duro de carregamento — o board é rejeitado. validateBoardIntegrity (board.ts:145-166) acha o índice da fase contrarian; se ele não for o último, retorna err({ kind: 'contrarian_not_last' }). Não há board parcialmente formado: ou o contrarian é o último, ou não há board. A garantia "fala por último" é verificada no load, antes de qualquer modelo rodar. Se você chutou "um prompt avisa o modelo", caiu na armadilha que a arquitetura existe justamente para eliminar.

03 · Um membro nunca lança — ele vira um resultado tipado

Despachar um membro é uma pequena pipeline: bind → run → parse → buildVote. Cada passo pode falhar, mas dispatchMember nunca lança — ele honra o invariante nunca-lança (Lição 14) ramificando no resultado. Uma falha vira um MemberFailureReason tipado, não uma exceção que derruba o debate inteiro:

dispatchMember · cada passo ou avança ou vira uma falha tipada (sem try/catch em volta)
membro + fase bind ok? (adapter existe?) não failure: bind { kind:'bind', adapterId } sim run().ok? (ModelRunResult) não failure: adapter { kind:'adapter', code } sim parse ok? (parseAxisScores) não failure: parse { kind:'parse', detail } sim (todos) ✓ buildVote CouncilVote (nota + voto) Em qualquer ramo: retorna MemberOutcome, nunca lança. O debate segue com os votos válidos.

As três falhas formam a união discriminada MemberFailureReason = bind | adapter | parse (debate.ts:47-50); o despacho devolve um MemberOutcome (53-60) carregando ou um voto ou uma falha. Um membro que cai não vira exceção: vira um voto a menos — e é exatamente aí que o quórum fail-closed da seção 08 entra em cena.

Por que isso importa pro veredito. Se uma falha de um membro lançasse, ela poderia abortar o debate inteiro ou, pior, ser silenciosamente engolida e contada como "abstenção neutra". Tratando-a como um voto ausente tipado, o sistema sabe exatamente quantos votos válidos tem — e pode falhar fechado quando forem poucos.

04 · Pontuação — 0–10 sobre cinco eixos fixos

Cada membro emite uma nota por eixo sobre SCORING_AXES — cinco eixos numa escala 0–10 — e o motor combina tudo num único agregado 0–10 com pesos fixos. Os pesos somam exatamente 1.0, então o agregado fica na mesma escala dos eixos. Veja a fórmula peça por peça:

O AGREGADO, PEÇA POR PEÇA · soma ponderada de 5 eixos → uma nota 0–10 → um veredito
feasibility× .25 revenue× .25 cx× .20 ttm× .15 risk× .15 Σ computeAggregateScore agregado ∈ [0, 10] decisionFromScore ≥ 7 → GO ≥ 5 → PIVOT senão → NO_GO pesos somam 1.0, então o agregado fica na escala 0–10. Nomes de eixo são FONTE ÚNICA (SCORING_AXES): o mapa de pesos, o schema Zod e o agregador leem as mesmas chaves — um typo não zera um eixo em silêncio.

A nota e o voto de um membro vêm do mesmo agregado (buildVote, scoring.ts:122-135): o score é o agregado e a decision é decisionFromScore desse mesmo agregado. Por construção, a nota e o veredito de um membro nunca podem discordar — uma propriedade que o Verifier depois confere (seção 10).

A tabela de eixos e pesos (comparativo)

EixoPesoO que medeDireção
feasibility0.25É construível com o que temos?10 = alto / melhor
revenue0.25Potencial de receita10 = alto / melhor
cx0.20Experiência do cliente10 = alto / melhor
ttm0.15Time-to-market (rapidez)10 = alto / melhor
risk0.15Risco do empreendimento10 = BAIXO risco (soma igual!)

05 · O detalhe não-óbvio: o risco não é invertido

Aqui mora o bug clássico que a arquitetura recusa cometer. Quatro eixos são "mais é melhor" — fácil. Mas risco também é codificado assim: 10 = baixo risco, 0 = alto risco. Ele soma na mesma direção de todos os outros. Não existe em lugar nenhum um passo 1 - risk — e é justamente esse passo que inverteria todo veredito:

// packages/council/src/scoring.ts:17-27 — KNOWN-BUG GUARDS (do not regress)
// No GO/NO_GO inversion: a HIGH score yields GO, a LOW score yields NO_GO. The
// `risk` axis is scored such that 10 = low risk / 0 = high risk, so it adds to
// the aggregate in the same direction as every other axis. There is NO
// `1 - risk` normalization anywhere; doing so is what flips the verdict.
RISCO · o jeito CERTO (uniforme) vs o bug (1 − risk) — o mesmo input, vereditos opostos
✓ CERTO · direção uniforme risk = 9 já significa "risco baixo" entra no Σ como 9 (sem mexer) agregado ≈ 7.8 → GO todos os eixos somam na mesma direção um projeto de baixo risco PUXA a nota pra cima ✗ BUG · 1 − risk risk = 9, mas alguém "normaliza": 1 − 0.9 = 0.1 (vira quase zero!) agregado despenca → NO_GO o eixo risco agora SUBTRAI o MESMO projeto bom seria reprovado por engano
"Score de risco maior deve significar mais risco." É a intuição que derruba sistemas. Aqui, deliberadamente, risk = 10 significa baixo risco. Por isso não há passo 1 - risk: ele faria o eixo subtrair e inverteria toda decisão. Os eixos são mantidos uniformes e os nomes (SCORING_AXES) são fonte única — o comentário no código existe para que ninguém "conserte" o que não está quebrado.

06 · Faça a conta na mão (passo a passo → agora você)

Você viu a fórmula. Agora aplique-a devagar, com um caso concreto — depois um exercício é seu. Recuperar o procedimento (não só ver o número) é o que fixa.

Exemplo resolvido · um membro com notas [8, 7, 6, 9, 8] vota GO ou PIVOT?
1
Liste as notas por eixo, na ordem canônica. feasibility=8, revenue=7, cx=6, ttm=9, risk=8 (lembre: 8 = risco baixo, soma normal).
2
Multiplique cada uma pelo peso. 8×.25=2.0 · 7×.25=1.75 · 6×.20=1.2 · 9×.15=1.35 · 8×.15=1.2.
3
Some tudo. 2.0 + 1.75 + 1.2 + 1.35 + 1.2 = 7.5. Como os pesos somam 1.0, o resultado já está na escala 0–10.
4
Aplique os limiares. 7.5 ≥ GO_THRESHOLD (7) → GO. (Se desse, digamos, 6.2, seria PIVOT; abaixo de 5, NO_GO.)
5
Veredito. O voto deste membro é GO com score 7.5 — e nota e voto vêm do mesmo agregado, então não podem se contradizer.
Agora você: notas [6, 5, 7, 4, 5] (feas, rev, cx, ttm, risk). Qual o agregado e o veredito? Faça antes de revelar.
6×.25=1.5 · 5×.25=1.25 · 7×.20=1.4 · 4×.15=0.6 · 5×.15=0.75 → soma = 5.5. 5.5 está entre 5 e 7 → PIVOT. Dica: o procedimento é sempre o mesmo — só trocam as cinco notas. E note: o agregado de UM membro não é o veredito do board; o board agrega a média ponderada de TODOS os membros válidos (seção 08).

07 · Calculadora de veredito — sinta os limiares

Arraste os cinco eixos. A barra cresce na escala 0–10 e cruza as linhas de PIVOT (5) e GO (7); o veredito recalcula ao vivo com os pesos reais. Experimente afundar só o risk e veja que ele baixa a nota como qualquer outro eixo — não há inversão:

8
7
6
9
8
010 PIVOT ≥ 5 GO ≥ 7 7.5
GO
O agregado de um membro ≠ o veredito do board
Esta calculadora mostra um membro. O board faz a média ponderada (weight × trust) dos agregados de todos os membros válidos (weightedMeanScore) e aplica os mesmos limiares — board e membros falam uma escala só. Mas antes de olhar qualquer nota, vem o quórum (próxima seção).

08 · Quórum fail-closed — checado ANTES de qualquer nota

Esta é a propriedade de segurança que carrega o sistema. aggregateConsensus retorna NO_GO imediatamente quando há menos de MIN_VALID_AGENTS = 3 votos válidos — antes de consultar qualquer score. Um board esparso ou degradado nunca deve dar sinal verde:

FLUXOGRAMA · o quórum é o PRIMEIRO portão; a nota só é consultada depois dele
votos do debate (só os MemberOutcome com voto) votos válidos < 3 ? SIM ✗ NO_GO failClosed=true (sem ver a nota!) NÃO (≥ 3) média ponderada → decisionFromScore agora sim: GO ≥ 7 · PIVOT ≥ 5 · senão NO_GO consensus.ts :108-125 ordem importa

A ordem é a garantia: o quórum é a primeira coisa que a função olha. Um board que perdeu dois membros por falha de adapter (seção 03) tem só um voto válido — e não pode passar nem com a nota mais otimista do mundo. Junto disso, o board também mede o spread dos votos (CONSENSUS_SPREAD_THRESHOLD = 3): se as notas dos membros divergem muito, o board é marcado como "dividido" (consensus: false) mesmo que a média cruze um limiar — o dissenso não some na média.

O medidor de quórum

Arraste o número de votos válidos. Abaixo de 3, o veredito é NO_GO fail-closed independentemente de qualquer nota:

MIN_VALID_AGENTS = 3

Consenso ≠ média alta: o spread também conta

Com quórum atingido, o board ainda mede o spread = (maior nota − menor nota) dos membros. Se ele passa de CONSENSUS_SPREAD_THRESHOLD = 3, o board é marcado consensus: false (dividido) — mesmo que a média cruze um limiar. Dois boards com a mesma média de 7 podem contar histórias opostas:

MESMA MÉDIA (7.0), SPREADS OPOSTOS · à esquerda há acordo; à direita, dissenso escondido na média
✓ consenso · spread ≤ 3 m1 = 7 m2 = 6 m3 = 8 média 7.0 · spread = 8−6 = 2 os membros realmente concordam consensus: true ✗ dividido · spread > 3 m1 = 10 m2 = 7 m3 = 4 média 7.0 · spread = 10−4 = 6 um adora, um odeia — a média mente consensus: false (dissenso preservado)

É a mesma filosofia da ADR-0003: o dissenso é uma propriedade do sistema, não ruído a ser apagado pela média. O Verifier depois prova a claim mole consensus-spread (seção 10) exatamente sobre esse número.

09 · O Verifier — read-only por arquitetura

O Verifier maker-checker é a encarnação do invariante ④ (Lição 16). Ele aceita apenas vistas readonly — o DebateResult do maker e o ContextPack — e não expõe adapter, registry, nem nenhuma superfície de mutação. Não pode re-rodar um modelo, não pode editar um voto, não pode mudar uma decisão. Só pode inspecionar evidência e emitir um veredito. É essa separação que o torna um checker, não um segundo maker:

O QUE O VERIFIER PODE TOCAR · vistas readonly de um lado, ZERO superfície de mutação do outro
Verifier predicados puros sobre evidência consensus (readonly) votes (readonly) pack (readonly) VerifierEvidence (imutável) ✓ lê adapter ✗ registry ✗ mutação ✗ superfícies ausentes por design Prova com ORÁCULOS DETERMINÍSTICOS sobre a estrutura — nunca sobre a prosa do maker. "Mesma evidência entra ⇒ mesmo veredito sai." É o que o diferencia de mais uma opinião.
Em uma frase: o Verifier não dá um palpite — ele confere os autos. Pega o veredito pronto e checa, com regras fixas, se ele é sólido: tem gente suficiente votando? O voto bate com a nota? Como as regras são determinísticas, rodar o Verifier dez vezes dá o mesmo resultado dez vezes — diferente de perguntar a um modelo, que poderia mudar de ideia.
No tipo: VerifierEvidence = { consensus, votes, pack } (tudo readonly); cada claim tem um ClaimOracle = (evidence) => { proven, evidence } — uma função PURA, sem I/O e sem modelo, que lê só a estrutura. verifyDecision (verifier.ts:216-251) decompõe a decisão em claims, roda cada oráculo, e dobra os resultados num VerificationReport com veredito verified / needs-review / rejected.

10 · As cinco claims atômicas — duras e moles

O Verifier decompõe uma decisão em cinco claims atômicas, cada uma provada por um oráculo. Três são duras (falhou → rejected, a decisão é insustentável na cara); duas são moles (sinais consultivos — se não provam, no máximo levam a needs-review):

decomposeClaims() · 5 oráculos sobre a evidência — vermelho = duro (veta), oliva = mole (consultivo)
DUROquorum — validVoteCount ≥ 3 DUROverdict-matches-score DUROvotes-self-consistent molenot-fail-closed moleconsensus-spread ≤ 3 verdictFromProofs dura falhou → rejected todas provadas → verified senão → needs-review Os oráculos leem só a estrutura: validVoteCount, aggregateScore, spread, e a auto-consistência nota↔voto de cada CouncilVote. Nunca a prosa. (verifier.ts:119-195)
"votes-self-consistent" é a captura mais elegante. Cada voto deve concordar com a própria nota (≥7 → GO, ≥5 → PIVOT, senão NO_GO). Como buildVote já deriva nota e voto do mesmo agregado, isso normalmente passa de graça — mas a claim existe para pegar um voto adulterado ou malformado que tentasse dizer "nota 3, mas voto GO". O oráculo é o guardião de uma invariante que o construtor já tenta garantir.

11 · escalate-after-N — não há retry infinito

E quando a verificação não converge — quando uma decisão é re-litigada vez após vez? O Verifier não tenta para sempre. Acima de MAX_LOOPS = 3 (loopCount > 3), ele para de re-decidir e estaciona o trabalho para T4 (revisão humana), via escalateTier(Tier.T3):

FLUXOGRAMA · re-litigar até MAX_LOOPS; passou disso, estaciona em T4 (não um loop infinito)
verifyDecision loopCount atual loopCount > 3 ? NÃO → re-decide (volta) SIM estaciona: parkedTier = T4 verdict forçado a needs-review escalateTier(Tier.T3) → T4 A emissão só é aprovada com verdict 'verified' E sem parkedTier (isEmissionApproved) — um trabalho estacionado nunca emite sozinho.

É o mesmo padrão escalate-after-N que o board declara em workflowRulesSchema.escalateAfter (default 3, board.ts:101-103): uma decisão contestada sobe para humano em vez de queimar ciclos. A T4 é o estacionamento — de onde alembic propose/approve/reject retoma o trabalho parado.

12 · O painel N-lentes — o portão de emissão T3+

Para T3 e acima, a emissão tem portão por verifyPanel / isPanelEmissionApproved. Por que um painel, e não só o Verifier? Porque um verificador determinístico rodado N vezes dá o mesmo veredito N vezes — o fan-out só compra sinal se cada lente olhar por uma perspectiva diferente. São três:

LentePerguntaCheca
COHERENCEA decisão é internamente sólida?= verifyDecision reusado — quórum, verdict↔score, auto-consistência, spread
FAITHFULNESSEstá ancorada na evidência que recebeu?evidência presente (dura); confiança média ≥ 0.5; força de pico ≥ 3
DOMAINRespeita os invariantes de negócio?um GO precisa de sinal validation; evidência não é monocultura (≥ 2 tipos)

A agregação é a peça inteligente — quórum E um veto, ambos de propósito. O painel aprova por quórum (padrão DEFAULT_PANEL_QUORUM = 2 de 3) mas uma rejeição dura em qualquer lente única veta o painel inteiro (preservação-de-dissenso), e o escalate-after-N da coherence propaga:

GATE DE EMISSÃO T3+ · 3 lentes → (parked? veta? quórum?) → emite ✓ / rejeita ✗ / estaciona
COHERENCE= verifyDecision FAITHFULNESSevidência ancora? DOMAINGO tem validation? coherence parked? sim estaciona (T4) não alguma lente rejeitou duro? SIM ✗ rejeita (veto) não lentes verificadas ≥ quórum (2)? sim ✓ EMITE (verified) isPanelEmissionApproved não needs-review ordem dos cortes: 1) parked 2) veto 3) quórum (verifier-panel.ts :230-274)
Maioria para passar, qualquer-veto para bloquear
Uma maioria confiante de duas lentes ainda pode ser detida por uma lente que achou uma falha fatal (ex.: um GO sobre evidência zero, na FAITHFULNESS). Esta conjunção — quórum e ausência de veto — é deliberadamente mais difícil de passar que um voto simples. É exatamente o GO-verificado de que o funil (Lição 15) depende para deixar algo emitir em T3+.

13 · Maker vs checker — por que são coisas diferentes

A lição inteira gira em torno de uma separação: quem decide (o maker — o debate) não é quem prova (o checker — o Verifier/painel). Veja o contraste lado a lado:

MAKER (debate)
• Roda modelos (tem adapter).
• Produz votos, notas, prosa.
• Pode discordar, errar, alucinar.
• Não-determinístico (depende do modelo).
• Saída: DebateResult + ConsensusResult.
CHECKER (verifier/painel)
Não roda modelo (sem adapter).
• Prova claims com oráculos puros.
• Só lê estrutura, nunca a prosa.
Determinístico (mesma evidência → mesmo veredito).
• Saída: VerificationReport / aprovação de emissão.
MAKER × CHECKER · o mesmo veredito, dois papéis que NÃO podem ser a mesma coisa
MAKER usa adapter · roda modelos não-determinístico · pode errar decide "e se este for o palpite errado?" CHECKER sem adapter · oráculos puros determinístico · prova ou rejeita prova "a decisão satisfaz as invariantes?"
"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 claims com oráculos determinísticos sobre evidência estruturada, não perguntando a um modelo. É isso que o torna uma checagem, não mais uma opinião. Misturar os dois papéis é o erro que o padrão maker-checker existe para evitar.

14 · Como isso se encaixa

Você dissecou o kernel por dentro. Agora dê um passo atrás: onde ele liga no resto da máquina? O Council & Verifier não decide no vácuo — ele é o portão de emissão que o funil (Lição 15) atravessa quando uma decisão sobe para T3. À esquerda entra o contexto que alimenta o debate (o ContextPack em camadas); no meio roda esta peça — debate → pontuação → Verifier → painel N-lentes — opcionalmente espelhada pelo Validador coordenado; à direita só sai o que o painel emitiu (o GO-verificado), que segue para o swarm executar.

O PORTÃO DE EMISSÃO NA TUBULAÇÃO · contexto + tier T3 → ESTE kernel (debate→prova) → só o verificado emite · clique para percorrer
o que ALIMENTA ContextPack L0–L7 (Lição 27) decisão classificada T3 (Lição 15) contexto + tier ESTE kernel (Lição 18) debate (contrarian por último) pontuação 0–10 · quórum fail-closed Verifier read-only (5 claims) painel N-lentes = GATE de emissão T3+ espelho opcional: Validador coordenado (--coordinated) aditivo · só grava · nunca muda a decisão veta ✗ / estaciona T4 → re-litiga ou sobe pro humano GO verificado o que ELE ALIMENTA swarm executa (Lição 19) sob a pipeline de gates (Lição 17) Upstream = o contexto em camadas + o tier (Lições 27, 15). Esta peça (Lição 18) debate e PROVA. Downstream = a execução só do que emitiu. É a seta tracejada de volta (veto / T4) que torna o portão real — sem ela, "GO-verificado" seria só mais um carimbo.

Onde você está na metodologia: as Lições 14–17 montaram a cintura estreita, o funil, as invariantes e a pipeline de gates que executam uma run; esta Lição 18 é o cérebro do Council Gate dentro dessa pipeline — o componente que, com prova determinística, decide se uma decisão de T3+ pode emitir. A Lição 19 (o swarm) é quem pega o que emitiu e roda. Para ver as 30 peças no mesmo mapa e como cada uma liga na seguinte, abra o mapa interativo da metodologia.

As peças que este kernel conecta — cada link abre o mergulho na peça vizinha, e a frase diz por que elas se tocam:
  • Lição 15 · O funila esteira T0→T3 que desemboca aqui: quando uma ideia chega ao topo do funil (T3), é este painel N-lentes que decide se ela emite. O "GO-verificado" da Lição 15 é literalmente a saída desta peça.
  • Lição 17 · A pipeline de gateso Council Gate desta lição é um dos cinco portões dessa pipeline (Scope → Council → Proof → Validator → Publish); aqui você vê o Council Gate por dentro, lá você vê a ordem dos cinco e como o run os encadeia.
  • Lição 16 · As quatro invarianteso Verifier read-only é a encarnação do invariante ④ (maker ≠ checker) e a preservação-de-dissenso vem da ADR-0003 — as invariantes que esta peça impõe estruturalmente, não por prompt.
  • Lição 27 · Tiers, custo & orçamentoo tier (T3+) é o que liga o painel N-lentes: um trabalho de tier baixo nem aciona o gate. É a Lição 27 que define como uma decisão ganha seu tier e por que só T3+ paga o custo do fan-out de lentes.
  • Lição 19 · O swarmo destino do que emitiu: uma decisão verificada vira trabalho que o swarm distribui e executa. Esta peça é o "sim, pode" antes do swarm gastar um único token de execução.

15 · Na prática

Chega de diagrama — eis a peça como comandos reais. O Council Gate roda dentro de alembic run sem flag; o que você liga à mão é o Validador coordenado, o espelho multi-lente desta lição. Ele é opt-in (--coordinated), aditivo e nunca altera a decisão do council: roda a verificação risk-tier multi-lente sobre cada unit e grava o veredito em units/<id>/coordinated-verdict.json — offline e $0 por padrão.

# Roda a run e LIGA o passe do Validador coordenado (opt-in, aditivo).
# --yes pula a confirmação; offline = adapter determinístico, $0.
alembic run --goal GOAL.md --plan alembic.plan.ts --coordinated --yes

# … a run executa as units e passa os gates como sempre …
# para CADA unit, o passe coordenado imprime UMA linha de resumo:
#   coordinated validator [u1]: grade VERIFIED · verdict pass · tier full · 6 lens(es) · 0 finding(s)
# e grava o veredito completo (findings, lentes, grade) em:
#   .alembic/runs/<run-id>/units/u1/coordinated-verdict.json
# (evento de log por falha não-fatal: run:coordinated-pass-error)

Fonte da forma exata: apps/cli/src/index.ts:118 (a flag --coordinated) e apps/cli/src/commands.ts:921-953 (runCoordinatedPassForSpec) — o passe seleciona as units com proof ou de tier T4, roda runCoordinatedValidatorGate por unit, imprime coordinated validator [<id>]: <note> e persiste o JSON; ele nunca falha a run (o Council Gate continua sendo o único portão de emissão). A linha <note> é montada em packages/coda/src/coordinated-validator.ts:521-529.

E para ver o contexto em camadas que o council recebe antes de debater — o mesmo ContextPack L0–L7 que entra no kernel — monte um pack à mão com alembic context-pack:

# Monta o ContextPack de 8 camadas (L0–L7) sobre arquivos reais.
alembic context-pack packages/council/src/verifier.ts --brief "como o Verifier prova uma decisão"

# saída (cabeçalho + as camadas + o orçamento de tokens):
#   context-pack: pack-… (role default, model …)
#     manifest: man-… (1 file(s))
#     layers: L0 brief 35 chars | L1 repoMap … | L2 files 1 | L3 snippets … |
#             L4 summaries … | L5 debate … | L6 artifacts …/constraints … | L7 manifest 1
#     budget: … estimated / … max (within budget)

Fonte: apps/cli/src/commands.ts:3356-3423 (runContextPack) — imprime context-pack: <id> (role <role>, model <model>), uma linha de manifest:, a linha de layers: L0–L7 e a de budget: com within budget ou OVER BUDGET; --json emite o pack inteiro. Atenção à camada: este é o pack que alimenta o debate (o upstream do diagrama acima) — não o veredito que sai dele.

EXPERIMENTE · veja o Validador coordenado gravar um veredito por unit (offline, $0)
  1. Clone e entre no repo, depois garanta o build verde:
    git clone <repo> alembic && cd alembic · pnpm -r typecheck && pnpm -r build && pnpm -w test
  2. Gere um escopo mínimo (cria GOAL.md + alembic.plan.ts + contrato):
    alembic plan "um passo trivial que só roda um teste"
  3. Rode com o passe coordenado:
    alembic run --goal GOAL.md --plan alembic.plan.ts --coordinated --yes
  4. O que procurar na saída: uma linha coordinated validator [<unit-id>]: grade … · verdict … · tier … · N lens(es) · M finding(s) por unit elegível (com proof ou T4).
  5. O que procurar no dir da run: abra .alembic/runs/<run-id>/units/<unit-id>/coordinated-verdict.json e leia o veredito completo (grade, verdict, riskTier, lensesRan, findings).
  6. Prove o "aditivo": rode a MESMA run sem --coordinated. Nenhuma linha coordinated validator aparece e nenhum coordinated-verdict.json é escrito — mas o resultado da run (o que o Council Gate decidiu) é idêntico. O passe só observa; nunca muda o veredito.
Faça na mão · qual comando para cada intenção?
1
"Quero um segundo parecer multi-lente sobre cada unit, sem mudar a decisão." → adicione --coordinated ao alembic run. (É o espelho aditivo desta lição — só grava.)
2
"Quero ver o contexto em camadas que o council debate."alembic context-pack <arquivos…> --brief "<objetivo>". É o upstream do diagrama (o que ALIMENTA).
3
"Quero o veredito coordenado como JSON, não como texto." → leia units/<id>/coordinated-verdict.json no dir da run (o passe sempre persiste o JSON além de imprimir a linha-resumo).
Agora você: você rodou alembic run … --coordinated --yes, uma das lentes do passe falhou, mas a run terminou verde e o council emitiu normalmente. Bug ou esperado? Decida antes de revelar.
Esperado. O passe coordenado é aditivo e não-fatal: uma lente que falha apenas marca o veredito como degraded (com um WARNING no note) e segue — o erro vira o evento de log run:coordinated-pass-error, nunca um throw. Por que assim: o único portão de emissão é o Council Gate (a peça desta lição); este espelho observa, ele não decide. Deixá-lo falhar a run daria a um observador o poder de um portão — exatamente o que o desenho recusa.

Fixe os conceitos (flashcards)

Clique pra virar. Tente lembrar a resposta antes de virar — recuperação ativa fixa mais que reler.

DebateEngine
Como "o contrarian fala por último" é garantido?
clique pra virar ↻
Resposta
Estruturalmente: fases seriais + a fase contrarian ordenada por último; contrarian_not_last é erro duro de board-load. Não é um prompt.
Pontuação
Por que o eixo risk não é invertido?
clique pra virar ↻
Resposta
risk 10 = baixo risco; soma na mesma direção dos outros. Não há 1 - risk — ele inverteria todo veredito.
Quórum
2 votos válidos. Qual o veredito?
clique pra virar ↻
Resposta
NO_GO fail-closed — abaixo de MIN_VALID_AGENTS = 3, antes de consultar qualquer nota.
Painel
Como o painel N-lentes decide emitir?
clique pra virar ↻
Resposta
Quórum (2 de 3) E sem veto: uma rejeição dura em qualquer lente bloqueia tudo (preservação-de-dissenso).

Revisão cumulativa — recupere de memória

Antes de clicar: responda de cabeça. As quatro opções têm tamanho parecido de propósito — sem pista pela forma.

1. Como "o contrarian fala por último" é garantido?
Correto: c. Membros dentro de uma fase são paralelos, mas as fases são seriais e a contrarian é a última, então as declarações anteriores já existem antes de seus inputs serem montados; o board loader rejeita um board onde ela não é a última. a confia num prompt — exatamente o que a arquitetura recusa. b inventa um truque de latência: a ordem vem das fases, não da velocidade do modelo. d ignora a serialidade entre fases (só os membros são paralelos).
2. Um board produz só 2 votos válidos (um membro falhou ao fazer bind). Qual o veredito?
Correto: b. O quórum é checado PRIMEIRO: menos de 3 votos válidos é NO_GO automático, com failClosed=true. a e c consultam a nota — mas a nota nem é olhada abaixo do quórum. d erra o invariante nunca-lança: a falha de bind virou um MemberFailureReason tipado (um voto a menos), não um throw.
3. Duas das três lentes do painel aprovam, mas a DOMAIN emite rejeição dura (um GO sem sinal de validation). O painel aprova a emissão?
Correto: d. O painel precisa de quórum E nenhum veto duro; o veto é checado antes do quórum. a e b param no quórum e esquecem o veto. c erra a direção: o painel é o gate justamente de T3+, não de T2. A preservação-de-dissenso deixa uma lente que achou falha fatal bloquear a emissão mesmo com as outras passando.
4. Por que o Verifier é uma "checagem" e não "mais uma opinião"?
Correto: a. O Verifier não tem adapter nem superfície de mutação; só lê VerifierEvidence e prova claims com predicados puros — mesma evidência entra, mesmo veredito sai. b contradiz o design: ele NÃO roda modelo nenhum. c confunde-o com um membro do council — ele não vota nem tem peso. d inventa uma pré-condição de unanimidade que não existe; ele confere qualquer decisão, inclusive uma que falhou o quórum.
💬 Travou em algo? Eu sou seu professor neste curso — pergunte. "Por que o spread importa se a média já decide?", "O que é uma 'monocultura' de evidência na DOMAIN?", "Como o T4 estacionado volta a rodar?". É só dizer.