Lição 1 · Curso de Fusão · O motor
Alembic × Hermes · Curso de Fusão · Lição 1

O motor

Antes de qualquer fusão, você precisa do hospedeiro. O Alembic é um motor de execução de planos para enxames (swarms) de agentes: recebe um objetivo, um plano executável e um contrato de validação, e então roda unidades de trabalho em muitos modelos — roteando por custo, provando cada passo e passando por gates antes de entregar. Esta lição é a sua forma: seis camadas, um contrato que sustenta tudo, quatro invariantes e o funil que transforma fontes brutas em Learnings.

Cada seção tem um cartão Técnico opcional. Abra quando quiser o detalhe linha-a-linha.
Ao terminar esta lição, você consegue…
  • Desenhar as seis camadas do Alembic (L-1 → L4) e explicar por que o grafo de dependências só aponta para baixo.
  • Explicar a cintura estreita: por que toda chamada de modelo passa por run(), que nunca lança e devolve uma união discriminada em ok.
  • Dizer como runWithGuards torna o "nunca lança" estrutural, não uma promessa de comentário.
  • Enumerar as quatro invariantes (são exatamente quatro) e como cada uma é sustentada no código.
  • Descrever o funil de quatro tiers (T0–T3) e por que ele é um filtro de custo e confiança, não de velocidade.
  • Explicar o sinal verified-GO e por que GO sozinho não basta para emitir.
O que assumimos de você
  • Sabe ler um pouco de TypeScript — reconhecer um tipo, uma função, um interface.
  • Já ouviu a palavra hash como "uma impressão digital de um conteúdo". Se não — explicamos no caminho.
  • Nada mais. Os termos tier, gate, narrow waist (cintura estreita), adapter e residue são definidos aqui.
A fonte primária desta lição
docs/alembic-complete-map.md — o mapa fonte-de-verdade do repositório: §1 (as 6 camadas + a cintura estreita), §2 (as 4 invariantes), §3 (o funil de 4 tiers), §4 (o grafo acíclico de camadas). Tudo aqui é verificado contra o código.
6
camadas (L-1 → L4)
4
invariantes — exatamente quatro
27+1
pacotes + apps/cli
$0
custo do tier T0 (sempre)

Os números são verificados na fonte. O complete-map registrava 19 pacotes quando foi escrito; o workspace cresceu para 27 pacotes + apps/cli depois que pacotes como @alembic/hermes entraram. A disciplina do projeto é contar o que está no código, não o que soa redondo.

01

A grande ideia

Um motor que destila: barato primeiro, caro só no que sobrevive

O Alembic não é um chatbot. É uma fábrica: você entrega um objetivo e um plano, e ela executa o trabalho dividindo-o em unidades, distribuindo-as entre vários modelos de IA, provando cada passo e barrando o que não passa. Tudo é desenhado para gastar o mínimo: o trabalho mais pesado roda de graça, e só os sinais mais fortes chegam a um modelo pago.

Analogia. Pense numa peneira de garimpo em quatro malhas. A primeira malha é gigante e gratuita — passa toneladas de cascalho. A cada malha seguinte, o buraco aperta e o custo sobe, mas você está peneirando bem menos material — e só o que parece ouro chega à última, onde um especialista (caro) confirma. O Alembic é essa peneira para conhecimento e oportunidades.

A forma do sistema

O Alembic é um motor de execução de planos para enxames de agentes. A entrada é um trio: um GOAL.md (o objetivo), um alembic.plan.ts (o plano executável, com unidades e provas) e um contrato de validação. A saída é um run verificado, com cada unidade provada por comandos reais (o Proof Gate) e cada milestone escrutinado por um Validador independente.

A lição é a forma dessa máquina: seis camadas (uma pilha onde as dependências só apontam para baixo), uma cintura estreita (toda chamada de modelo tem a mesma forma e nunca lança), quatro invariantes (propriedades sustentadas no código) e um funil de quatro tiers (a destilação cheap-first).

Diagrama · a entrada e a saída do motor
GOAL.md (objetivo) alembic.plan.ts contrato de validação motor Alembic rota · prova · gate run verificado cada passo provado
Três entradas, um motor, uma saída verificada. Tudo no meio existe para garantir que a saída é provada — não só produzida.
Guarde istoTier é o nível de custo/autonomia de um trabalho. T0 = silencioso, totalmente automático, $0. A escada vai de T0 até T4 (parked — exige council + humano). Cada degrau acima vê menos itens, mais fortes, e custa mais.
02

As seis camadas

Uma pilha onde as dependências só apontam para baixo

O Alembic é organizado em camadas de cima para baixo, e a organização é real: o grafo de imports não tem nenhuma aresta para cima e nenhum ciclo. Cada camada só pode depender das que estão abaixo. Leia de baixo para cima — a fundação é o vocabulário; o topo é onde humanos e ferramentas se conectam.

Preveja antes de ver

Numa pilha de seis camadas onde "tudo passa por uma chamada de modelo", em qual camada você acha que mora essa chamada — no topo (perto do humano), no meio, ou perto da base?

No meio-baixo: a camada L1 · ADAPTER, a cintura estreita. Tudo acima dela (engine, swarm, clients) chama modelos através dela; tudo abaixo (substrate, source) é determinístico e $0. É o ponto único por onde o sistema fala com qualquer provedor.
Infográfico vertical das seis camadas do Alembic empilhadas: no topo L4 CLIENTS (cli, http, mcp, cockpit, tui), depois L3 SWARM (orchestrator, lead, worker), L2 ENGINE (council, scoring, verifier), L1 ADAPTER destacada como a cintura estreita (run devolve ModelRunResult, nunca lança), L0 SUBSTRATE (Zod, SHA-256, stores append-only, budget) e na base L-1 SOURCE (collector somente-leitura). Uma seta vertical à direita indica que as dependências apontam só para baixo, sem ciclo.

As seis camadas, de L4 (clientes, no topo) a L-1 (ingestão, na base). A seta à direita é a regra inteira: dependências apontam só para baixo.

Diagrama · as seis camadas e a única direção permitida
L4 · CLIENTS cli · http (REST+SSE) · mcp (somente-leitura) · web cockpit · tui L3 · SWARM orchestrator → lead → worker · fila por dependências · MAX_DEPTH=2 · T4 park L2 · ENGINE DebateEngine + score 0–10 + Verifier maker-checker + painel de N-lentes (o gate 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 guard · run-dirs L-1 · SOURCE Collector + agent-browser (somente-leitura) → materializa wiki packages append-only dependências ↓ só para baixo sem aresta para cima · sem ciclo
Passe o mouse sobre uma faixa para destacá-la. As duas faixas com borda clay são as "chave": L4 (onde o humano entra) e L1 (a cintura estreita).
CamadaO que ela detémPacotes
L4 · CLIENTSAs superfícies que humanos e ferramentas usam: a 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-tier: um orchestrator gera um lead que gera workers, sobre uma fila gated por dependências, com profundidade limitada, isolamento por git-worktree e resume à prova de crash.swarm
L2 · ENGINEO núcleo de decisão: um DebateEngine qualitativo, score quantitativo 0–10, um Verifier maker-checker independente, e um painel de N-lentes que é o gate de emissão em T3+.council
L1 · ADAPTERA cintura estreita — toda chamada de modelo tem uma única forma de função que nunca lança. Adapters + offline + um router sem fallback silencioso, com retry, circuit-breaker e contabilidade de custo.adapters
L0 · SUBSTRATEO chão determinístico de $0: o vocabulário Zod (todo tipo é um z.infer), o leitor de corpus em stream, dedupe SHA-256, stores JSONL append-only endereçados por conteúdo, redação de PII, o budget guard, os run-dirs, 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 de agent-browser que só navega, nunca muta.ingestion

Cola que atravessa as camadas. Alguns pacotes orquestram através de L2–L4 e se leem melhor como um tier próprio: @alembic/mission (compila missões → run specs), @alembic/vm (executa o alembic.plan.ts injetando os hooks h.*), @alembic/coda (os quatro gates que fecham o run), @alembic/forge (o front-end Forge + Scope Gate) e @alembic/planf3 (o HTML do plano).

03

A cintura estreita

Um contrato que nunca lança

Aqui está a ideia mais importante do código. Toda chamada de modelo no sistema inteiro passa por uma única forma de função: uma chamada assíncrona que nunca lança e devolve uma união discriminada em ok. Sucesso e falha são ambos valores de retorno comuns — não existe um segundo caminho "excepcional" para você raciocinar.

Analogia. É como uma ampulheta. Todo o conhecimento do topo (engine, swarm, clients) tem de passar por um único gargalo estreito para chegar ao fundo (os provedores de modelo). Como o gargalo tem sempre o mesmo formato, qualquer coisa que passa por ele sai do outro lado igual — você nunca precisa de um tubo separado para "quando der erro".
Diagrama · a ampulheta (a cintura estreita)
engine swarm funnel / clients run(input) → ModelRunResult a única forma — NUNCA lança cliproxyapi offline (local) outros adapters
Muitos chamadores no topo, muitos provedores embaixo, uma forma de função no meio. Esse é o gargalo que torna o resto do sistema simples.

A forma exata

packages/contracts/src/model.ts — a cintura (forma)
// INVARIANTE: ModelAdapter.run NUNCA lança.
interface ModelAdapter {
  run(input: ModelRunInput): Promise<ModelRunResult>;   // nunca lança
}
// 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.tsmodelRunResultSchema = z.discriminatedUnion('ok', […]) e a interface ModelAdapter com a invariante "run NUNCA lança". Governado por ADR-0009 ("narrow waist — run never throws").

Por que uma cintura importa

Como 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 do provedor — tudo chega como a mesma falha tipada. Não há try/catch espalhado pela engine, pelo swarm ou pelo funil. Um contrato, aplicado uma vez, comprado em todo lugar.

Existe ainda uma segunda união, mais leve, Result<T, E> (também em ok, com braços value/error) para trabalho falível que não é de modelo — IO de arquivo, parsing, wrapping de subprocesso. Ela espelha de propósito a cintura de modelo, para os dois se lerem idênticos no ponto de chamada. Essa disciplina é a Lição 5.

Diagrama · as duas uniões espelhadas (ambas em ok)
Result<T, E> — IO, parsing, subprocesso ok: true · value ok: false · error ModelRunResult — chamadas de modelo ok: true · text ok: false · error ↑ a mesma forma — discriminada em `ok` — então leem idênticos no call site ↑ a diferença: ModelRunResult nunca lança (estrutural); Result é para trabalho falível comum
De propósito espelhadas: você lê ok do mesmo jeito nas duas. A cintura de modelo só acrescenta a garantia estrutural de "nunca lança".
Flashcard · recall
O que run() faz quando o provedor retorna HTTP 429?
clique para virar
Devolve um ModelRunResult com ok:false e error.retryable=truenão lança. A mesma forma de um sucesso. O retry é dirigido pela flag.
Flashcard · recall
Por que a engine não tem try/catch espalhado?
clique para virar
Porque o try/catch vive num único lugar (runWithGuards). A falha vira valor tipado uma vez, e todo consumidor a lê de forma uniforme.
Flashcard · recall
O que o router faz quando o modelo pedido não existe?
clique para virar
Devolve um erro tipadosem fallback silencioso. Ele nunca troca por outro modelo sem você saber.
04

runWithGuards

"Nunca lança" não é uma esperança — é estrutural

"Nunca lança" não é um comentário que você torce para ser verdade — é imposto estruturalmente por uma única espinha reutilizável, runWithGuards. Cada adapter implementa só um attempt() interno; a espinha o envolve, em ordem, com quatro proteções.

Diagrama · as quatro proteções de runWithGuards (em ordem)
1 · Zod valida input 2 · try/catch throw → falha tipada 3 · circuit-breaker gate opcional 4 · retry se retryable try/catch externo final — "última rede de segurança para preservar a invariante"
O attempt() do adapter (que pode lançar) é embrulhado por estas camadas. Qualquer throw que escapar é convertido em ModelRunFailure — a invariante sobrevive.
  1. Validação Zod do input no boundary — entrada malformada nunca chega ao provedor.
  2. try/catch que converte qualquer throw que escapou numa ModelRunFailure tipada.
  3. Um circuit-breaker opcional como gate.
  4. Retry com backoff, dirigido pela flag retryable de cada resultado — com um try/catch externo final "como última rede de segurança para preservar a invariante".
Papo técnico (pode pular)A engine também re-estabelece o boundary para sub-runs inteiros com runDebateSafe / runSwarmSafe — ou seja, não só uma chamada de modelo, mas um debate ou um swarm inteiro também devolvem um resultado em vez de lançar. Fonte: packages/adapters/src/adapter-core.tsrunWithGuards.
Cuidado · armadilha de iniciante"Nunca lança" não quer dizer "nada dá errado". Dá — o tempo todo (429, timeout, queda). A diferença é que o erro vira um valor que você lê, não uma exceção que estoura. Você ainda tem de tratar o ok:false.
05

As quatro invariantes

São exatamente quatro

A arquitetura repousa sobre quatro propriedades (não mais — o mapa enumera exatamente estas). Cada uma é afirmada na fonte, e a maioria é governada por uma ADR.

Diagrama · as quatro invariantes (e a ADR de cada)
1 · run() nunca lança união discriminada uniforme estrutural via runWithGuards · ADR-0009 2 · engines agnósticas de adapter E de store — kernels puros side-effects injetados (AdapterRegistry, FsPort) 3 · IDs por conteúdo + layout de run-dir determinístico SHA-256 sobre JSON canônico → replay 4 · dissenso preservado pelo Verifier, não por um prompt "contrarian-last" é erro de board · ADR-0003
Quatro cartões, quatro invariantes. Não cinco, não seis: a disciplina é contar o que está no código.
#InvarianteComo é sustentada
1run() nunca lança; o resultado é uma união discriminada uniforme.Estrutural, via runWithGuards (adapters/src/adapter-core.ts). ADR-0009.
2Engines são agnósticas 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 o run $0).
3IDs endereçados por conteúdo + layout de run-dir determinístico (para os runs darem replay).O id de um run é o hash SHA-256 do conteúdo da sua spec; os stores são endereçados por conteúdo sobre JSON canônico, então re-anexar conteúdo idêntico é no-op. Plan modules não podem usar Date.now()/Math.random().
4Dissenso é preservado/forçado pelo Verifier, não meramente 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 de board fail-closed. ADR-0003.
Diagrama · invariante 3 em ação — por que um run dá replay
conteúdo (JSON canônico) SHA-256 id estável (endereço) re-anexar conteúdo idêntico → mesmo id → no-op (não um duplicado) → o run dá replay por isso plan modulesnão usam Date.now()
Determinismo é load-bearing: o id é o conteúdo. Re-rodar um corpus inalterado não cria deltas — é o que torna um run replayable.
Por que vale dizer "exatamente quatro". 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.
06

O funil de quatro tiers

Uma destilação em quatro tiers, barato primeiro

A razão de o Alembic existir é 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 tiers de custo, cheap-first, de modo que a maior parte do trabalho não custa nada e só os sinais mais fortes chegam a um modelo pago.

Analogia. É um funil de RH para uma vaga muito disputada. A triagem automática (grátis) lê milhares de currículos. O teste técnico (barato) avalia centenas. A entrevista com o time (mais cara) vê dezenas. O painel final com a liderança (a mais cara) vê só os finalistas — e ainda pode vetar. Cada etapa custa mais e vê menos gente.
Infográfico de um funil de destilação em quatro tiers, do mais largo no topo ao mais estreito embaixo: T0 caminhada determinística com dedupe SHA-256, validar contrato e score de 6 eixos, custando zero sobre 100% do corpus; T1 um BusinessSignal por item via adapter local, quase zero; T2 shortlist FRONTIER com orçamento, medido (metered); T3 council mais verifier de N-lentes resultando em verified-GO, medido. Uma seta vertical indica menos itens, mais fortes e mais caros a cada tier.

O funil de quatro tiers. T0 (topo) é gratuito e roda sobre 100% do corpus; cada tier abaixo é medido e vê menos itens, mais fortes.

Diagrama · o funil, do corpus bruto ao verified-GO
T0 · caminhada determinística → dedupe SHA-256 → validar contrato → score de 6 eixos $0 · 100% do corpus T1 · um BusinessSignal por item de residue (adapter LOCAL) ~$0 · local T2 · shortlist FRONTIER com orçamento refina os mais fortes metered T3 · council + verifier de N-lentes → verified-GO metered menos itens, mais fortes, mais caros
Quanto mais fundo, mais estreito e mais caro. Quando você chega num modelo frontier pago (T2/T3), está gastando dinheiro só em sinais que já sobreviveram a dois passos mais baratos.
Diagrama · o score de 6 eixos do T0 (0–5 cada, determinístico)
chunksrefsQ&Acharsraw sourcemultimodal 05 (máx por eixo) 0–30 totalsem modelo · $0
Os seis eixos são pontuados por evidência estrutural (contagem de chunks, referências, perguntas-e-respostas, caracteres, fonte bruta, conteúdo multimodal) — determinístico, sem chamar nenhum modelo. As barras aqui são ilustrativas.

Explore tier a tier

Tier 0
Pipeline determinístico
runT0Pipeline: caminha o corpus → dedupe SHA-256 → valida contrato → score de 6 eixos → emite residue. Roda sobre 100% do corpus, sem humano, sem modelo.
Custo: $0
T0 · $0 · 100% do corpus T1 · ~$0 · local T2 · metered T3 · metered
TierO que fazCusto
T0runT0Pipeline: caminha o corpus → dedupe SHA-256 → valida contrato → score de 6 eixos → emite residue. Roda sobre 100% do corpus.$0
T1runT1Extraction: um BusinessSignal por item de residue via o adapter LOCAL injetado (free-tier, então na prática nunca é bloqueado por budget).~$0
T2runT2Shortlist: uma shortlist FRONTIER gated por orçamento refina os sinais T1 mais fortes em lotes; toda chamada paga é medida.metered
T3runT3Council: um council sintético de 3 membros (otimista / analista / pessimista) mais o painel verifier de N-lentes.metered

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

07

O sinal verified-GO

GO sozinho não basta

O sinal verified-GO — a barra de qualidade do funil

Um desfecho de T3 emite quando ambos são verdade: a decisão de consenso é GO e isPanelEmissionApproved(report) vale — o painel de N-lentes verificou, não parkou. Uma maioria simples não basta; o painel pode vetar. É isso que mantém o grafo de oportunidades honesto: nada sedimenta sem passar pelo gate de emissão.

Diagrama · o gate de emissão (E lógico, não OU)
consenso.decision === 'GO' ? isPanelEmissionApproved (report) ? E ambos verdadeiros → EMITE (verified-GO) qualquer um falso → PARKED
É um E lógico, não um OU. Fonte: packages/harness/src/funnel.tsif (!isPanelEmissionApproved(report) || consensus.decision !== 'GO') { … }.
Exemplo trabalhado · um sinal chega ao T3
1
Um BusinessSignal sobreviveu a T0 (passou o score), T1 (virou sinal local) e T2 (entrou na shortlist frontier).
2
O council de 3 membros debate. O consenso fecha em decision: 'GO'. Até aqui, parece aprovado.
3
O painel verifier de N-lentes roda isPanelEmissionApproved(report). Uma lente acha a evidência fraca e parka em vez de aprovar.
4
Resultado: GO === true, mas isPanelEmissionApproved === false. O Efalso → o sinal vira PARKED, não emite. O grafo continua honesto.
Agora você. E se o consenso fosse NO_GO mas todas as lentes aprovassem? Pense antes de seguir: o E ainda exige os dois verdadeiros — então também daria PARKED. Nenhum dos lados sozinho emite.
08

Confusões comuns

Três mal-entendidos que valem desfazer

"'Nunca lança' só quer dizer que embrulharam tudo em try/catch." Em parte — mas o ponto é que o try/catch vive num único lugar (runWithGuards), então a falha é convertida em valor tipado uma vez e todo consumidor a lê de forma uniforme. O ganho é a ausência de tratamento de erro em todo o resto, não a presença dele no adapter.
"Os tiers são sobre velocidade." São sobre custo e confiança. T0 é grátis e roda em tudo; cada tier acima é mais caro e vê menos itens, mais fortes. Quando você chega num modelo frontier pago (T2/T3), só gasta em sinais que já passaram por dois filtros mais baratos.
"Determinismo é um nice-to-have." É load-bearing: IDs endereçados por conteúdo sobre JSON canônico são o que torna um run replayable — re-rodar um corpus inalterado produz só duplicatas e não toca nenhum delta. É também por isso que plan modules não podem chamar Date.now() / Math.random(); a VM os rejeita.
09

Recapitulando

Cinco slides para fixar a forma do motor

Passe pelos slides com os botões, as setas do teclado (← →) ou os pontos. Cada um é um dos pilares desta lição.

A grande ideia

Um motor que destila

O Alembic recebe objetivo + plano + contrato e executa unidades em muitos modelos — roteando por custo, provando cada passo, passando por gates. Barato primeiro, caro só no que sobrevive.

T0 · $0
1

As seis camadas

Só para baixo

L4 clients · L3 swarm · L2 engine · L1 adapter · L0 substrate · L-1 source. O grafo de imports não tem aresta para cima nem ciclo.

L4 clients L3 swarm L2 engine L1 adapter L0 substrate
2

A cintura estreita

Uma forma, nunca lança

Toda chamada de modelo é run(input) → ModelRunResult, união discriminada em ok. Sucesso e falha são o mesmo tipo de retorno — sem caminho excepcional.

run() ok: true · text ok: false · error
3

As quatro invariantes

Exatamente quatro

1) run nunca lança · 2) engines agnósticas de adapter e store · 3) IDs por conteúdo → replay · 4) dissenso preservado pelo Verifier. Não cinco, não seis.

1 2 3 4
4

verified-GO

GO sozinho não basta

T3 só emite quando consenso é GO E isPanelEmissionApproved vale. O painel pode vetar — uma maioria simples não emite. É o que mantém o grafo honesto.

GO? painel aprovado? E → EMITE (verified-GO)
5
Slide 1 / 5 use
Recall rápido: quais são as duas condições para um sinal T3 emitir? (Resposta: consenso GO e aprovação do painel — o E lógico.)
10

Verifique

Três checagens — pontuadas

Revisão da Lição 1

Responda às três. O placar conta sozinho.

1. Um provedor retorna HTTP 429 (rate-limited) no meio de um run. Como isso chega à engine acima de L1?
Correto: c. A cintura estreita significa que run() nunca lança; um 429 aparece como ModelRunFailure com error.retryable. O runWithGuards converte qualquer throw que escape nessa forma e ainda dirige o retry pela flag. E o roteamento não tem fallback silencioso — um modelo ausente devolve erro tipado, nunca um substituto. a/b assumem exceção/abort; d é exatamente o que o router proíbe.
2. Quantas invariantes arquiteturais o mapa enumera, e o que garante que "engines são agnósticas de store"?
Correto: b. São exatamente quatro invariantes. A agnosticidade de store/adapter é estrutural: os kernels recebem seus side-effects por injeção em vez de importá-los, o que também os torna testáveis em memória e deixa um adapter registry offline rodar o funil inteiro hermético, de graça. a/d erram a contagem (seis); c inventa um mecanismo (asserção em toda função) que não é como é sustentado.
3. Por que o funil roda quatro tiers em vez de mandar tudo para um modelo frontier?
Correto: d. O funil é um filtro de custo. O chão determinístico de T0 é grátis e roda em tudo; T2/T3 gated por orçamento gastam dinheiro só em sinais que já passaram por passos mais baratos, e uma emissão de T3 exige tanto um consenso GO quanto aprovação do painel. a é falso; b inverte a qualidade (T3 é o mais rigoroso); c confunde o objetivo (é custo, não velocidade).
Acertos: 0/3
Os cinco fatos para levar desta lição
  1. O Alembic é um motor de execução de planos: objetivo + plano + contrato → run verificado.
  2. Seis camadas (L-1 → L4); o grafo de dependências só aponta para baixo, sem ciclo.
  3. A cintura estreita: toda chamada de modelo é run() → ModelRunResult, e nunca lança.
  4. São exatamente quatro invariantes — conte o que está no código, não o que soa redondo.
  5. O funil é um filtro de custo e confiança; T3 só emite com GO E aprovação do painel.
Ficou com dúvida em qualquer ponto — a cintura estreita, o gate de emissão, por que "exatamente quatro"? Pergunte ao seu agente (seu professor). A próxima lição faz o caminho inverso: o reverse-engineering do Hermes, o agente cujas capacidades vamos fundir aqui.