Módulo 1 · Launch Academy · O sistema operacional do código-fonte
Alembic Launch Academy · Módulo 1

O sistema operacional do código-fonte

Antes de mudar qualquer linha, você precisa ler o repositório como uma engenheira sênior. Não pelo primeiro arquivo que abre — mas pela ordem certa: as configs raiz que governam o monorepo, os contratos que dão forma a tudo, o binário alembic e seus comandos, uma execução de ponta a ponta, as surfaces HTTP/SSE/MCP e, por fim, os testes — que são o mapa real dos invariantes. Este módulo é essa planta-baixa.

Cada seção tem um cartão Técnico opcional. Abra quando quiser o detalhe linha-a-linha.
Ao terminar este módulo, você consegue…
  • Reconhecer o Alembic como um monorepo pnpm: 27 pacotes em packages/* mais apps/cli, orquestrados pelo Turborepo.
  • Explicar por que se começa pela config raiz (pnpm-workspace.yaml, package.json, turbo.json, vitest.workspace.ts) — ela explica todo o resto.
  • Ler o contrato primeiro: Result<T,E>, Tier e o ModelRunResult (a cintura estreita) de @alembic/contracts.
  • Achar o binário alembic em apps/cli/src/index.ts e os 30 comandos que ele despacha.
  • Seguir uma execução real (alembic distill --offline) do parse até o funil e os stores.
  • Apontar as 3 rotas HTTP/SSE e as ferramentas MCP do harness, e usar os testes como mapa dos invariantes.
O que assumimos de você
  • Sabe ler um pouco de TypeScript — reconhecer um type, uma função, um import.
  • Já abriu um terminal e rodou um comando. Se ainda não — mostramos o caminho exato.
  • Nada mais. Os termos monorepo, workspace, contrato, narrow waist (cintura estreita), store e SSE são definidos aqui.
A fonte primária deste módulo
O próprio checkout do Alembicpnpm-workspace.yaml, package.json e turbo.json na raiz; packages/contracts/src/ (os tipos); apps/cli/src/index.ts e commands.ts (os comandos); packages/harness/src/http.ts e mcp.ts (as surfaces). Tudo neste módulo é verificado contra esses arquivos.
27
pacotes em packages/* + apps/cli
30
comandos do binário alembic
174
arquivos *.test.ts no workspace
3
rotas HTTP/SSE do harness

Os números são contados no código, não estimados. O comando para conferir está em cada seção — a disciplina do Alembic é provar, nunca afirmar de memória.

01

O mapa do território

Um repositório é uma cidade — você precisa do mapa antes da rua

O Alembic não é um único programa. É um monorepo: um repositório que guarda muitos pacotes pequenos que se encaixam. Abrir um arquivo qualquer e tentar entender o todo é como entrar numa cidade por um beco aleatório. A leitura sênior começa pelo mapa: quais são os bairros (os pacotes), quem manda (as configs raiz) e por onde a água corre (uma execução).

Analogia. Pense num shopping. A planta na entrada mostra todas as lojas e os corredores antes de você andar. As configs raiz são essa planta: elas declaram quais lojas existem (os workspaces) e as regras da casa (build, typecheck, test). Ler a planta primeiro economiza horas de caminhada perdida.

A forma do repositório

O Alembic é um monorepo pnpm com workspaces declarados em pnpm-workspace.yaml (packages/* e apps/*). Hoje são 27 pacotes sob packages/ (de @alembic/contracts a @alembic/marketing-factory) e um app, apps/cli (o binário @alembic/cli). O Turborepo orquestra quatro tarefas — build, typecheck, test, lint — respeitando o grafo de dependências entre pacotes.

O sistema de módulos é ESM com resolução NodeNext e TypeScript em modo strict. Bibliotecas evitam lançar exceções: usam o Result<T,Error> fail-closed de @alembic/contracts. IO de arquivo passa pelo FsPort de @alembic/etl para ser testável.

Mapa do monorepo Alembic: no topo a faixa RAIZ com as quatro configs (pnpm-workspace.yaml, package.json scripts, turbo.json, vitest.workspace.ts), abaixo uma grade de cartões para os workspaces (apps/cli, contracts, etl, adapters, council, swarm, harness, coda, hermes, marketing-factory) com a nota de 27 pacotes no total, e uma seta indicando que as configs raiz governam tudo.

O mapa do repositório. As configs raiz no topo governam; cada cartão abaixo é um workspace com uma responsabilidade única. Comece sempre de cima.

Diagrama · as quatro configs raiz e os workspaces que elas governam
CONFIGS RAIZ — governam todos os workspaces pnpm-workspace.yaml package.json · scripts turbo.json · pipeline vitest.workspace.ts @alembic/contractsZod waist · tipos apps/clibinário alembic @alembic/etlstores · FsPort @alembic/adaptersrun() nunca lança @alembic/councilvotos · verifier @alembic/swarmagentes limitados @alembic/harnessHTTP · SSE · MCP @alembic/codaos gates @alembic/hermesmemória · skills @alembic/forgescope gate @alembic/marketing-factoryassets manifests … + 16 outros27 pacotes no total
Passe o mouse sobre um cartão para destacá-lo. Quatro configs no topo, 27 pacotes + apps/cli embaixo — todos governados por elas.
Guarde istoMonorepo = um repositório com vários pacotes. Workspace = cada pacote dentro dele. O pnpm liga os workspaces por symlink, então @alembic/cli importa @alembic/contracts como se fosse uma dependência publicada — mas é a pasta vizinha.
Diagrama · workspaces ligados por symlink (a "dependência" é a pasta vizinha)
apps/cliimport '@alembic/contracts' packages/contractso código real, ao lado symlink do pnpm · sem download
O mesmo grafo de módulos que o consumidor vê em produção roda nos testes — porque o import resolve para a pasta vizinha, não para uma cópia publicada.
02

As configs raiz governam

Quatro arquivos no topo explicam o repositório inteiro

Quatro arquivos na raiz são a constituição do projeto. Lê-los primeiro responde "o que existe?", "como eu valido?" e "como rodo os testes?" antes de qualquer detalhe.

pnpm-workspace.yaml — declara os workspaces: packages/* e apps/*. É a lista de bairros da cidade.
package.json (raiz) — os scripts: typecheck, build, test, lint, detect, governance, factory. É o painel de controle.
turbo.json — o pipeline do Turborepo: as tarefas build · typecheck · test · lint e suas dependências. É quem sabe a ordem.
vitest.workspace.ts — junta os projetos de teste de cada pacote. É o mapa de onde os testes vivem.
O contrato de validação local

Toda mudança de código tem que manter três comandos verdes. Decore-os — eles são o portão de entrada de qualquer trabalho no Alembic:

pnpm -r typecheck && pnpm -r build && pnpm -w test
Diagrama · o pipeline de validação (a mesma ordem que o CI executa)
install typecheck build test doctor --json impeccabledesign gate
O CI (.github/workflows/ci.yml) roda exatamente esta sequência. Se um passo falha, o pipeline falha fechado — nada entra na main sem os seis verdes.
Faça sua aposta antes de revelar

Você roda pnpm -r build e o build falha em @alembic/swarm com erro de tipo num import de @alembic/contracts — que você acabou de editar. Qual a causa mais provável?

Você editou o contracts mas não rodou pnpm -r build nele antes. Pacotes dependentes enxergam o contrato pelos .d.ts publicados; sem rebuild, o swarm compila contra a versão antiga. A regra do projeto: depois de mudar um pacote, rode pnpm -r build para os dependentes verem o novo .d.ts.
Diagrama · por que a ordem de leitura é de baixo para cima no grafo
contracts adapters harness apps/cli setas = "depende de" · leia na direção contrária (de baixo para cima)
O grafo só aponta para baixo. Ler na ordem inversa das setas — contratos primeiro — garante que nenhum detalhe apareça antes do contrato que o define.
03

Contratos antes de tudo

Se você não entende os tipos, o resto parece acidental

O pacote @alembic/contracts é a cintura estreita do sistema: os tipos e schemas que todos os outros pacotes compartilham. Lê-lo primeiro é o atalho — depois disso, cada função em cada pacote tem um formato que você já reconhece.

Analogia. É como aprender as peças do xadrez antes de assistir a uma partida. Sem saber como o cavalo anda, cada lance parece caótico. Result, Tier e ModelRunResult são as peças do Alembic.

Os três contratos que você precisa primeiro

Result<T, E = Error> (result.ts): uma união discriminada em ok. Ou { ok: true, value } ou { ok: false, error }. Bibliotecas devolvem Result em vez de lançar — falha vira valor, não exceção.

Tier (tier.ts): a escada de autonomia T0 → T4. T0 é silencioso e $0; T4 é park (exige council + humano). Há ainda o marcador LOCAL para trabalho que deve ficar em modelos locais/$0.

ModelRunResult (model.ts): a união discriminada que toda chamada de adapter devolve — modelRunSuccessSchema (ok: true, com text, usage, costUsd) ou modelRunFailureSchema (ok: false, com um error estruturado e retryable). Sucesso e falha são o mesmo tipo de retorno.

Diagrama · a cintura estreita — toda chamada de modelo tem a mesma forma
ModelRunInput run()nunca lança ok: truetext · usage · costUsd ok: falseerror · retryable
Uma forma de entrada, uma função, dois ramos de saída — e nenhum caminho excepcional. É isso que torna o sistema testável e roteável por custo.
Diagrama · a escada de Tier — autonomia e custo sobem juntos
T0 · silencioso · $0 T1 · log leve T2 · 1 revisor T3 · council T4 · PARK · council + humano + autonomia · + custo · + supervisão
Cada degrau acima vê menos trabalho, com mais supervisão e custo. T0 roda em tudo de graça; T4 nunca executa sozinho.
Contrato
O que é o Result<T,E>?
clique para virar
Uma união discriminada em ok: sucesso {ok:true,value} ou falha {ok:false,error}. Bibliotecas o devolvem em vez de lançar — fail-closed.
Contrato
O que T0 significa na escada de Tier?
clique para virar
Autonomia total, silenciosa, custo $0, sem humano no loop. A escada sobe até T4 (park — exige council + humano).
Contrato
Qual o tipo de retorno de toda chamada de adapter?
clique para virar
ModelRunResult — união em ok: success (com text) ou failure (com error + retryable). É a cintura estreita.
Dica de leituraComece por packages/contracts/src/result.ts, depois tier.ts, depois model.ts. São arquivos pequenos e densos — em 15 minutos você lê a "gramática" do projeto inteiro.
04

O binário alembic

Onde os comandos moram e como eles despacham

O programa que você roda no terminal — alembic — vive em apps/cli/src/index.ts. Ele faz três coisas em sequência: analisa os argumentos, despacha para o comando certo e devolve um Result. Hoje são 30 comandos.

parse → dispatch → Result

index.ts chama parseCliArgs (de args.ts, que usa schemas Zod por comando) e então um dispatch que é uma cadeia de if (parsed.command === '…'). Cada ramo chama uma função de comando em commands.ts (ou em módulos de seam como marketing-seams.ts), que devolve um Result<T, Error>. O USAGE impresso pelo alembic help também mora em index.ts.

Para adicionar um comando, o fluxo é fixo: schema em args.ts → função em commands.ts → ramo em index.ts → linha no USAGE → teste em commands.test.ts ou e2e.test.ts → doc em CLAUDE.md e README.md.

Explore alguns comandos representativos — clique nas abas:

alembic distill
O funil noturno
Roda o funil de destilação sobre um corpus. Com --offline é hermético e custa $0: valida o corpus, monta um registry offline e roda runFunnel, renderizando um FunnelReport.
Leitura: apps/cli/src/commands.ts · packages/harness/src/funnel.ts
30 comandos, uma forma. De distill e run a employee, marketing, memory e doctor — todos passam pelo mesmo parse → dispatch → Result. Aprender um é aprender a estrutura de todos.
Diagrama · um dispatch, muitos comandos — agrupados por propósito
dispatch (index.ts)parsed.command === … Execuçãorun · runs · serve · replay · tui Funildistill · status · embed-index Capacidadesembed · ocr · course · memory Produtoforge · plan · marketing · employee
Os 30 comandos não são caos: lendo o dispatch você os agrupa por propósito. Os exemplos por grupo são uma amostra — o total verificado é 30.
Exemplo guiado · achar um comando do zero
1
Abra apps/cli/src/index.ts e procure por parsed.command ===. Cada ocorrência é um comando. Conte-as: são 30.
2
Escolha um — digamos 'doctor'. Veja qual função ele chama no ramo (em commands.ts).
3
Abra essa função e confirme que ela devolve um Result. Esse é o contrato de saída de todo comando.
4
Agora você: rode rg -n "parsed.command ===" apps/cli/src/index.ts | wc -l e confira que dá 30. Depois escolha 'embed' e siga o mesmo caminho até o Result.
05

Seguir uma execução

Uma corrida de ponta a ponta liga todos os pacotes

Ler arquivos isolados ensina os bairros; seguir uma execução ensina como a água corre entre eles. alembic distill --offline é o melhor primeiro trajeto: ele toca CLI, contracts, adapters, ETL e harness numa única corrida — e de graça.

Fluxo de leitura sênior em seis passos, da esquerda para a direita: config raiz, contratos, entrypoint (30 comandos), uma execução (alembic distill roda runFunnel), surfaces (harness HTTP/SSE/MCP) e testes (174 arquivos, invariantes), com os selos contrato-antes-da-implementação e os-testes-são-o-mapa.

A ordem de leitura sênior. Do contrato à execução às surfaces — os números (30 comandos, 174 arquivos de teste) são verificados no código.

Diagrama · o trajeto de `alembic distill --offline`
parseCliArgsargs.ts valida corpuscommands.ts registry offline$0 hermético runFunnelT0 → T3 FunnelReportrender + Result
Cinco passos, cinco pacotes tocados. Com --offline nada vai à rede — é o jeito de estudar o sistema sem gastar nem depender do gateway.
Atenção a um engano comumalembic status não roda o funil — ele só lê contadores dos stores. Quem executa o trabalho (T0–T3) é o distill. Confundir os dois leva a "por que nada aconteceu?".
Diagrama · leitor vs. executor — dois comandos, papéis opostos
alembic statussó LÊ contadores alembic distillEXECUTA o funil stores (T0–T3)append-only · JSONL escreve
Mesma origem (os stores), papéis opostos: status consulta, distill produz. Saber qual é qual responde "por que nada mudou?".
06

As surfaces L4

HTTP, SSE e MCP são camadas finas sobre o núcleo

Além do terminal, o Alembic expõe um servidor. As surfaces (HTTP, streaming SSE e MCP) são finas: elas não contêm lógica de negócio, só traduzem pedidos para o HarnessCore. O núcleo não conhece transporte — por isso é fácil testar e adicionar novas surfaces depois.

Três rotas, ferramentas MCP, um núcleo

As rotas HTTP estão em packages/harness/src/http.ts — o objeto ROUTES declara exatamente três: POST /runs (a única escrita — inicia um run), GET /runs/:runId/status e GET /runs/:runId/events (o stream SSE).

As ferramentas MCP estão em mcp.ts — o registro MCP_TOOLS traz três stateless: harness_status, harness_events e harness_lane. Há ainda ferramentas read-only com escopo de run, como context_pack e artifact_read. Todas leem o snapshot/eventos do run via o HarnessCore e o FsPort.

Diagrama · surfaces finas, um núcleo que não conhece transporte
HTTP · http.tsPOST /runs · GET status SSE · /eventsstream de eventos MCP · mcp.tsstatus · events · lane HarnessCorenão conhece transporte FsPort → diretório do run
Toda surface aponta para o mesmo núcleo. Adicionar uma nova (tRPC, console) é escrever outro adaptador fino — o HarnessCore não muda.
Detalhe técnicoPOST /runs é a única escrita. Tudo o mais é leitura (status, events) ou ferramentas MCP read-only. Isso mantém a superfície de mutação minúscula e auditável.
07

Os testes são o mapa

Use a suíte como o catálogo de invariantes do sistema

Documentação envelhece; testes não — eles rodam. Quando quiser saber o que o sistema promete, leia os testes do pacote. São 174 arquivos *.test.ts distribuídos por pacote, cada um protegendo um invariante.

Onde olharQue invariante o teste protege
packages/etl/srcAppend-only e PII: stores só acrescentam; dados sensíveis são barrados.
packages/adapters/srcrun() nunca lança: falha de modelo vira ok:false, não exceção; custo é contabilizado.
packages/council/srcScoring e dissenso: votos agregam de forma determinística; o verifier preserva a discordância.
packages/harness/srcRotas e MCP: as 3 rotas e as ferramentas respondem com o formato do contrato.
packages/swarm/srcResume: um run interrompido retoma do events.jsonl + cache sem refazer trabalho.
apps/cli/srce2e: parse → dispatch → Result de ponta a ponta para os comandos.
Como contar. find packages apps -name '*.test.ts' | wc -l devolve 174. E vitest.workspace.ts mostra que cada pacote roda seus próprios testes — o @alembic/factory é um caso especial, vendorizado e isolado da raiz.
Diagrama · o teste é a especificação executável de um invariante
invariante"run nunca lança" *.test.tsa spec executável verde = provavale AGORA
Documentação afirma; o teste prova ao rodar. Por isso a suíte — e não o HANDOFF.md — é o catálogo confiável do que o sistema garante.
08

Confusões comuns

Três armadilhas que pegam quem lê o repositório pela primeira vez

Docs dizem estado, código diz verdade

HANDOFF.md e o status semanal ajudam a se orientar, mas uma decisão de release precisa rodar os comandos atuais. Documentos atrasam; o checkout é a fonte.

`status` não é `distill`

alembic status só lê contadores. Quem executa o funil (T0–T3) é o distill. Se "nada aconteceu", você provavelmente rodou o leitor, não o executor.

HTTP pronto ≠ produto SaaS pronto

O servidor do harness existe e expõe 3 rotas, mas surfaces extras (console, tRPC) e partes de produto são follow-ups documentados. Ler o código evita prometer o que ainda não está lá.

"19 pacotes" é um número velho

Mapas antigos citam contagens menores. O workspace cresceu — hoje são 27 pacotes + apps/cli. Sempre conte no código (find … -name package.json), não na memória.

09

Como eu verifico isso

Quatro checagens que provam o que este módulo afirma

Listar os workspaces. find packages apps -maxdepth 2 -name package.json -not -path '*/node_modules/*' — confira os 27 pacotes + apps/cli.
Contar os comandos. rg -n "parsed.command ===" apps/cli/src/index.ts | wc -l — devolve 30. Abra args.ts e commands.ts para ver o parse → dispatch → Result.
Ler as rotas. rg -n "ROUTES" packages/harness/src/http.ts — confira POST /runs, GET …/status, GET …/events. Depois rg -n "MCP_TOOLS" mcp.ts.
Ler os scripts. Abra package.json e turbo.json na raiz — o contrato de validação local é typecheck · build · test (verde antes de qualquer commit).
Recall rápido: por onde se começa a ler o Alembic, e por quê? (Resposta: pela config raiz e pelos contratos — eles dão a lista do que existe e a gramática de tipos, antes de qualquer detalhe acidental.)
10

Recapitulando

Cinco slides para fixar a planta-baixa

Passe pelos slides com os botões, as setas do teclado (← →) ou os pontos. Cada um é um pilar deste módulo.

O território

Monorepo, não um programa

O Alembic são 27 pacotes + apps/cli em workspaces pnpm, orquestrados pelo Turborepo. Leia o mapa antes da rua.

configs raiz
1

A ordem

Contrato antes de tudo

@alembic/contracts primeiro: Result, Tier, ModelRunResult. Depois disso, cada função tem um formato que você já reconhece.

Result Tier ModelRunResult
2

O binário

parse → dispatch → Result

apps/cli/src/index.ts despacha 30 comandos. Aprender um (o caminho até o Result) é aprender a estrutura de todos.

parse dispatch Result
3

As surfaces

Finas sobre um núcleo

HTTP, SSE e MCP só traduzem para o HarnessCore. 3 rotas (POST /runs é a única escrita); o core não conhece transporte.

HTTP SSE MCP HarnessCore
4

A prova

Os testes são o mapa

174 arquivos *.test.ts: append-only, PII, run-nunca-lança, scoring, MCP, resume. Quer saber o que o sistema promete? Leia o teste.

174 testes
5
Slide 1 / 5 use
Os cinco fatos para levar deste módulo
  1. O Alembic é um monorepo pnpm: 27 pacotes + apps/cli, orquestrados pelo Turborepo.
  2. Leia a config raiz primeiro; ela lista o que existe e as regras (build · typecheck · test · lint).
  3. Leia os contratos em seguida: Result, Tier, ModelRunResult — a cintura estreita.
  4. O binário despacha 30 comandos via parse → dispatch → Result; as surfaces (3 rotas + MCP) são finas sobre o núcleo.
  5. Os 174 arquivos de teste são o mapa real dos invariantes — prove, não afirme de memória.
11

Verifique seu domínio

Três perguntas — cada erro vem com a explicação

Revisão cumulativa
1. Por onde uma engenheira sênior começa a ler o Alembic?
Correto: b. A config raiz dá a lista do que existe (workspaces) e as regras (scripts/pipeline); os contratos dão a gramática de tipos. Com isso, cada arquivo depois faz sentido. a é o beco aleatório que a leitura sênior evita; c e d são úteis depois, não a porta de entrada.
2. O que toda chamada de adapter de modelo devolve?
Correto: c. ModelRunResult é a cintura estreita: modelRunSuccessSchema (ok:true, com text/usage/costUsd) ou modelRunFailureSchema (ok:false, com error + retryable). a viola o "nunca lança"; b joga fora o texto e o erro; d confunde adapter com store.
3. Qual a diferença entre alembic status e alembic distill?
Correto: a. status é leitura — conta o que está nos stores. distill é execução — valida o corpus, monta o registry (offline = $0) e roda runFunnel. b inverte os papéis; c é falso; d inverte quem escreve.
Acertos: 0/3
Ficou com dúvida em qualquer ponto — por que contrato antes de implementação, por que status ≠ distill, o que é a cintura estreita? Pergunte ao seu agente (o seu professor). O próximo módulo sobe um nível: produto e modelo de negócio — o que essa máquina vira quando vira oferta.