Passo 7 · Módulo 3 · Fábricas · Factory, Forge & cursos
Curso do Produto Alembic · Visual Course

Factory, Forge & cursos: as máquinas que constroem

Você já destilou o corpus, contratou a Iris e rodou a campanha da C.D. Agora veja o lado de construção: a Factory que põe agentes de código em caixas isoladas, o Forge de 7 passos que vira um pedido vago em escopo executável, e o course + @alembic/design — o mesmo motor que renderizou esta página que você está lendo.

Leia primeiro (fonte primária)
packages/design/src/render.ts — renderHtmlPage, a função que monta o shell deste curso

Esta lição é um espelho: ela explica a maquinaria que a produziu. alembic course chama exatamente esta função para escrever o index.html e cada lição. Ler 100 linhas de render.ts = entender por que toda página do curso é byte-estável e passa o gate impeccable.

Leia a versão simples, ou abra a camada técnica em qualquer seção.
Suposições tolas (o que presumimos de você)
  • Você sabe o que é um terminal e já rodou alembic alguma vez. Nada além disso.
  • Você leu (ou folheou) as lições 1–6: corpus → signal → venture → campanha → Iris. Esta é a 7ª peça: as ferramentas de construir.
  • Não presumimos que você saiba Docker, Effect, Zod ou design system. Cada termo é definido no primeiro uso.
Ao fim desta lição você consegue…
  • Explicar o que @alembic/factory (o sandcastle internalizado) faz e escolher entre seus 5 templates.
  • Listar os 7 passos do Forge e nomear cada artefato que ele materializa num diretório de escopo.
  • Descrever a cadeia do alembic course: corpus → selectLocalLlmPackages → manifesto → renderHtmlPage.
  • Entender por que esta própria página é byte-estável e passa o gate impeccable.
1

A grande ideia


Até agora o curso mostrou o Alembic produzindo: destilando conhecimento, orquestrando agentes, rodando campanhas. Esta lição vira a câmera para o outro lado — as três oficinas de construção que o operador usa para criar coisas novas.

São três máquinas distintas, cada uma resolvendo um problema diferente de "como construir":

  • A Factory (@alembic/factory) — põe agentes de código (Claude, Codex, Copilot…) para trabalhar dentro de caixas isoladas (git worktrees + containers), seguindo um template de orquestração. É o chão de fábrica onde código é escrito sem bagunçar seu repositório.
  • O Forge (forge/plan) — pega um pedido vago ("quero uma fábrica de petições") e o destila, em 7 passos, num escopo executável: GOAL.md, um contrato de validação e um plano alembic.plan.ts. É o porteiro que transforma desejo em especificação.
  • O course (@alembic/coda + @alembic/design) — pega um corpus e gera um curso visual-teach byte-estável. É o motor que renderizou esta página.

Pense como… uma marcenaria com três estações. A Factory é a bancada com tornos e morsas, onde a peça é cortada longe da sua mesa de jantar. O Forge é a prancheta onde o pedido do cliente vira um desenho técnico cotado. O course é a gráfica que imprime o manual do produto, sempre no mesmo papel timbrado. Onde a analogia quebra: a marcenaria precisa de um marceneiro; aqui as três estações são código puro que um agente opera sozinho — e duas delas rodam $0, offline, determinísticas.

Por baixo do capô

As três estações vivem em pacotes diferentes do monorepo e têm naturezas opostas de execução. @alembic/factory é imperativo e com efeitos colaterais reais (cria worktrees, sobe containers Docker/Podman, invoca CLIs de agentes) — é o único dos três que precisa de IO pesado. Já @alembic/coda+@alembic/design são puros e determinísticos: sem Date.now(), sem Math.random(), sem rede — o curso é função pura do corpus. O forge fica no meio: cada um dos 7 passos é plugável e roda online (via LLM) ou offline (fallback determinístico), conforme a flag --online.

O fio que costura tudo: os três são acionados por um comando alembic <cmd> que segue o mesmo padrão (schema Zod em args.tsrun<Command> em commands.ts retornando Result<T, Error> → dispatch em index.ts). Nenhum deles lança exceção: falham fechado, devolvendo o erro como valor.

2

Três máquinas, três problemas


Antes do diagrama, fixe a distinção — é o erro nº 1 de quem começa: achar que "factory", "forge" e "course" são sinônimos. Eles operam em momentos diferentes da história da nossa fábrica de petições.

MáquinaEntradaSaídaMomento na história C.D
Forge forge/planum pedido em textoGOAL.md + contrato + alembic.plan.ts"quero uma fábrica de petições" → escopo
Factory @alembic/factoryissues + um templatecódigo em branches isoladasos agentes implementam a venture, em caixas
course courseum corpuscurso HTML byte-estávelo manual de operação da Iris para a C.D
o que você tem em mãos? só umdesejo? FORGE issues +quer código? FACTORY um corpus adocumentar? COURSE sim sim sim
Qual máquina? Siga o losango. O Forge vem antes da Factory na vida real, mas cada um responde a uma pergunta distinta.
Factory
O que a Factory isola e por quê?
clique para virar
Resposta
Cada agente trabalha num git worktree próprio (branch separada) dentro de um container Docker/Podman. Isso deixa N agentes mexerem em paralelo sem colidir, e mantém seu repo principal intocado.
Forge
Quantos passos tem o Forge, e qual é o último?
clique para virar
Resposta
7 passos: grill → research → prototype → PRD → issues → GOAL → review. O review fecha gerando o REVIEW.md de observabilidade.
course
Qual função renderiza CADA página do curso?
clique para virar
Resposta
renderHtmlPage de @alembic/design. runCourse a chama uma vez para o index.html e uma vez por lição — a mesma função que montou esta página.
Determinismo
Por que o curso é "byte-estável"?
clique para virar
Resposta
O manifesto e o HTML são função pura do corpus: sem Date.now()/Math.random() lidos na saída. Um clock é injetado mas deliberadamente não usado no artefato. Mesmo corpus → bytes idênticos.
3

Em uma imagem


As três oficinas no fluxo da nossa fábrica de petições, da esquerda (desejo) para a direita (produto entregue):

pedido vago "fábrica de petições" FORGE 7 passos GOAL.md contrato alembic.plan.ts FACTORY agentes em caixas wt1 wt2 wt3 COURSE renderHtmlPage index.html lessons/*.html byte-estável produto + manual cintura estreita · Result<T, Error> · falha fechado, nunca lança
O pedido entra à esquerda; o Forge o coa em escopo; a Factory executa em worktrees paralelos (wt1/wt2/wt3); o course publica o manual. As três assentam na mesma cintura estreita Result.
A wide concept illustration of three connected artisan workshop stations on one long bench, read left to right: the first is a drafting table turning a rough sketch into a precise
A wide concept illustration of three connected artisan workshop stations on one long bench, read left to right
O detalhe que prende tudo: as setas são um pipeline, não um acoplamento. Você pode rodar o course sozinho sobre qualquer corpus, ou a Factory sobre qualquer conjunto de issues, sem ter passado pelo Forge. Eles compõem, mas não dependem um do outro.

Recapitulando em slides

Os cinco pontos que sustentam o resto da lição. Use as setas .

Lição 0007 · A grande ideia

Três oficinas de construção

O Alembic não só produz — ele dá as ferramentas de construir: Factory, Forge e course.

A Factory

Agentes em caixas isoladas

@alembic/factory é o sandcastle internalizado: cada agente num worktree + container, guiado por um de 5 templates.

5 templates de orquestração

O Forge

Desejo → escopo, em 7 passos

grill → research → prototype → PRD → issues → GOAL → review. Sai um alembic.plan.ts que o harness executa.

O course

Esta página foi gerada assim

selectLocalLlmPackages → manifesto → renderHtmlPage. O mesmo motor que você está lendo agora.

corpusselectLocalLlm…renderHtmlPage

Determinismo

Byte-estável = governável

Sem clock/RNG na saída. Mesmo corpus → bytes idênticos. Isso deixa o gate impeccable detectar qualquer drift de marca.

A peça que falta

O lado de construir da fábrica

Forge especifica · Factory executa · course documenta. Três máquinas, uma cintura Result.

1 / 6setas
4

A Factory — agentes em caixas


@alembic/factory é uma fábrica de software: ela pega issues (tarefas) e um template de orquestração, e põe agentes de código — Claude Code, Codex, Copilot, Cursor, OpenCode — para resolvê-las dentro de ambientes isolados. "Isolado" aqui é literal: cada agente recebe um git worktree próprio (uma cópia da branch) dentro de um container (Docker ou Podman). Eles trabalham em paralelo sem pisar no seu repositório nem um no outro.

Esse pacote não foi escrito do zero. Ele é o sandcastle — uma fábrica open-source — internalizado (vendorizado) e adaptado às convenções do Alembic.

Fonte primária (a procedência honesta)
packages/factory/package.json — a linha de atribuição

A própria descrição do pacote declara a origem: "Alembic software factory … Vendored and adapted from @ai-hero/sandcastle (MIT, (c) Matt Pocock); see README.md." Internalizar com crédito é a regra — nunca apagar a procedência.

Jargão · worktreegit worktree = uma segunda (terceira, quarta…) pasta de trabalho do mesmo repositório, cada uma numa branch diferente. Deixa vários agentes editarem "o código" ao mesmo tempo sem conflito, porque cada um tem sua própria árvore de arquivos.
repositório-pai container 1 worktree · branch Aagente 1 container 2 worktree · branch Bagente 2 container 3 worktree · branch Cagente 3
Isolamento em duas camadas: cada agente recebe um worktree (sua própria árvore de arquivos) dentro de um container (seu próprio sistema). Paralelo, sem colisão.

A Factory não amarra você a um único agente. Ela fala com vários CLIs de código por trás de uma fachada comum:

AgentProvider claudeCode codex copilot cursor opencode pi
Seis provedores reais exportados por AgentProvider.ts — você troca o agente sem mudar a orquestração.

Os 5 templates que vêm na caixa — cada um é uma estratégia de orquestração diferente. Repare que o nome descreve o grafo de agentes:

sequential-reviewer uma issue por vez · review após cada implementa #1 review #1 implementa #2 review #2 serial: previsível, fácil de auditar parallel-planner-with-review planeja · ramifica · revisa por branch · merge planner branch A branch B branch C review A review B review C merge paralelo: rápido, mas precisa do merge
Os nomes não são decoração — "review" no nome adiciona o passo de revisão; "parallel" abre branches simultâneas. (Grafos: interpretação da descrição de cada template; o JSON traz só nome+descrição.)
Preveja antes de revelar

O parallel-planner precisa instalar a dependência zod no host, mas o simple-loop não. Por quê só o planner precisa de Zod?

Porque o template planner faz o agente emitir um plano estruturado (uma saída <plan>) que precisa ser validado contra um schema. O TemplateMetadata declara isso em dependencies: ["zod"], e o init oferece instalar — senão npx tsx .sandcastle/main.ts quebraria com ERR_MODULE_NOT_FOUND. O simple-loop só "pega issue, fecha issue": não tem saída estruturada, então não precisa de validador.

DicaComece pelo simple-loop ou sequential-reviewer. Os templates parallel são mais rápidos, mas o merge de N branches é onde mora a complexidade — deixe para quando o fluxo serial já estiver provado.
CuidadoA Factory é a única das três máquinas desta lição que tem efeitos colaterais pesados de verdade (cria worktrees, sobe containers, invoca CLIs reais). Forge e course rodam offline/$0; a Factory executa código de agente — trate-a com o mesmo respeito de um git push.
5

O Forge — desejo vira escopo


O Forge resolve o problema mais humano de todos: você quer algo ("uma fábrica de petições personalizadas para a C.D"), mas um pedido em uma frase não é executável. O Forge é um funil de 7 passos que coa esse desejo até virar um escopo que o harness consegue rodar sozinho.

Cada passo produz um artefato concreto, escrito num diretório de escopo. Você nomeia o pedido e o Forge materializa a pasta inteira:

1 grillSCOPE.md 2 researchRESEARCH.md 3 prototypePROTOTYPE.md 4 PRDPRD.md 5 issuesissues.jsonl 6 GOALGOAL.md +contrato 7 reviewREVIEW.md o passo 6 produz o que o harness executa: alembic.plan.ts
Os 7 passos, em ordem, com o artefato de cada um. Da esquerda (interrogatório do pedido) à direita (relatório de observabilidade).
A wide illustration of a funnel with seven labeled rings stacked vertically, a vague cloudy scribble poured in at the wide top and a single crisp folded blueprint document dropping
A wide illustration of a funnel with seven labeled rings stacked vertically, a vague cloudy scribble poured in

Veja o passo a passo de perto — clique em cada um:

grill

→ SCOPE.md

Interroga o pedido. "Fábrica de petições" para quem? quais tipos de petição? o que é sucesso? Sai um SCOPE.md com o problema afiado.

Scope Gate. Quando o escopo está pronto, o Scope Gate (packages/forge/src/scope.ts) copia o GOAL.md, o plano e o contrato para o diretório da run — é o primeiro dos 5 gates do pipeline (Scope → Council → Proof → Validator → Publish). Assim a run executa contra uma cópia congelada do escopo, não contra arquivos que podem mudar embaixo dela.
LembreCada passo do Forge é plugável: roda online (um LLM faz o trabalho) ou offline (um fallback determinístico), conforme a flag --online. O default é offline, $0 — você forja o esqueleto de graça e só paga quando quiser a versão pensada por um modelo.
forge

O front-end de 7 passos completo. Materializa a pasta de escopo inteira — SCOPE/RESEARCH/PROTOTYPE/PRD/issues/GOAL/contrato/plan/REVIEW.

plan

O atalho: gera direto o plano HTML + GOAL.md + contrato + alembic.plan.ts a partir do pedido, sem os 7 passos. É o @alembic/planf3 por baixo (o Forge também o usa internamente).

6

course & @alembic/design — esta página


Agora o espelho. O comando alembic course pega um corpus e gera um curso visual-teach — um index.html + uma página por lição. E ele faz isso com a mesma máquina que produziu a página que você está lendo: @alembic/design, o design system Warm-Neutral como dados tipados + renderizadores puros.

A cadeia tem três elos, e cada um vive num pacote:

corpusrecords/família @alembic/codaselectLocalLlmPackagesseleciona pacotes @alembic/contractscourseManifestartefato tipado, sem timestamp @alembic/designrenderHtmlPageHTML self-contained index.htmllessons/*.html puro · determinístico · sem Date.now / Math.random / rede → byte-estável
Três pacotes, uma cadeia. O coda escolhe o conteúdo; o contracts fixa o formato; o design pinta o HTML — sempre nas mesmas cores.

Por que "byte-estável" importa

O runCourse recebe um clock injetado (para honrar a assinatura padrão), mas deliberadamente não o lê na saída: o manifesto não carrega timestamp. Resultado: rode o mesmo corpus dez vezes, e os dez cursos são byte por byte idênticos.

Isso não é preciosismo. É o que torna a marca governável: como o HTML é determinístico, o gate impeccable consegue detectar qualquer "drift" — uma cor fora do sistema, um heading pulado — porque sabe exatamente quais bytes esperar. Os tokens de cor vivem como dados validados (THEME_REGISTRY), copiados verbatim de docs/design-system.html.

Fonte primária (a regra de governança)
packages/design/src/themes.ts — o preset WARM_NEUTRAL

O docstring é a lei: "Every token value below is copied VERBATIM from the live :root block of docs/design-system.html … these values are what the impeccable CI detector keys off, so a drift here is a brand-governance bug."

WARM_NEUTRALpreset (dados)validado por Zod renderThemeCsstokens → CSS :root { … }bloco de variáveis <style>embutido na página a cor nunca é "hardcoded" — é um dado que flui do preset até o <style>
O design system é dado, não CSS solto. Por isso o gate impeccable consegue auditá-lo: há uma única fonte da verdade.
Modo claro ivory #FAF9F5
palette base
Modo escuro paletteDark #1A1917
mesmos tokens, valores escuros

Os mesmos nomes de token (clay, olive, ivory…) ganham valores diferentes sob :root[data-theme="dark"]. renderHtmlPage emite o bloco escuro só quando darkMode está ligado e o preset tem paletteDark — senão a página é puro modo claro (o artefato que o gate impeccable valida sem ruído). É por isso que o botão ☾/☀ no topo desta página funciona.

Duas renderizações, um sistema

Sutileza que vale ouro: esta página foi escrita à mão a partir do shell visual-teach; o alembic course gera as páginas dele com @alembic/design. São duas fontes diferentes do mesmo design system. Compare o token olive:

TokenShell desta página (lesson-template)@alembic/design (themes.ts)Igual?
olive#788C5D#788C5D✓ idêntico
oat#E3DACC#E3DACC✓ idêntico
ivory#FAF9F5#FAF9F5✓ idêntico
clay#D97757#E75533≈ mesma família
Nota honestaO clay tem hexes ligeiramente diferentes entre as duas fontes (o shell calibrou o tom para os dois temas; o themes.ts traz o valor de marca de docs/design-system.html). É a prova viva de por que o gate impeccable existe — para que essas pequenas derivas sejam intencionais e rastreáveis, não acidentes. Os SVGs desta lição usam #D97757 (a paleta do shell) por consistência com a página.
shell à mão lesson-template esta página renderHtmlPage alembic course páginas geradas Warm-Neutral olive #788C5D serif h1 · mono h2 dark toggle
Uma identidade, duas mãos que a desenham. O centro (o sistema Warm-Neutral) é o que importa — e é byte-idêntico onde conta.
A wide illustration of an identical printed page coming off a press three times in a row, the three copies stacked perfectly aligned to show they are byte-for-byte identical; to th
A wide illustration of an identical printed page coming off a press three times in a row, the three copies sta
Publish Gate. Depois de gerar o curso, o último gate (packages/coda/src/publish.ts) o publica: gist privado + Cloudflare Pages. É o mesmo padrão que levou o curso anterior do produto ao ar. Gerar ≠ publicar: a publicação é uma ação outward, sempre explícita.
7

No código


O elo central, sem retoque: dentro de runCourse, a mesma renderHtmlPage é chamada para o índice e para cada lição. Esta é a linha que torna a lição um espelho.

apps/cli/src/commands.ts — runCourse (trecho real, ~linha 5007)
// puro e $0: o manifesto + HTML são função pura das entradas
// (sem clock/RNG lido na saída) → mesmo corpus = curso byte-idêntico.
const indexHtml = renderHtmlPage({
  preset,
  title: manifest.title,
  body: renderCourseIndexBody(manifest),
  lang: 'en',
  nav: { lessons: courseIndexNav, lessonsLabel: 'Course' },
});
// …e uma vez por lição:
for (const lesson of manifest.lessons) {
  const lessonHtml = renderHtmlPage({
    preset, title: lesson.title,
    body: renderCourseLessonBody(lesson),
    nav: { /* on-this-page + índice do curso */ },
  });
}

E a assinatura do renderizador — note que ele devolve uma string de HTML self-contained, e que darkMode e nav são opcionais (o curso byte-estável e esta página usam ambos):

packages/design/src/render.ts — RenderHtmlPageInput
export interface RenderHtmlPageInput {
  readonly preset: ThemePreset;     // os tokens Warm-Neutral
  readonly title: string;
  readonly body: string;          // HTML cru, inserido após o <h1>
  readonly lang?: string;          // PT-BR é o default
  readonly darkMode?: boolean;     // toggle embutido; default true
  readonly nav?: RenderHtmlPageNav;  // sidebar opcional
}

As três máquinas chegam ao usuário pelo mesmo wiring de 3 arquivos — o padrão de qualquer comando alembic:

args.tsschema Zod valida flags commands.tsrunComando → Result index.tsdispatch + USAGE nenhum lança exceção — o erro volta como valor (Result), falha fechado
O mesmo trilho para forge, course e os comandos da Factory — previsível de ler e de testar.

E o lugar do Forge no quadro maior: ele alimenta os 5 gates do pipeline. O Scope Gate (de forge/src/scope.ts) é o primeiro; o Publish Gate (de coda/src/publish.ts) é o último — o mesmo que publica o curso.

Scopeforge/scope.ts CouncilGO / NO_GO Proofproof[] bash Validatorescrutínio Publishcoda/publish.ts o Forge entra aqui o course sai aqui
Forge e course são as pontas de um mesmo pipeline: o Scope Gate congela o escopo; o Publish Gate publica o manual.

Acesse você mesmo

Clone o repo e abra os três arquivos lado a lado: apps/cli/src/commands.ts (busque runCourse), packages/design/src/render.ts (a função renderHtmlPage) e packages/factory/src/InitService.ts (a constante TEMPLATES e listTemplates). Rode pnpm --filter @alembic/design test para ver os testes de byte-estabilidade e de contraste do tema passarem.

O @alembic/design exporta: WARM_NEUTRAL, DEFAULT_THEME_ID, THEME_REGISTRY, pickTheme, e os renderizadores renderThemeCss / renderStyleTag / renderHtmlPage. Tudo puro, depende só de @alembic/contracts.

8

Experimente


Hora de pôr a mão. Primeiro um exemplo resolvido (como o operador forja o escopo da fábrica de petições), depois um seletor de templates ao vivo.

Exemplo resolvido — forjar a venture C.D, do pedido ao plano
1
Pedido. alembic forge "fábrica de petições personalizadas para a C.D Advocacia" — o Forge cria a pasta de escopo a partir do slug do pedido.
2
grill → research → prototype → PRD. Os 4 primeiros passos afiam o problema e produzem SCOPE.md, RESEARCH.md, PROTOTYPE.md e PRD.md. Offline ($0) por padrão.
3
issues → GOAL. O passo 5 gera issues.jsonl (as tarefas); o passo 6 escreve GOAL.md, o validation-contract.md e o alembic.plan.ts — o plano que o harness executa.
4
review. O passo 7 fecha com REVIEW.md (observabilidade). O Scope Gate copia GOAL+plano+contrato para a run, e a venture está pronta para alembic run --goal GOAL.md --plan alembic.plan.ts --yes.
5
Agora você: qual passo do Forge você puxaria para --online primeiro, e por quê? (Dica: onde o pensamento de um modelo agrega mais — o research ou o issues?)

Demo — o seletor de templates da Factory

Clique num template. O grafo de agentes e a "configuração derivada" mudam — exatamente como a Factory deriva os passos de setup a partir do nome do template (name.includes("review") adiciona o revisor; dependencies com zod pede o validador de schema).

Descrições verbatim de TEMPLATES em packages/factory/src/InitService.ts. Os grafos são a interpretação visual de cada descrição.

Simples ou técnico — escolha a profundidade

As três máquinas dividem o trabalho de "construir": o Forge decide o que fazer (escopo), a Factory faz (agentes em caixas) e o course documenta (o manual). Duas delas são de graça e offline; só a Factory mexe no mundo de verdade.

Naturezas de execução opostas: @alembic/factory é imperativo com IO real (worktrees, containers, CLIs de agente — construído sobre Effect); @alembic/coda+@alembic/design são puros/determinísticos (sem clock/RNG/IO, dependem só de @alembic/contracts); o forge é híbrido com passos plugáveis online/offline. Todos seguem o contrato Result<T, Error> e o wiring args.ts → commands.ts → index.ts.

Revisão — fixe os três

1. O que @alembic/factory internaliza, e qual é a regra ao fazê-lo?
O package.json declara: "Vendored and adapted from @ai-hero/sandcastle (MIT, (c) Matt Pocock)". Internalizar com procedência é a regra de ouro — nunca apagar o crédito.
2. Quais são, em ordem, os 7 passos do Forge?
É o docstring de runForgeFrontEndCli em commands.ts: "grill → research → prototype → PRD → issues → GOAL → review". (A opção b são as fases de uma run, não os passos do Forge.)
3. Por que o curso gerado por alembic course é byte-estável?
O comentário em runCourse é explícito: "o manifesto + HTML são função pura das entradas (sem clock/RNG lido na saída)". Determinismo na origem — não um passo de normalização posterior — é o que o impeccable consegue auditar.

Acertos: 0/3

As Dez sobre Factory, Forge & cursos

  1. São três máquinas de construir, não sinônimos: Forge especifica, Factory executa, course documenta.
  2. Factory = o sandcastle internalizado; cada agente num worktree + container isolado.
  3. São 5 templates: blank, simple-loop, sequential-reviewer, parallel-planner, parallel-planner-with-review.
  4. O nome do template descreve o grafo: "review" adiciona revisor; "parallel" abre branches.
  5. Forge = 7 passos: grill → research → prototype → PRD → issues → GOAL → review.
  6. Cada passo materializa um artefato; o passo 6 produz o alembic.plan.ts executável.
  7. plan é o atalho de um passo (via @alembic/planf3); forge é o funil completo.
  8. O course encadeia selectLocalLlmPackagescourseManifestrenderHtmlPage.
  9. renderHtmlPage de @alembic/design renderizou esta página — o curso é um espelho.
  10. Byte-estável = governável: sem clock/RNG na saída, o gate impeccable detecta drift de marca.
Você é o professor agora: rode alembic course <corpus> sobre qualquer pasta de records e abra o index.html gerado — compare o shell dele com esta página. Que token você mudaria primeiro em themes.ts, e o que o gate impeccable diria? Próxima lição (0008): amarramos tudo — corpus → signal → venture → C.D → Iris — em três walkthroughs ponta a ponta.