Tudo que a fusão construiu — funil, employees, memória, marketing, RAG — sai por UMA porta: o único app entre os 28 workspaces. São 33 comandos top-level e 47 formas documentadas, todos nascidos do mesmo protocolo de seis passos, todos obedecendo à mesma doutrina (dry-run por default) e, desde o digest #1, com os próprios números policiados por script — a lei do derive-counts.
Um comando novo NUNCA é improvisado. A receita canônica (documentada no CLAUDE.md do repo, "How to add or change a CLI command") tem seis passos: ① schema Zod em args.ts (<comando>ArgsSchema + entrada no PARSE_OPTIONS + mapeamento no toRecord); ② implementação em commands.ts como run<Comando> devolvendo Result<T, Error> — nunca lançando; ③ dispatch em index.ts; ④ atualizar o USAGE; ⑤ testes em commands.test.ts/e2e.test.ts; ⑥ documentar em CLAUDE.md, AGENTS.md e README. Pular o ④ ou o ⑥ cria o drift que o derive-counts denuncia (seção 4).
No caminho de execução, o argv percorre: parseCliArgs (args.ts:1248) tokeniza, toRecord (args.ts:993) casa posicionais + flags num record único — inclusive lendo ambiente: --offline OU ALEMBIC_OFFLINE ligam o modo hermético —, o parsedArgsSchema.safeParse valida TUDO na borda, e só então dispatch chama o run<Comando>. Uso inválido nem chega ao comando: index.ts:434–438 imprime a mensagem + o USAGE no stderr e devolve EXIT_USAGE = 2 (index.ts:225).
Pense como… a recepção de um hospital: ninguém entra direto na sala de cirurgia — triagem (Zod), prontuário tipado (Args) e só então o especialista (run<Comando>). Onde quebra: a recepção humana improvisa com paciente incompleto; parseCliArgs recusa QUALQUER coisa fora do schema, com o caminho exato do campo errado na mensagem.
Nenhum run<Comando> escreve no stdout por conta própria. Todos recebem CommandDeps (commands.ts:289–300): um write (Writer), um logger (CliLogger) e o seam opcional imagegen — a costura de geração de imagem do course --images, populada na BORDA (index.ts lê ALEMBIC_CLIPROXY_TOKEN/CLIPROXYAPI_API_KEY do ambiente; a library nunca toca process.env). É o mesmo idioma de ports do motor inteiro, agora na casca: os testes injetam writer/logger/imagegen falsos e exercitam comandos INTEIROS sem terminal, sem rede e sem custo — é assim que commands.test.ts e e2e.test.ts (2 dos 8 arquivos de teste do app; ls apps/cli/src/*.test.ts) cobrem a superfície dentro dos 200 arquivos · 1.834 testes verdes da suíte.
/** Inputs every command shares: where to write output and where to log. */ export interface CommandDeps { readonly write: Writer; readonly logger: CliLogger; /** Optional image-generation seam for `course --images` (item 4.3). The CLI * boundary populates it with the env-sourced cliproxy creds; tests inject fake * runner/fetch/fileIo so the opt-in hero path is exercised WITHOUT any real call. */ readonly imagegen?: ImageGenDeps; }
*.test.ts em apps/cli/src/ (prove: ls apps/cli/src/*.test.ts) — de commands.test.ts, que injeta CommandDeps fake por comando, ao e2e.test.ts, que percorre parse→dispatch→exit code de ponta a ponta sem terminal real.derive-counts antes de confiar no seu dedo (ou no nosso).runDistill, runEmployee, runImport… são adaptadores de borda: parse aqui, decisão nos packages. Quando a lição 30 falar de "superfície única", é isto — um lugar onde TODO caminho do motor tem forma de comando, com as mesmas garantias.Regra da casa, sem exceção na superfície: comando digitado por curiosidade custa $0 e não muda nada. Cada tipo de efeito tem sua trava EXPLÍCITA — e elas não se confundem:
| Efeito | Default | Opt-in explícito | Exemplo |
|---|---|---|---|
| Rede/modelo | offline, $0 | --online | employee run imprime o prompt e PARA; import --distill sem --online falha fechado |
| Escrita em disco | preview | --write | import mostra o que gravaria; --write appenda idempotente (dedupe por id) |
| Gasto real | preview de custo | --approve E --yes | marketing video/ad: uma flag sozinha NUNCA gasta — dupla trava |
| Funil | — | --offline força $0 | distill <corpus> --offline: hermético, byte-estável |
Detalhe de desenho que os quizzes adoram: a dupla trava é permissiva no parse e rígida no efeito. marketing video --approve sem --yes NÃO é erro de uso — o comando roda e entrega o preview de custo, que é o resultado útil e seguro do caminho incompleto. Erro de uso (exit 2) é para argv malformado; caminho incompleto é dry-run.
De onde saem "33 comandos / 47 formas"? De script, não de dedo. O USAGE exportado (index.ts:68–196) é a superfície DECLARADA — o contrato legível por humanos e por scripts. A história que virou lei: o digest #1 do failure-historian elegeu o drift de contagem como pior ofensor recorrente → propôs "derivar contagens da fonte" → nasceu scripts/derive-counts.mjs (PR #158) → e a PRIMEIRA execução pegou drift vivo: docs diziam 32 comandos; a fonte dizia 33/47. Desde então, número de superfície se DERIVA. (Este curso obedece: todo número aqui vem do spec derivado, nunca de contagem manual — está no rodapé.)
alembic marketing video shots.json --approve, sem --yes. O que acontece?--approve E --yes. Uma flag passada por engano num script jamais queima orçamento.derive-counts compara a superfície declarada com os docs e denuncia o drift (foi exatamente o 32→33/47 da primeira execução, PR #158). A receita de 6 passos existe para o drift nunca nascer.Explore a superfície inteira sem gastar um centavo (doutrina: tudo abaixo é $0):
# a superfície declarada (os 33/47 saem daqui) alembic help # o enum canônico + a esteira de parse sed -n '18,52p' apps/cli/src/args.ts sed -n '993,1010p' apps/cli/src/args.ts sed -n '1248,1270p' apps/cli/src/args.ts # o contrato de deps injetadas + o caminho do uso inválido sed -n '289,300p' apps/cli/src/commands.ts sed -n '430,444p' apps/cli/src/index.ts # a superfície de teste do app (8 arquivos; e2e cobre parse→dispatch→exit) ls apps/cli/src/*.test.ts # dry-runs de doutrina: rodar "de verdade" sem efeito nenhum alembic import ~/.claude/projects --format claude # preview; nada gravado alembic employee run <id> --goal "teste" # imprime o prompt e para alembic marketing campaign request.json --out plan.json # pipeline inteiro em $0 # o baseline de saúde (a forma canônica, com safe-test) pnpm -r typecheck && pnpm -r build && node scripts/safe-test.mjs pnpm -w test
run<Comando> → dispatch → USAGE → testes → docs. Pular um passo = drift.Result no meio, exit code na saída (0/1/2); uso inválido nunca toca o comando.CommandDeps injetado (write/logger/imagegen) — env lido só na borda; testes exercitam comandos inteiros a $0.--online), escrita (--write) e gasto (--approve E --yes) são fechaduras separadas.derive-counts, PR #158): 33 comandos / 47 formas — e o drift 32→33/47 é a prova.