Curso / Lição 01
Lição 01 · O sistema que você está integrando

O motor

Antes de qualquer fusão, você precisa do hospedeiro. O Alembic é um motor de execução de planos para enxames de agentes: ele recebe um objetivo, um plano executável e um contrato de validação, então roda unidades de trabalho em vários modelos — roteando por custo, provando cada passo, com portão antes de entregar. Esta lição é a forma dele: seis camadas, um contrato que carrega o peso e nunca lança, quatro invariantes e o funil que transforma fontes brutas em Learnings.

Leia primeiro (fonte primária)
docs/alembic-complete-map.md — o mapa as-built do Alembic

Esta lição destila o mapa verificado na fonte (§1–§5) mais os arquivos que ele cita: packages/contracts/src/model.ts, packages/adapters/src/adapter-core.ts e packages/harness/src/funnel.ts. Por que importa pra missão: é o hospedeiro onde toda capacidade do Hermes vai ser clonada — você precisa da forma dele antes de fundir qualquer coisa.

Objetivos desta lição
  • Recitar as seis camadas (L-1 → L4) e por que o grafo de imports é acíclico e só aponta para baixo.
  • Explicar a cintura estreita: por que run() nunca lança e como runWithGuards impõe isso estruturalmente.
  • Enumerar as quatro invariantes (exatamente quatro) e o que sustenta cada uma na fonte.
  • Descrever o funil de 4 níveis (T0→T3) e por que uma emissão exige GO + aprovação do painel.
0
camadas (L-1 → L4)
0
invariantes — exatamente quatro
19+1
pacotes + apps/cli
~0
testes verdes

As contagens são verificadas na fonte. O mapa registra 19 pacotes de workspace + 1 app; a suíte estava em 415 quando o mapa foi escrito e cresceu para ~565 depois que @alembic/hermes chegou — os mesmos 565 que o estudo de caso da Lição 6 roda.

01 · O que é, de fato, o Alembic

Pense no Alembic como uma fábrica com esteira. Você entrega três coisas na entrada — o que fazer (o objetivo), o passo a passo (o plano executável) e como saber se ficou certo (o contrato de validação). A esteira então faz o trabalho em estações, sempre conferindo a qualidade antes de empacotar. As "máquinas" da esteira são modelos de IA; a graça é que você troca uma máquina cara por uma barata sem reescrever a esteira.

Analogia-âncora desta lição: o Alembic é a esteira; cada modelo é uma máquina plugável; a cintura estreita é o encaixe padrão que toda máquina usa; o funil é o controle de qualidade que decide o que sai da fábrica.

Você dá objetivo + plano + contrato; o Alembic roda os passos em vários modelos, escolhe o mais barato que dá conta, prova cada passo e só entrega depois de passar nos portões. Trocar de modelo não quebra a esteira.
Em termos do código: a entrada canônica é GOAL.md + alembic.plan.ts + um contrato de validação. O @alembic/vm executa o plano injetando os hooks h.*; o roteamento escolhe o modelo mais barato do tier pedido (pickCheapestForTier); cada unit.proof[] vira um comando que falha fechado; e os quatro gates de fechamento vivem em @alembic/coda.

02 · As seis camadas

O Alembic é estratificado de cima a baixo, e a estratificação é real: o grafo de imports não tem arestas para cima nem ciclos. Cada camada só pode depender das que estão abaixo. Leia de baixo — a fundação é o vocabulário; o topo é onde humanos e ferramentas se conectam.

AS SEIS CAMADAS · de L-1 SOURCE (fundação) a L4 CLIENTS (topo) — dependências só apontam para baixo
L4 · CLIENTS cli · http (REST+SSE) · mcp (somente-leitura) · web cockpit · tui L3 · SWARM orchestrator → lead → worker · fila com dependências · profundidade limitada (MAX_DEPTH=2) · park T4 L2 · ENGINE DebateEngine + score 0–10 + Verifier maker-checker + painel de N-lentes (o portão de emissão) L1 · ADAPTER — a cintura estreita ModelAdapter.run(input) → ModelRunResult ⟵ NUNCA LANÇA L0 · SUBSTRATE vocabulário Zod · dedupe SHA-256 · stores append-only endereçados por conteúdo · budget · run-dirs L-1 · SOURCE Collector + agent-browser (somente-leitura) → materializa pacotes wiki append-only dependências apontam só para baixo — sem aresta para cima, sem ciclo
CamadaO que possuiPacotes
L4 · CLIENTSAs superfícies que humanos e ferramentas usam: CLI, o servidor harness HTTP+SSE, um servidor MCP somente-leitura, o web cockpit, a TUI.harness, web, tui, apps/cli
L3 · SWARMOrquestração multi-nível: um orchestrator gera um lead que gera workers, sobre uma fila com dependências, com profundidade limitada, isolamento git-worktree e resume à prova de crash.swarm
L2 · ENGINEO kernel de decisão: um DebateEngine qualitativo, score quantitativo 0–10, um Verifier maker-checker independente, e um painel de N-lentes que é o portão de emissão T3+.council
L1 · ADAPTERA cintura estreita — toda chamada de modelo é uma forma de função que nunca lança. Seis adapters + offline + um roteador sem fallback silencioso, retry, circuit-breaker, contabilidade de custo.adapters
L0 · SUBSTRATEO piso determinístico de $0: o vocabulário Zod (todo tipo é um z.infer), o leitor de corpus em streaming, dedupe SHA-256, stores JSONL append-only endereçados por conteúdo, redação de PII, o guarda de budget, run directories, o registro de modelos.contracts (vocabulário), etl (a camada determinística)
L-1 · SOURCEA camada de ingestão que alimenta a wiki que o ETL depois destila — um Collector somente-leitura + um wrapper agent-browser que só pode navegar, nunca mutar.ingestion

Cola que atravessa as camadas. Um punhado de pacotes orquestra através de L2–L4 e é melhor lido como seu próprio nível: @alembic/mission (compila missões → run specs), @alembic/vm (executa alembic.plan.ts injetando os hooks h.*), @alembic/coda (os quatro portões de fechamento de run), @alembic/forge (o front-end Forge + Scope Gate) e @alembic/planf3 (HTML de plano).

03 · Por que o grafo é acíclico (e por que isso importa)

"Sem aresta para cima, sem ciclo" soa abstrato — mas é o que deixa cada camada testável e substituível em isolamento. Como L0 não conhece L1, e L1 não conhece L2, você pode trocar um adapter inteiro sem tocar no engine, e testar o engine sem nenhuma rede. Compare as duas formas:

✓ ACÍCLICO (Alembic) · setas só descem
L2 · engine L1 · adapter L0 · substrate troque L1 sem tocar em L0/L2
✗ COM CICLO (o que evitamos)
engine adapter substrate a seta de volta acopla tudo

Por isso a invariante 2 (engines agnósticos de adapter e de store) é estrutural, não um conselho: o grafo acíclico é o que torna a injeção possível. Guarde isto para a seção 06.

04 · A cintura estreita — um contrato que nunca lança

Eis a ideia mais importante do código. Toda invocação de modelo no sistema inteiro flui por uma forma de função. A analogia: é a tomada padrão da casa. Não importa se o aparelho é uma frontier cara ou um modelo local grátis — todos têm o mesmo plugue, então toda a fiação acima é igual. Muitos chamadores em cima, muitos provedores embaixo, e um encaixe único no meio:

A CINTURA ESTREITA (ampulheta) · muitos chamadores → 1 forma → muitos provedores
CHAMADORES (toda camada acima de L1) engine (L2) swarm (L3) funil cli (L4) ModelAdapter.run(input) → ModelRunResult a forma única — NUNCA lança PROVEDORES (6 adapters + offline, atrás de um roteador) cliproxyapi localCli …+ outros offline ($0) um modelo ausente retorna erro tipado — nunca um substituto silencioso

Sucesso e falha são ambos valores de retorno comuns — não há um segundo caminho excepcional para raciocinar. O resultado é uma união discriminada chaveada em ok: se ok:true, você lê o texto; se ok:false, você lê um erro tipado. Mesma forma, dois ramos:

ModelRunResult · uma união discriminada em ok — o consumidor só ramifica num if
run(input) retorna ModelRunResult ok ? true ModelRunSuccess text · usage · costUsd false ModelRunFailure error {code,message,retryable} um 429, um timeout, uma resposta malformada → todos chegam pelo ramo false, com a MESMA forma z.discriminatedUnion('ok', [ Success, Failure ])
// packages/contracts/src/model.ts — a cintura (forma)
interface ModelAdapter {
  run(input: ModelRunInput): Promise<ModelRunResult>;   // NUNCA lança (a invariante)
}
// ModelRunResult é uma união discriminada em `ok`:
ModelRunSuccess = { ok: true;  text; usage?; costUsd?; durationMs; modelId; ... }
ModelRunFailure = { ok: false; error: { code; message; retryable }; durationMs; ... }
ModelRunResult  = z.discriminatedUnion('ok', [ Success, Failure ])

Fonte: packages/contracts/src/model.ts:30-151. Governado pela ADR-0009 ("cintura estreita — run nunca lança").

Preveja antes de continuar
Um provedor cai no meio de uma run e a chamada HTTP estoura uma exceção lá dentro do adapter. O que o engine (L2), uma camada acima, vê chegar?
Um ModelRunResult com ok:false — a mesma forma de sempre. O throw nunca sobe: ele é capturado lá embaixo e convertido em um ModelRunFailure tipado, com error.retryable setado. Se você chutou "uma exceção que o engine precisa capturar", caiu na armadilha clássica — o ponto inteiro da cintura é que não existe caminho de exceção acima de L1. O engine só faz if (!res.ok).
Por que uma cintura importa

Porque a forma é uniforme, toda camada acima de L1 pode ser escrita como um kernel puro que só ramifica em ok. Um 429, um timeout, uma resposta malformada, uma queda de provedor — todos chegam como a mesma falha tipada. Não há try/catch espalhado pelo engine, pelo swarm ou pelo funil. O núcleo de orquestração até re-estabelece a fronteira para sub-runs inteiras com runDebateSafe / runSwarmSafe. Um contrato, imposto uma vez, comprado em todo lugar.

Duas uniões irmãs: a de modelo e a de IO

Existe uma segunda união, mais leve, para trabalho falível que não é chamada de modelo — IO de arquivo, parsing, encapsular subprocessos. Ela é o Result<T, E> e espelha de propósito a cintura de modelo, para que ambas leiam idêntico nos call sites. Veja-as lado a lado:

ModelRunResult vs Result<T,E> · mesmo formato, dois domínios (ambos chaveados em ok)
ModelRunResult (chamada de modelo) { ok: true, text, usage, costUsd } { ok: false, error{code,message,retryable} } L1 adapters · governa ADR-0009 Result<T,E> (IO / parsing) { ok: true, value: T } { ok: false, error: E } @alembic/contracts · fail-closed Mesmo discriminante (ok) → o call site lê idêntico nos dois. Essa disciplina é a Lição 5.

05 · runWithGuards — a espinha que impõe "nunca lança"

"Nunca lança" não é um comentário que você torce para valer — é estruturalmente imposto por uma única espinha reusável, runWithGuards. Cada adapter implementa só um attempt() interno; a espinha o encapsula, em ordem, com quatro etapas. Pense numa linha de montagem com guarda-corpos: a entrada é validada, o miolo roda dentro de uma rede, e a saída sempre tem a forma certa.

runWithGuards · 4 etapas em ordem + uma rede final — o throw nunca escapa
try/catch externo — última rede de segurança p/ preservar a invariante 1 · valida Zod entrada na fronteira 2 · try/catch throw → ModelRun- Failure tipado 3 · circuit-breaker portão opcional (abre se falha demais) 4 · retry backoff dirigido pelo flag retryable de cada result o adapter só escreve attempt() — as 4 guardas + a rede vêm de graça, iguais para todos

Fonte: packages/adapters/src/adapter-core.tsrunWithGuards (linhas 118–147), a espinha estrutural nunca-lança. adapter-core.ts:118.

A diferença que isso faz: erro espalhado vs erro num lugar só

Compare as duas arquiteturas de erro. Sem a cintura, cada consumidor precisa do seu próprio try/catch (e um esquecido vira um crash). Com a cintura, o try/catch vive uma vez, e todo o resto só lê um valor:

AspectoSem cintura (throws espalhados)Com cintura (runWithGuards)
Onde mora o try/catchEm todo call site (N lugares)Num lugar só (a espinha)
O que o consumidor faztry { run() } catch(e) { … }if (!res.ok) { … }
Um handler esquecidoVira crash não tratadoImpossível — a falha é só um valor
429 / timeoutCada um lida do seu jeitoForma única, retryable padronizado
Modelo ausenteRisco de fallback silenciosoErro tipado, sem substituto

06 · As quatro invariantes — exatamente quatro

A arquitetura se apoia em quatro propriedades (não mais — o mapa enumera exatamente estas). Cada uma é afirmada na fonte, e a maioria é governada por uma ADR. Visualize-as como quatro pilares:

AS QUATRO INVARIANTES · cada quadrante é uma propriedade afirmada na fonte
1 · run() nunca lança união discriminada uniforme em ok estrutural via runWithGuards ADR-0009 2 · agnóstico de adapter E store kernels puros, side-effects injetados AdapterRegistry + FsPort injetados offline → run $0 3 · endereçado por conteúdo id = SHA-256 do spec; run-dir determin. re-anexar idêntico = no-op (replay) sem Date.now()/Math.random() no plano 4 · dissidência forçada Verifier maker-checker, somente-leitura oráculos sobre evidência, não prosa ADR-0003 · "contrarian-last" = erro
#InvarianteComo é mantida
1run() nunca lança; o resultado é uma união discriminada uniforme.Estrutural, via runWithGuards (adapters/src/adapter-core.ts:118). ADR-0009.
2Engines são agnósticos de adapter E de store — kernels puros com side-effects injetados.O DebateEngine recebe views readonly + um AdapterRegistry injetado; o ETL roteia todo IO por um FsPort injetável; o funil recebe um registry injetado (então um offline torna a run $0).
3IDs endereçados por conteúdo + layout determinístico de run-dir (para runs replicarem).O id de uma run é o hash de conteúdo SHA-256 do spec; stores são endereçados por conteúdo sobre JSON canônico, então re-anexar conteúdo idêntico é no-op. Módulos de plano não podem usar Date.now()/Math.random().
4Dissidência é preservada/forçada pelo Verifier, não apenas por um prompt.O Verifier maker-checker é somente-leitura por arquitetura e prova claims com oráculos determinísticos sobre evidência estruturada, nunca a prosa do maker. "Contrarian-last" é um erro rígido de carga do board. ADR-0003.
Por que "exatamente quatro" vale dizer. Um rascunho anterior deste curso alegava seis invariantes. O mapa verificado na fonte enumera quatro. A disciplina do projeto inteiro é contar o que está no código, não o que soa redondo — então: quatro.

Invariante 3 de perto: por que o mesmo corpus replica

O endereçamento por conteúdo é o que torna uma run replicável. O id é o SHA-256 do spec; os stores guardam por hash do conteúdo canônico. Então rodar de novo um corpus inalterado não cria nada novo — só bate nos mesmos hashes e não toca delta nenhum:

1ª run · grava conteúdo novo
spec SHA-256 = id grava ✓ conteúdo inédito → vira um delta novo no store append-only append-only = só acrescenta, nunca sobrescreve
Replay · mesmo spec → no-op
mesmo spec MESMO id no-op o hash já existe → nada é regravado (só "duplicata") por isso plano não pode usar Date.now()/Math.random()

07 · O funil — uma destilação de 4 níveis

A razão de existir do Alembic é o funil: ele transforma um corpus bruto em duas cadeias de valor — um grafo de oportunidades de negócio e um store de Learnings. Faz isso em quatro níveis de custo, barato-primeiro, para que a maior parte do trabalho custe nada e só os sinais mais fortes cheguem a um modelo pago. A imagem mental: uma peneira de quatro malhas — a primeira deixa passar tudo de graça; cada malha seguinte é mais fina, mais cara, e segura menos.

O FUNIL · T0 grátis sobre 100% do corpus → T3 medido só nos sobreviventes
T0 · walk determinístico → dedupe SHA-256 → valida contrato → score 6-dim $0 · 100% do corpus T1 · um BusinessSignal por item de resíduo (adapter LOCAL) ~$0 · local T2 · shortlist FRONTIER com portão de budget refina os mais fortes medido T3 · council + verifier N-lentes → verified-GO medido
NívelO que fazCusto
T0runT0Pipeline determinístico: percorre o corpus → dedupe SHA-256 → valida contrato → score 6-dim → emite resíduo. Roda sobre 100% do corpus.$0
T1runT1Extraction: um BusinessSignal por item de resíduo via o adapter LOCAL injetado (free-tier, então na prática nunca bloqueado por budget).~$0
T2runT2Shortlist: um shortlist FRONTIER com portão de budget refina os sinais T1 mais fortes em lotes; toda chamada paga é medida.medido
T3runT3Council: um council sintético de 3 membros (otimista / analista / pessimista) mais o painel verifier de N-lentes.medido

Fonte: packages/harness/src/funnel.ts — os quatro níveis (linhas 55–81) + verified-GO (linhas 496–514).

Por que barato-primeiro: o custo desaba a cada malha

A peneira só faz sentido porque poucos itens chegam às malhas caras. T0 vê 100% do corpus, mas a $0. Quando você finalmente gasta com uma frontier (T2/T3), está pagando por um punhado de sobreviventes. Veja a queda do volume e a entrada do custo:

VOLUME POR NÍVEL · barras encolhem (menos itens) enquanto o custo entra só no fim
T0 100% do corpus · $0 T1 resíduo (menos itens) · ~$0 local T2 shortlist · medido T3 sobreviventes · medido Largura ≈ quantos itens chegam ao nível. Dinheiro só entra onde a barra já é curta — por isso o gasto é mínimo.

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

Antes de mexer no slider, sinta a queda na mão, devagar. Suponha um corpus de 10.000 itens e uma sobrevivência didática de 20% a cada malha. Recuperar o procedimento (não só ver o número pronto) é o que fixa de verdade. [uncertain] os 20% são um modelo de ensino — o funil real prioriza por score 6-dim, não por taxa fixa.

Exemplo resolvido · quantos itens tocam um modelo PAGO num corpus de 10.000?
1
T0 vê tudo, a $0. O runT0Pipeline determinístico roda sobre 100% do corpus: 10.000 itens, custo $0. Nada de modelo aqui — só walk + dedupe SHA-256 + contrato + score.
2
T1 extrai dos sobreviventes (local, ~$0). 20% passam adiante: 10.000 × 0,20 = 2.000 itens viram um BusinessSignal cada, via o adapter LOCAL. Ainda praticamente de graça.
3
T2 é o primeiro nível PAGO. 20% dos 2.000 cruzam o portão de budget: 2.000 × 0,20 = 400 itens vão para o shortlist FRONTIER — aqui começa o dinheiro (medido).
4
T3 é o council + verifier. 20% dos 400 chegam ao topo: 400 × 0,20 = 80 itens passam por council + painel de N-lentes.
5
Veredito de custo. De 10.000 itens, só ~400 (os de T2) tocaram um modelo pago — 400 / 10.000 = 4% do corpus. Os outros 96% foram filtrados de graça. É essa a economia do funil.
Agora você: com a mesma taxa de 20%, quantos itens chegariam a T3 num corpus de 40.000? Faça antes de revelar.
40.000 × 0,20 × 0,20 × 0,20 = 320 itens chegam a T3 (três malhas de 20%). Dica: cada malha multiplica por 0,20, então T3 ≈ corpus × 0,008 — menos de 1% do corpus enfrenta o nível mais caro. O slider abaixo faz exatamente essa conta em tempo real.

Calcule você: a peneira em ação

Arraste o tamanho do corpus e a "taxa de sobrevivência" entre malhas. Repare: T0 é sempre $0 e roda em tudo; só os poucos que chegam a T2/T3 custam dinheiro. [uncertain] os percentuais de sobrevivência são um modelo didático ajustável — o funil real prioriza por score, não por uma taxa fixa.

chegam a T3 (caro)
80
% do corpus que toca modelo pago
3,2%
itens processados a $0 (T0)
10000
O sinal verified-GO — a barra de qualidade do funil

Um resultado T3 emite quando ambos a decisão de consenso é GO e isPanelEmissionApproved(report) vale — o painel de N-lentes verificou, não estacionou. Maioria simples não basta; o painel pode vetar. É isso que mantém o grafo de oportunidades honesto: nada sedimenta sem passar o portão de emissão.

PORTÃO DE EMISSÃO · é um E lógico — basta um lado falhar e nada sedimenta
consenso = GO isPanelEmissionApproved E ✓ EMITE ✗ qualquer um falso → estaciona (T4) maioria simples NÃO basta — o painel pode vetar

Três invariantes de segurança que o funil nunca deve regredir: PII antes da saída (um sinal de canal privado é redatado antes da chamada de modelo e re-checado antes de qualquer escrita), budget fail-closed (toda chamada paga é encapsulada num BudgetGuard.check fail-closed — uma quebra projetada bloqueia a chamada e o nível degrada em vez de gastar demais), e append-only (resultados fluem para stores endereçados por conteúdo, validados por schema, append-only; leituras de fonte permanecem somente-leitura).

TRÊS TRILHOS DE SEGURANÇA · cada um falha fechado (negar é o default)
PII antes da saída redata antes da chamada re-checa antes da escrita budget fail-closed BudgetGuard.check bloqueia quebra projetada → degrada o nível append-only stores endereçados por conteúdo fonte permanece somente-leitura ADR-0011 governa segurança/proveniência — nenhum dos três pode regredir

08 · Decisão: "o que esse sinal vira?" (fluxograma)

Junte o funil e o portão de emissão numa única decisão. Diante de um item do corpus, siga as setas — cada losango é uma pergunta que escolhe o caminho, de "passou no contrato?" até o veredito final "emite" ou "estaciona":

FLUXOGRAMA · do item bruto ao veredito (verified-GO) — siga as setas
item do corpus (T0) dedupe SHA-256 → score 6-dim passa no contrato Zod? NÃO descarta SIM T1: extrai BusinessSignal adapter LOCAL · ~$0 forte o bastante E budget OK? NÃO → fica no resíduo SIM T2/T3: refina frontier + council GO E painel aprovado? SIM ✓ EMITE NÃO ✗ estaciona (T4)
Por que o losango do budget (não "manda tudo para a frontier"): o teste antes de gastar é "forte o bastante E budget OK?". Quem pula esse losango e manda todo sinal para um modelo pago queima o orçamento em ruído — exatamente o que o BudgetGuard.check fail-closed impede.
O losango final é um E: não basta o council dizer GO; o painel de N-lentes precisa aprovar. Esse "E" é o que separa uma opinião de uma emissão — e é por isso que nada entra no grafo sem ser verificado.

09 · Confusões comuns

"Nunca-lança só significa que envolveram tudo em try/catch." Em parte — mas o ponto é que o try/catch vive num lugar só (runWithGuards), então a falha é convertida em valor tipado uma vez e todo consumidor a lê uniformemente. A vitória é a ausência de tratamento de erro em todo o resto, não a presença dele no adapter.
"Os níveis são sobre velocidade." São sobre custo e confiança. T0 é grátis e roda em tudo; cada nível acima é mais caro e vê itens menos numerosos e mais fortes. Quando você chega a um modelo de fronteira pago (T2/T3) está gastando dinheiro só em sinais que já sobreviveram a dois passos mais baratos.
"Determinismo é um nice-to-have." É carga estrutural: IDs endereçados por conteúdo sobre JSON canônico são o que torna uma run replicável — re-rodar um corpus inalterado produz só duplicatas e não toca deltas. É também por isso que módulos de plano não podem chamar Date.now() / Math.random(); a VM os rejeita.

Como isso se encaixa

Esta lição é o panorama da máquina inteira. As outras lições do módulo "Motor & método" abrem, uma a uma, as peças que você acabou de ver de cima: a cintura, o funil, as invariantes, os gates, o swarm. Aqui está o lugar de cada uma no fluxo de controle real — leia da esquerda (o que entra) para a direita (o que sai), com o motor inteiro destacado no meio:

ONDE O MOTOR ENTRA · fonte → o motor (esta lição) → emissão com portão — cada peça vira uma lição
fonte (L-1) ingestão → wiki corpus bruto O MOTOR (esta lição) — a esteira inteira L1 · cintura estreita — run() nunca lança (Lição 14) toda chamada de modelo passa por uma forma única L0 · funil T0→T3 — destila barato-primeiro (Lição 15) 100% do corpus a $0 → só sobreviventes pagam as quatro invariantes — o que o grafo sustenta (Lição 16) nunca-lança · injeção · endereço por conteúdo · dissidência L3 · swarm — orchestrator → lead → worker (Lição 19) fila com dependências · profundidade limitada · resume a pipeline de gates — fail-closed (Lição 17) Scope → Council → Proof → Validator → Publish emissão arestas de oport. + Learnings + entregas (ship) só com verified-GO dependências apontam só para baixo (seção 02) — nada sai sem passar pelo portão (seção 08)
Clique em "Percorrer" para acender uma etapa por vez — da fonte ao motor à emissão.

Cada caixa acesa acima é uma lição inteira. Estes são os encaixes diretos — siga o link e veja a peça por dentro:

Lição 14 · A cintura estreitaporque conecta: é o L1 deste motor de perto; a forma única run() que a seção 04 só apresentou, e que toda camada acima (engine, swarm, funil) compra de graça.
Lição 15 · O funilporque conecta: é a razão de existir do motor (seção 07) por dentro — os quatro níveis T0→T3, o portão de budget e o sinal verified-GO que decide o que sedimenta.
Lição 16 · As quatro invariantesporque conecta: aprofunda as exatamente-quatro propriedades da seção 06; é o grafo acíclico (seção 03) que as torna estruturais, não conselho.
Lição 17 · A pipeline de gatesporque conecta: é o portão de emissão (seção 08) generalizado para uma run inteira — Scope → Council → Proof → Validator → Publish, cada um fail-closed.
Lição 19 · O swarmporque conecta: é o L3 deste mapa (seção 02) por dentro — como o orchestrator gera lead e workers sobre a cintura estreita, com profundidade limitada e resume.

Onde você está na metodologia: esta é a visão de cima. As Lições 14–19 descem a um andar de cada vez nas peças desta esteira; as Lições 2–3 contam o que foi fundido aqui; o Módulo 4 (22–30) põe a mão na massa. Volte ao hub do curso para o mapa completo dos 30 passos, ou abra a galeria de blocos e demos. [uncertain] não existe um metodologia.html dedicado no repositório hoje — o index.html é o mapa-índice canônico da metodologia; se um mapa interativo separado for criado, aponte para ele aqui.

Na prática

Chega de diagrama — rode o motor. O caminho canônico tem dois comandos: você gera um escopo executável a partir de um prompt, depois roda esse escopo com os gates ligados. Primeiro, transforme uma ideia em GOAL.md + alembic.plan.ts + contrato:

# 1 · gera o escopo: plano HTML + GOAL.md + contrato + alembic.plan.ts
alembic plan "adiciona um comando `alembic hello` que imprime a versão"

# saída esperada (resumida):
#   ✓ GOAL.md            (objetivo + done-when mensurável)
#   ✓ alembic.plan.ts    (o plano executável — sem Date.now()/Math.random())
#   ✓ validation-contract.md
#   ✓ plan.html          (o plano legível)

Agora execute esse escopo. O run injeta os hooks h.* na VM, roteia cada unidade para o modelo mais barato do tier, roda os unit.proof[] e só fecha depois dos gates. O --yes dispensa a confirmação interativa:

# 2 · roda o escopo com os gates de fechamento
alembic run --goal GOAL.md --plan alembic.plan.ts --yes

# saída esperada (resumida):
#   run-id: 3f9a…  (SHA-256 do spec — endereçado por conteúdo)
#   ▸ Scope Gate     ✓   copia GOAL/plan/contrato para o run-dir
#   ▸ unit u1        ✓   roteada p/ o modelo mais barato do tier
#   ▸ Proof Gate     ✓   pnpm -r typecheck && pnpm -w test  (exit 0)
#   ▸ Validator Gate ✓   scrutiny independente por milestone
#   resultado em ~/.alembic/runs/3f9a…/  (events.jsonl · units/ · proof-results.jsonl)

Comandos canônicos do CLAUDE.md ("Forge scope execution" / "Run orchestration"). Um fetch failed aqui significa que o gateway cliproxyapi está fora do ar — rode tudo hermético com o sufixo --offline onde o comando o aceita (ex.: alembic distill <corpus> --offline, da Lição 15).

Experimente · do clone ao primeiro run-dir (5 passos)
1
Entre no repositório e prove o baseline. Rode pnpm -r typecheck && pnpm -r build && pnpm -w test a partir da raiz — é o mesmo Proof que o motor roda. Tem que ficar verde antes de qualquer coisa.
2
Confira o ambiente. Rode alembic doctor --client-stack — valida a forma do MODEL_REGISTRY e a coerência dos adapters, offline e a custo $0 (sem rede). É a checagem da cintura estreita (seção 04) antes de gastar qualquer token.
3
Gere um escopo. Rode alembic plan "<seu prompt>" e abra o GOAL.md gerado. Procure pelo done-when mensurável — sem ele, o Forge se recusa a compilar.
4
Rode o escopo. Rode alembic run --goal GOAL.md --plan alembic.plan.ts --yes e anote o run-id impresso — ele é o SHA-256 do spec (a invariante 3 da seção 06, ao vivo).
Inspecione o run-dir. Liste suas runs com alembic runs list e abra a pasta do run-id: procure events.jsonl (a trilha), units/<id>/proof-results.jsonl (cada proof que passou) e o registro dos gates. Re-rode o mesmo escopo e veja o id repetir — replay é no-op (seção 06).

Por que dois comandos e não um. plan só produz texto (um escopo reversível — nada foi feito ainda); run é o que executa atrás dos gates. Essa separação é o ADR-0005 da Lição 17 em forma de CLI: tudo que é reversível roda sozinho, e o portão humano fica no fim. [uncertain] o caminho exato do run-dir (~/.alembic/runs/<id>/) depende do --data-dir configurado; use alembic runs list para o caminho real da sua máquina.

Fixe os conceitos (flashcards)

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

Camadas
Quantas camadas, e em que ordem o grafo aponta?
clique pra virar ↻
Resposta
Seis (L-1 → L4). O grafo de imports é acíclico e só aponta para baixo — cada camada só depende das de baixo.
Cintura
O que run() retorna num 429?
clique pra virar ↻
Resposta
Um ModelRunResult com ok:false e error.retryable — nunca lança. runWithGuards converte qualquer throw nessa forma.
Invariantes
Quantas invariantes, e o que torna engines testáveis?
clique pra virar ↻
Resposta
Exatamente quatro. Engines são agnósticos de adapter e store: recebem AdapterRegistry + FsPort por injeção — um registry offline roda o funil a $0.
Funil
O que um T3 precisa para emitir?
clique pra virar ↻
Resposta
Os DOIS: consenso GO E isPanelEmissionApproved(report). Maioria simples não basta; o painel pode vetar.

Revisão cumulativa — recupere de memória

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

1. Um provedor retorna HTTP 429 (rate-limited) no meio de uma run. Como isso chega ao engine acima de L1?
Correto: c. A cintura estreita significa que run() nunca lança; um 429 vem como um ModelRunFailure com error.retryable setado, e runWithGuards até dirige o retry pelo flag. a inverte a invariante — não existe caminho de exceção acima de L1; o engine só faz if (!res.ok). b confunde uma falha de modelo (um valor) com um crash de processo: a run não aborta por um 429, ela trata. d descreve um fallback silencioso que o roteamento proíbe — um modelo ausente retorna erro tipado, nunca um substituto.
2. Quantas invariantes arquiteturais o mapa enumera, e o que impõe "engines são agnósticos de store"?
Correto: b. Exatamente quatro invariantes, e o agnosticismo de store/adapter é estrutural: os kernels recebem seus side-effects por injeção em vez de importá-los, o que os torna testáveis em memória e deixa um adapter offline rodar o funil hermeticamente de graça. a e d erram a contagem (seis) — o rascunho antigo dizia seis, mas o mapa conta quatro. c acerta o número mas erra o mecanismo: não é uma asserção em runtime, é o grafo acíclico que permite a injeção (seção 03).
3. Por que o funil roda quatro níveis em vez de mandar tudo para um modelo de fronteira?
Correto: d. O funil é um filtro de custo: o piso determinístico T0 roda em tudo de graça, e T2/T3 com portão de budget gastam só nos sinais que já passaram passos mais baratos — e uma emissão exige GO + aprovação do painel. a é falso: a razão não é capacidade de leitura, é economia deliberada. b inverte a qualidade — T3 (council + verifier) é mais rigoroso que T0/T1, que só filtram barato. c confunde com velocidade: os níveis são sobre custo e confiança, não paralelismo.
💬 Travou em algo? Eu sou seu professor neste curso — pergunte. "Por que o id é o hash do spec e não um UUID?", "Como o offline adapter roda o funil a $0?", "O que acontece se dois itens diferentes colidirem no SHA-256?". É só dizer.