Lição 14 · Curso de Fusão · A cintura estreita — um run() que nunca lança
Alembic × Hermes · O Curso de Fusão · Parte 3 · A arquitetura

A cintura estreita: um run() que nunca lança

Toda invocação de modelo no Alembic — todo adapter, todo membro do council, todo worker do swarm, todo tier do funil — atravessa um único formato de função: uma chamada async que nunca lança e devolve um valor discriminado em ok. Este é o invariante mais importante da base de código. Os subsistemas @alembic/hermes que você viu nas lições 7–13 se conectam a esta cintura; entendê-la é entender por que uma rede instável ou um 429 não conseguem derrubar um run. Fonte: packages/contracts/src/model.ts + packages/adapters/src/adapter-core.ts, governado pela ADR-0009.

Leia primeiro (fonte primária)
packages/contracts/src/model.ts — a união ModelRunResult + a interface ModelAdapter ("NEVER throws"), ancorada no spine packages/adapters/src/adapter-core.ts

Tudo nesta página é citado de arquivo real do monorepo. O conceito em torno do qual a camada inteira gira é a cintura estreita (the narrow waist): o tipo de retorno é uma união discriminada e o spine runWithGuards é o que torna o "nunca lança" verdadeiro de verdade — não um comentário esperançoso. Nada aqui é inventado.

Leia a versão simples, ou abra a camada técnica em qualquer seção.
O que você vai conseguir fazer
  • Explicar o que é a cintura estreita e por que toda chamada de modelo passa por ModelAdapter.run.
  • Ler a união discriminada ModelRunResult e dizer por que falha é um valor, não uma exceção.
  • Seguir as quatro guardas em ordem de runWithGuards: Zod no boundary → guardedAttemptwithRetry → rede de segurança externa.
  • Justificar por que o ModelRunFailure carrega um retryable: boolean classificado na fonte.
  • Distinguir ModelRunResult do irmão leve Result<T, E> e dizer por que ambos compartilham o discriminante ok.
Suposições tolas (o que presumimos de você — bem pouco)
  • Você já passou pelas lições 7–13 e viu cada subsistema @alembic/hermes devolver um Result.
  • Você sabe ler pseudocódigo simples. Não precisa dominar TypeScript — traduzimos cada trecho.
  • Você não precisa saber o que é união discriminada, circuit breaker ou backoff. A gente constrói esses termos aqui.
1

A grande ideia


Imagine um prédio inteiro com uma só porta giratória. Cada pessoa que entra ou sai — não importa de qual sala, andar ou setor — passa por aquela única porta. Se a porta for confiável, o prédio é confiável. A cintura estreita é essa porta: o ponto fino por onde toda chamada de modelo do Alembic obrigatoriamente passa.

O sistema tem muitas origens de chamada lá em cima — o CLI, o Council, o Swarm, a Factory, o pipeline de ETL — e vários destinos lá embaixo — o gateway cliproxyapi, modelos MLX locais, o adapter offline. Entre eles existe um contrato comum: ModelAdapter.run(input), que devolve um valor onde a primeira coisa que você lê é o campo ok. Tudo afunila para esse ponto e depois se reabre — como uma ampulheta.

Pense como… a tomada de parede. Mil aparelhos diferentes (lá em cima) e várias usinas de energia (lá embaixo), mas no meio há um padrão de plugue. Você não reprojeta o ferro de passar para cada usina; ele só fala "plugue". Aqui, cada parte do Alembic só fala "ModelAdapter" — e nunca precisa saber qual modelo, qual provedor, ou o que fazer quando a rede cai.

A ampulheta: muitas origens · um contrato · muitos destinos CLI Council Swarm Factory ETL ModelAdapter.run(input) → ModelRunResult · NUNCA lança cliproxyapi gateway modelos MLX locais adapter offline se a porta é confiável, o sistema inteiro é confiável
Figura 1 — A cintura estreita afunila toda origem para um contrato e reabre para qualquer destino.
Infográfico vertical em forma de ampulheta: cinco chips clay no topo (CLI, Council, Swarm, Factory, ETL) sob o rótulo 'origens (L2-L4)' afunilam para o gargalo central destacado 'ModelAdapter.run(input) → ModelRunResult · NUNCA lança', que reabre para três chips olive embaixo (cliproxyapi gateway, modelos MLX locais, adapter offline) sob 'destinos (providers)'; legenda explica que clay é o contrato único e olive é o provider concreto.

A cintura estreita como ampulheta — toda chamada de modelo passa por uma porta só.

Guarde isto Cintura estreita = o único contrato por onde toda chamada de modelo do sistema passa. O nome em inglês é narrow waist; o "waist" (cintura) é o gargalo no meio da ampulheta. Confiável ali = confiável em todo lugar.

Por que isto é arquitetura, não estilo

Num sistema que dispara uma requisição para dezenas de modelos em paralelo (o painel do council, o fan-out do swarm), a diferença entre "um 500 de um provedor degrada aquela faixa" e "um 500 de um provedor derruba o run" está inteira em como a falha viaja. Se ela viaja como exceção, ela escapa para cima e contamina o chamador. Se ela viaja como valor, ela fica contida no resultado daquela faixa. A cintura estreita força a segunda opção em todo ponto de chamada — não por convenção, mas pelo tipo de retorno.

É por isso que a ADR-0009 trata o "never-throws" como invariante de sistema, e não como preferência de código: é a peça que torna o paralelismo seguro.

2

O shape: ModelRunResult


O contrato é uma união discriminada sobre um campo literal ok. Não existe throw no tipo — falha é um valor, não um evento de controle de fluxo:

Faça sua aposta antes de revelar

Uma função declara run(input): Promise<ModelRunResult>. Olhando só para essa assinatura, quantas formas distintas o resultado pode ter — e o que você precisa checar antes de usar o texto?

Exatamente duas formas: sucesso (ok: true, com text) ou falha (ok: false, com error). Você checa if (result.ok) primeiro; o TypeScript então estreita o tipo e só deixa você ler .text no ramo de sucesso. A assinatura sozinha já te diz tudo — nenhuma exceção escondida.

packages/contracts/src/model.ts — os dois ramos (condensado)

type ModelRunSuccess = {                  // ok: literal(true) é o discriminante
  ok: true; adapterId: string; durationMs: number; modelId: string;
  text: string; usage?: TokenUsage; costUsd?: number; raw?: unknown;
};
type ModelRunFailure = {                  // ok: literal(false)
  ok: false; adapterId: string; durationMs: number; modelId: string;
  error: { code: string; message: string; retryable: boolean };
};
type ModelRunResult = z.discriminatedUnion('ok', [Success, Failure]);

O chamador escreve if (result.ok) e o TypeScript estreita o tipo. Crucialmente, o ramo de falha carrega um retryable: o ponto de chamada não precisa adivinhar se um 429 vale a pena repetir — o resultado já diz. A própria interface documenta a lei: run(input): Promise<ModelRunResult> // NUNCA lança (invariante).

ModelRunResult — um discriminante, dois ramos totais checa ok ok: true ok: false ModelRunSuccess text: string ← a resposta usage? · costUsd? · raw? adapterId · modelId · durationMs ModelRunFailure error.code · error.message error.retryable: boolean ★ adapterId · modelId · durationMs
Figura 2 — A primeira leitura é ok; o compilador só libera text no ramo verde e error no ramo vermelho.
O que "discriminada" quer dizer. Os dois ramos compartilham um campo (ok) cujo valor literal (true ou false) identifica qual ramo você tem. O compilador usa esse literal para "estreitar": dentro de if (result.ok) { … } ele sabe que result.text existe; fora dele, sabe que result.error existe. Você não pode ler o campo errado por acidente — vira erro de compilação.
Estreitamento — o compilador só libera o campo do ramo certo if (result.ok) { result.text ✓ liberado result.error ✕ erro de compilação } else { result.error ✓ liberado result.text ✕ erro de compilação
Figura 2b — Você não consegue ler .text num ramo de falha: o compilador barra antes de rodar.

Por que Zod e não só um tipo TypeScript

O tipo é definido a partir de um schema Zod (z.discriminatedUnion('ok', […])), não só com type. Isso dá duas coisas que um tipo puro não dá: (1) o tipo TypeScript é derivado do schema com z.infer, então tipo e validador nunca divergem; (2) o mesmo schema pode validar em runtime — por exemplo, validar o que um provider devolveu antes de confiar nele. O contrato vira executável, não só uma anotação que o compilador apaga.

Os campos de telemetria não são decoração

durationMs, usage e costUsd sobem em ambos os ramos quando aplicável: é assim que o BudgetGuard contabiliza gasto e o cockpit mostra latência sem precisar instrumentar cada provider à parte. A cintura é também o ponto único de telemetria.

3

Como a garantia é imposta, não só documentada


Um comentário dizendo "nunca lança" não vale nada se um adapter esquecer. Então o invariante é estrutural: cada adapter implementa apenas um attempt() interno, e um único spine compartilhado — runWithGuards — o embrulha com quatro camadas, em ordem:

runWithGuards(adapterId, attempt, input) — quatro guardas aninhadas
① validação Zod do input no boundary — input ruim ⇒ ModelRunFailure (nunca chega ao attempt) ④ try/catch externo ao redor do withRetry — "rede de segurança final preserva o invariante" ③ withRetry — re-executa enquanto result.error.retryable, com backoff ② guardedAttempt — try/catch + feedback ao circuit-breaker ① attempt(input) — a ÚNICA coisa que cada adapter escreve um throw em QUALQUER profundidade vira um ModelRunFailure tipado via failureFromThrown — a união é total
Infográfico de camadas concêntricas aninhadas mostrando runWithGuards: anel externo slate-blue '④ try/catch externo - rede de segurança final', dentro dele anel olive '③ withRetry com backoff', dentro anel clay '② guardedAttempt com circuit-breaker e recordSuccess/recordFailure', e no centro a caixa oat '① attempt(input) - a única coisa que cada provider escreve'; à esquerda uma porta clay rotula a validação Zod do input no boundary; legenda no rodapé diz que a união é total e run() jamais rejeita.

As quatro guardas em ordem, de fora para dentro — só o miolo (attempt) é escrito por cada adapter.

packages/adapters/src/adapter-core.ts:118-147 — o spine canônico (condensado)

export const runWithGuards = async (adapterId, attempt, input, runtime = {}) => {
  const validation = validate(adapterId, input);          // ① Zod no boundary
  if (!validation.ok) return validation.result;         //    input ruim ⇒ Failure, não throw
  try {
    return await withRetry(                                // ③ backoff em resultados retryable
      () => guardedAttempt(adapterId, attempt, input, runtime.breaker, logger), // ②
      policy, { clock, logger, random: runtime.random, signal: input.signal },
    );
  } catch (cause) {
    // withRetry só rejeita se guardedAttempt rejeitar, o que ele nunca faz;
    // esta é uma rede de segurança final para preservar o invariante.
    return failureFromThrown({ adapterId, input, durationMs: 0 }, cause); // ④
  }
};
Trabalhado: siga um throw subindo pelas quatro camadas
1
O attempt() de um adapter dispara um TypeError cru lá no fundo do corpo dele (digamos, leu um campo undefined da resposta do provedor).
2
Camada ② — guardedAttempt tem um try/catch ao redor do attempt. O throw é capturado e convertido em failureFromThrown(...), virando um ModelRunFailure tipado. O breaker registra a falha (recordFailure()).
3
Camada ③ — withRetry recebe um valor (não uma exceção). Se result.error.retryable for verdadeiro, ele faz backoff e tenta de novo; senão, devolve o valor como está.
4
Camada ④ — mesmo que algo impossível escapasse das camadas internas, o try/catch externo do runWithGuards converteria via failureFromThrown. Resultado: o chamador de run() sempre recebe um ModelRunResult resolvido — a Promise nunca rejeita.
Agora você: suponha que a validação Zod do input (camada ①) falhe — você passou um input sem modelId. Em qual camada isso para, e o attempt() chega a rodar? (Resposta: para na ① antes do try; return validation.result devolve um Failure e o attempt nunca roda.)
Por que uma união em vez de exceções? Exceções são invisíveis no tipo de uma função — você não consegue dizer, a partir de run(): Promise<X>, o que ela pode lançar, então os chamadores ou capturam demais ou esquecem. Uma união discriminada torna todo caminho de falha visível e exaustivo: o compilador te obriga a tratar o ok: false. Num sistema que dispara uma requisição para dezenas de modelos em paralelo, essa é a diferença entre "o 500 de um provedor degrada aquela faixa" e "o 500 de um provedor derruba o run".
Exceção (sobe invisível) vs. valor (fica contido) com throw — o erro escapa para cima provider 500 contamina o run com união — o erro vira valor contido provider 500 ok:falsesó aquela faixa degrada
Figura 4 — Um throw escapa do tipo e sobe; um valor de falha fica contido no resultado da faixa.

A camada ④ é logicamente inalcançável — e fica de propósito

O comentário no código é literal: withRetry só rejeitaria se guardedAttempt rejeitasse, e guardedAttempt nunca rejeita (ele captura tudo internamente). Logo, hoje, o catch externo não pode disparar. Ele permanece porque o custo é um try e o prejuízo de o invariante quebrar é catastrófico — defesa em profundidade na costura mais carregada do sistema. Se um refactor futuro quebrar uma suposição interna, a rede externa ainda segura o "nunca lança".

O início de tudo: a porta Zod (①)

Antes do try, validate(adapterId, input) roda o schema Zod no input. Se falhar, runWithGuards faz return validation.result — um ModelRunFailuresem nunca entrar no bloco protegido. Input malformado morre no boundary, não no meio do attempt. Isso mantém os adapters concentrados só na chamada ao provedor, sem revalidar input.

4

Onde o breaker aprende e o retryable decide


A camada ② é onde o circuit-breaker aprende. guardedAttempt chama breaker?.recordSuccess() num resultado ok e breaker?.recordFailure() caso contrário, e reporta retry: true só quando result.error.retryable (adapter-core.ts:103-110). E há um caminho que nem chega a tentar: se o breaker já estiver aberto, guardedAttempt retorna cedo com circuit_open — protege um provedor que já está caído de levar mais carga.

Resultado ok → o breaker se recupera
attempt → ok recordSuccess() retry: false
Resultado retryable → backoff e repete
attempt → 429 recordFailure() retry: true → backoff ↻

Por que classificar a "repetibilidade" na fonte? Porque o adapter conhece o provedor. Um 429 (rate limit) ou um 5xx transitório são retryable: true; um 400 (input inválido) ou 401 (sem credencial) são retryable: false — repetir só desperdiça. Classificar uma vez, na fonte, mantém a política uniforme e impede que cada ponto de chamada reinterprete o erro à sua maneira (o que levaria a drift).

O circuit-breaker — três estados que protegem um provedor caído FECHADO deixa passar normal ABERTO circuit_open · nem tenta MEIO-ABERTO testa uma chamada falhas demais após timeout teste deu ok → fecha de novo
Figura 5 — Aberto, o breaker devolve circuit_open sem tocar o provedor; dá tempo para ele se recuperar.
Demonstração — clique num cenário e veja onde a falha para
① Zod ②/① attempt ② breaker ③ withRetry resultadoModelRunResult
Cenário: resposta 200 OKattempt devolve ok: true, o breaker faz recordSuccess(), sem retry. O chamador lê result.text. Sempre um ModelRunResult — nunca um throw.
Cuidado com a confusão Repetir nem sempre é certo. O retryable existe justamente para não repetir erros que repetição não conserta (400/401). Um sistema que repete tudo cegamente transforma um erro de credencial em uma tempestade de requisições inúteis — e ainda toma rate-limit por cima.

O early-return de circuito aberto

Antes de chamar o attempt, guardedAttempt consulta o breaker. Se o circuito está aberto (falhas demais recentes), ele retorna cedo: { retry: true, value: breakerRejection(...), reason: 'circuit_open' } — sem nem tocar o provedor. Isso é o circuit-breaker fazendo seu trabalho: dar tempo para um serviço caído se recuperar em vez de martelá-lo. O withRetry ainda decide o backoff a partir do retry: true.

De onde vem o backoff

withRetry recebe uma policy e os seams injetados clock e random (para jitter determinístico nos testes). Ele só repete resultados marcados retry: true; nunca repete um ok nem uma falha não-repetível. O número de tentativas e o crescimento do atraso vivem na policy, não espalhados pelos adapters.

5

O irmão: Result<T, E> para todo o resto


Existe uma segunda união discriminada, mais leve — também sobre ok, com ramos value/error — para todo trabalho falível que não é chamada de modelo: IO de arquivo, parsing, wrapping de subprocesso. Ela espelha de propósito a cintura do modelo, para que ambas se leiam igual nos pontos de chamada:

packages/contracts/src/result.ts — o irmão leve

type Result<T, E = Error> =
  | { readonly ok: true; readonly value: T }
  | { readonly ok: false; readonly error: E };
// helpers: ok(v), err(e), isOk, isErr, mapResult, tryCatch, tryCatchAsync
Duas uniões, um discriminante (ok) — leituras idênticas, cargas diferentes ModelRunResult — contrato de modelo ok: true → text, usage, costUsd… ok: false → error{code,msg,retryable} rico: telemetria + repetibilidade Result<T, E> — op falível qualquer ok: true → value: T ok: false → error: E mínimo: só o sucesso ou o erro ok
Figura 3 — Mesma forma (checa ok primeiro), payloads diferentes para trabalhos diferentes.

Este é o Result<T, Error> que você viu sendo devolvido por todo subsistema @alembic/hermes nas lições 7–13 — load(), transcribe(), webSearch() todos devolvem ele. A regra do repositório é explícita: "código de biblioteca devolve Result em vez de lançar" (CLAUDE.md). Duas uniões, uma filosofia: falibilidade é um valor, validado no boundary, nunca uma surpresa.

tryCatch / tryCatchAsync — a ponte do mundo "que lança" para o Result lib de terceiros pode lançar (throw) tryCatch( … ) ok(value) err(error)
Figura 6 — É por tryCatch que o código "que lança" entra no mundo "que devolve valor".

Pense em ModelRunResult como a "nota fiscal completa" de uma chamada de modelo: além de deu-certo/deu-errado, ela traz quanto custou, quanto demorou, qual modelo respondeu. Já o Result é o "recibo simples": só diz se deu certo (e o que veio) ou se deu errado (e qual o erro). Você usa o recibo simples para abrir um arquivo; usa a nota completa para falar com um modelo.

São deliberadamente não o mesmo tipo. ModelRunResult carrega o domínio da chamada de modelo (adapterId, modelId, durationMs, usage, costUsd, raw, e um error com retryable). Result<T, E> é genérico e mínimo: value: T ou error: E, mais helpers (ok, err, isOk, isErr, mapResult, tryCatch, tryCatchAsync). Compartilham o discriminante ok só para que if (r.ok) funcione igual nos dois — reduzindo a carga cognitiva de quem lê o código.

retrieval
O que o discriminante ok permite ao compilador?
clique para virar
Estreitar o tipo. Dentro de if (r.ok) ele libera o ramo de sucesso (text/value); fora, o ramo de erro. Ler o campo errado vira erro de compilação.
retrieval
Quem decide se um erro é retryable, e por quê?
clique para virar
O adapter, porque ele conhece o provedor. Classifica uma vez na fonte (429/5xx = true; 400/401 = false), mantendo a política uniforme.
retrieval
Por que manter a camada ④ se ela é inalcançável?
clique para virar
Defesa em profundidade. Custa um try; o prejuízo de o "nunca lança" quebrar é catastrófico. Protege o invariante contra um refactor futuro.
retrieval
Onde morre um input malformado?
clique para virar
Na camada ① (Zod no boundary), antes do try. runWithGuards devolve um Failure e o attempt nunca roda.

Os helpers que tornam o Result agradável de usar

ok(v) e err(e) constroem cada ramo; isOk/isErr são type-guards; mapResult transforma o valor de sucesso preservando o erro; tryCatch/tryCatchAsync embrulham código que lança (uma lib de terceiros, por exemplo) e o convertem para a fronteira de Result. É a ponte por onde o mundo "que lança" entra no mundo "que devolve valor".

Por que não unificar tudo num tipo só

Seria possível espremer chamadas de modelo dentro de Result<ModelText, ModelError>, mas você perderia a forma fixa e auto-documentada da chamada de modelo (telemetria + repetibilidade sempre presentes) e teria que reinventá-la em cada ponto. Dois tipos, com o mesmo discriminante, dão o melhor dos dois mundos: leitura uniforme, cargas sob medida.

6

Confusões comuns


"Nunca-lança significa que os erros são engolidos." Não — os erros são trazidos à tona, só que como valores em vez de exceções. Uma falha é um ModelRunFailure totalmente tipado, com code, message e retryable; nada fica escondido. O que se elimina é o salto invisível de controle de fluxo, não a informação.
"As duas uniões são redundantes." Não — ModelRunResult é o contrato rico de chamada de modelo (usage, custo, duração, id do adapter); Result<T, E> é o contrato mínimo de operação falível. Elas compartilham o discriminante ok de propósito, para que os pontos de chamada se leiam igual, mas carregam payloads diferentes para trabalhos diferentes.
"O retryable é só para log." Não — ele dirige a camada ③. guardedAttempt devolve retry: true exatamente quando result.error.retryable, e withRetry faz backoff a partir disso. É um campo de comportamento, não cosmético.
O mitoA realidade no código
"throw é mais simples"throw é invisível no tipo; a união força o tratamento do ok:false em tempo de compilação
"o catch externo é morto"é inalcançável hoje, mas é a rede que protege o invariante de um refactor futuro
"repetir sempre ajuda"retryable: true (429/5xx) repete; 400/401 não — repetir desperdiça
"validar input é do adapter"a porta Zod (①) valida antes; o adapter só escreve o attempt
7

Recapitulando


Cinco cartas, a lição inteira. Use as setas do teclado ou os botões.

1 · A porta única

Uma ampulheta

Toda chamada de modelo — CLI, Council, Swarm, Factory, ETL — passa por um contrato: ModelAdapter.run(input). Confiável ali = confiável em todo lugar.

5 origensrun()3 destinos
1

2 · O shape

Falha é um valor

O retorno é uma união discriminada em ok: ok:true traz text; ok:false traz error{code,message,retryable}. Sem throw no tipo.

ok ?true → textfalse → error
2

3 · A imposição

Quatro guardas em ordem

① Zod no boundary → ② guardedAttempt (try/catch + breaker) → ③ withRetry (backoff) → ④ rede externa. Um throw em qualquer profundidade vira Failure.

① attempt
3

4 · A decisão

Quem sabe, classifica

O adapter marca retryable na fonte (429/5xx = true; 400/401 = false). withRetry só repete o que está marcado. Breaker aberto = circuit_open, sem nem tentar.

retryable?4295xx→ true ↻400401→ false ✕
4

5 · O irmão

Uma filosofia, dois tipos

Result<T, E> espelha a cintura para IO/parse/subprocess. Mesma leitura (if (r.ok)), cargas sob medida. Falibilidade é um valor.

ModelRunResultResult<T,E>ok
5
Carta 1 / 5 use
As cinco verdades da cintura estreita
  1. Toda chamada de modelo passa por ModelAdapter.run — uma porta só.
  2. O retorno é uma união discriminada em ok; falha é valor, não exceção.
  3. runWithGuards embrulha o attempt de cada adapter com quatro guardas em ordem.
  4. Um throw em qualquer profundidade vira um ModelRunFailure tipado (failureFromThrown).
  5. Result<T, E> estende a mesma filosofia ao resto da base — leitura uniforme em todo canto.
8

Revisão


Cheque seu entendimento

Três perguntas. A pontuação corre conforme você responde.

1. O attempt() interno de um adapter dispara um TypeError cru lá no fundo do corpo. O que o chamador de run() observa?
Correto: b. O throw é capturado na camada ② e convertido em falha tipada; até o catch externo (logicamente inalcançável) da camada ④ o converteria. A união é total — run() resolve para um ModelRunResult, nunca rejeita.
2. Por que o ModelRunFailure carrega um retryable: boolean em vez de deixar o chamador decidir?
Correto: c. guardedAttempt devolve retry: true exatamente quando result.error.retryable, e withRetry faz backoff a partir desse flag. Classificar a repetibilidade na fonte mantém a política uniforme e evita drift entre pontos de chamada.
3. O try/catch externo do runWithGuards é descrito como "rede de segurança final". Por que manter código logicamente inalcançável?
Correto: d. withRetry só rejeita se guardedAttempt rejeitar, o que nunca acontece — então hoje o catch externo não dispara. Fica porque o custo é um try e o prejuízo de o invariante quebrar é catastrófico. O comentário no código diz exatamente isso.
Acertos: 0/3
Pergunta para levar à próxima lição: se uma única chamada já é tão blindada, como o sistema decide quais chamadas vale a pena fazer — e em que ordem barato-primeiro? Essa é a Lição 15 · O funil: o ETL de quatro tiers que só leva ao modelo pago os sinais que sobreviveram aos passos de graça.