Lição 08 · Alembic Completo v3 · Marketing Factory · multi-tenant · spend-safe
Alembic Completo v3 · Visual Course

Marketing Factory multi-tenant

Ao fim desta lição você sabe como UMA fábrica brand-agnóstica produz vídeo com a identidade REAL de qualquer cliente — pasta do cliente dirige tudo, modelos fixos, referências como medias ROLED (o unblock provado com a C.D), e nem um centavo sai sem --approve E --yes.

Leia primeiro (fonte primária)
packages/marketing-factory/src/ — ads.ts, director.ts, higgsfield-api.ts, validate.ts, campaign.ts

Esta lição destila o pipeline de anúncios da fábrica: identidade do cliente → shotlist de diretor → cenas → QA — com as decisões de spend-safety embutidas no código, não na disciplina do operador.

Você vai conseguir
  • Explicar por que "cada cliente é uma pasta" elimina código por cliente
  • Nomear os 3 estágios e o que cada um trava (ambiente, assets, cenas)
  • Justificar os modelos fixos: gemini_omni (vídeo) e gpt_image_2 (imagem)
  • Contar o unblock das referências ROLED e por que --image plano falhava
  • Aplicar a dupla trava e montar um validate sob medida (checks opt-in)
Leia a versão simples, ou abra a camada técnica em qualquer seção.
1

A grande ideia — a pasta do cliente dirige tudo


A fábrica é brand-agnóstica e multi-tenant: sinal, brief, marca, identidade visual e restrições são INPUTS por cliente — zero mudança de código por cliente. A porta de entrada é ingestClientFolder (packages/marketing-factory/src/folder-ingest.ts): a pasta do cliente carrega um brand-profile.json CURADO (autorado do material real do cliente, nunca inventado por modelo — a escolha "determinístico + curado" do founder) com localização, personagens, figurino, cenários, paleta, voz e proibições.

A identidade não fica no papel: buildIdentityContext(brief) monta UM bloco injetável PREPENDIDO a cada prompt criativo, e os assets reais (logo/escritório/uniforme — nunca avatares) viajam como referências de imagem em cada geração. Foi assim que a fábrica rodou para advocacia, odontologia e contabilidade — provada em 3 indústrias — trocando só a pasta.

Pense como… uma gráfica com UMA prensa e um gabarito por cliente: o gabarito (pasta) traz cores, logo e regras; a prensa nunca é reajustada. Quebra: gabarito de gráfica não processa vídeo com fala — aqui o "gabarito" também dita idioma, lipsync e o que é proibido aparecer.

PII-safety no ingest: uma pasta real de cliente NUNCA é varrida às cegas — a descoberta de assets é opt-in e escopada (--assets-dir <subdir> vouched), e isExcludedDir pula clientes/contratos/processos como segunda rede. Sem o subdir, valem só os assets declarados no profile.
2

Em uma imagem — os três estágios


produto · 0006-marketing-factory · s0
pasta do cliente brand-profile.json fotos · clipes · logo (curado, real) STAGE 1 · lock de assets marketing environment marketing sheets ambiente + personagem travados STAGE 2 · diretor buildDirectorShotlist beats → AdShotlist @name → assets REAIS STAGE 3 · cenas + QA generateAdScenes validate (QA gate) gemini_omni · pt-BR lipsync TRAVA DE GASTO em todo estágio pago: dry-run $0 por DEFAULT · real só com --approve E --yes estimateCost PRIMEIRO · uma flag sozinha NUNCA gasta (ads.ts — generateAdScenes) o capstone `alembic marketing ad <pasta> <brief>` encadeia os 3 estágios em UM comando (runMarketingAd)
Pasta → locks → shotlist → cenas+QA. A lei da casa (cinematic_headphones): "build all your assets first, lock them up front" — o Stage 1 existe para o Stage 3 não improvisar.
3

Stage 1 — environment e sheets: travar o real


marketing environment (runMarketingEnvironment) constrói a folha de referência do CENÁRIO real: coleta fotos e clipes da pasta (PII-safe), extractFrames amostra cada clipe de pan a 1fps nos ângulos distintos da sala, e planEnvironmentSheet pareia cada still real com um ângulo canônico (logo_wall, three_quarter, wide, entrance, detail) + um prompt de recriação que PRESERVA a sala e o logo reais — nunca inventa logo. Provado no escritório real da C.D: 2 clipes → 18 ângulos + 1 foto → 19 refs.

marketing sheets (runMarketingSheets) é a metade personagem/produto/prop: deriveAssetSheetSpecs deriva as folhas que o brief implica — personagem sem avatar travado vira folha split-frame (close-up trava o ROSTO, corpo inteiro trava o PORTE, fundo cinza) vestindo o uniforme REAL com o logo bordado preservado da referência limpa; o rosto é SEMPRE uma pessoa fictícia, nunca a likeness de quem aparece na foto do uniforme. Um par canônico feminino + masculino nasce quando existe asset de uniforme.

A gotcha do espelho
Uniforme fotografado em selfie de espelho faz o modelo copiar o logo INVERTIDO — regra de prompt não conserta vestuário copiado. O fix confiável é des-espelhar a FONTE: --flip-uniform aplica ffmpeg hflip antes de anexar (verificado na fronteira paga).
Camada técnica — copy-paste
# Stage 1 — ambiente (núcleo determinístico $0; escreve os stills em --out)
alembic marketing environment ./clientes/cd-advocacia --assets-dir brand-assets --out /tmp/cd-env

# Stage 1 — sheets: plano $0 por default (sheets-plan.json, sem chamada de modelo)
alembic marketing sheets ./clientes/cd-advocacia --out /tmp/cd-sheets
# render real (gpt_image_2, custo estimado ANTES, cap 8): exige as DUAS flags
alembic marketing sheets ./clientes/cd-advocacia --out /tmp/cd-sheets --approve --yes
4

Stage 2 — o shotlist de diretor


buildDirectorShotlist (packages/marketing-factory/src/director.ts) transforma beats simples ({shot, action, character?, setting?, camera?, speech?, cut?}) num AdShotlist de nível diretor, emitindo as linhas na ordem de fluxo internalizada (comentário em director.ts:308-310):

linhao que trava
PERSONAGEMidentidade E figurino idênticos à referência — "IMAGE-ONLY: renderize SOMENTE o que aparece na referência" (director.ts:326; a adição inventada é a causa nº1 de troca de identidade)
AMBIENTEo cenário EXATO da referência real — parede, móveis, identidade visual (director.ts:351)
AÇÃOmovimento passo a passo, específico ("never he dances") (director.ts:371)
CÂMERAo enquadramento do beat (director.ts:385)
FALAfala no idioma do brief com "lipsync natural" (director.ts:388)
MARCAlogo real @token fiel e discreto — e NUNCA uma linha de logo sem asset real (director.ts:347)
RESTRIÇÕESos locks de marca do profile (director.ts:412)
CONTINUIDADEo corte — e matchCut abre espelhando o fechamento da cena anterior
Elementos @name: o shotlist não fala "o logo" — fala @logo, @ambiente, @dra: tokens WIRED aos assets reais registrados no Stage 1. Um beat com cuts[] emite até 5 CUTs ordenados numa geração só; style por beat AUMENTA o prefixo compartilhado (a luz por cena do blog).
5

Modelos fixos — e aspect é PARÂMETRO, não prompt


Decisão do founder, fixada no código: vídeo SEMPRE gemini_omni (Gemini Omni Flash — pt-BR + lipsync) — DEFAULT_VIDEO_MODEL_ID = 'gemini_omni' em packages/marketing-factory/src/ads.ts:95, resolvido por resolveVideoModelId; um operador pode pinar outro via --model-id (ex.: seedance_2_0), mas o default não se discute. Imagem é gpt_image_2 (as sheets do Stage 1 rendem nele).

E a lição paga: aspect escrito no prompt é IGNORADO — o modelo devolve 16:9 e o gate de QA reprova. --aspect viaja como o parâmetro real aspect_ratio da geração (buildVideoSceneParams); o enum do gemini_omni aceita SÓ 16:9,9:16 (verificado via higgsfield model get gemini_omni; um override seedance_2_0 aceita enum mais largo). O valor passa verbatim e o modelo SELECIONADO valida — fora do enum falha fechado, sem gasto.

errado — aspect no texto do prompt
prompt: "…vídeo vertical 9:16…"
render → sai 16:9 (modelo ignora)
validate --aspect 9:16 → FAIL
certo — aspect como parâmetro
alembic marketing video shots.json \
  --aspect 9:16
params: { aspect_ratio: "9:16" } → 720×1280
6

O unblock ROLED — a C.D de verdade no quadro


O bloqueio (até 2026-07-04): o CLI real do Higgsfield anexa referências como --image "plano" (uma lista media_input_ids) — e o gemini_omni REJEITA esse shape no POST /agents/jobs com o erro de criação medias[].GeminiOmniReferenceImageMedia.role (higgsfield-api.ts:18-21). Tradução: o modelo exige que cada referência declare um PAPEL.

O unblock: reproduzir o caminho do PRÓPRIO site via API direta — createHiggsfieldApiClient (packages/marketing-factory/src/higgsfield-api.ts) envia cada asset real como media ROLED: { role: 'image', data: { id, type: 'media_input' } } (higgsfield-api.ts:134-135). PROVADO na fronteira paga: a parede do escritório REAL da C.D + o logo apareceram no render, 720×1280 (9:16).

CLI path — --image plano media_input_ids: [ id1, id2, … ] gemini_omni: create error medias[].…ReferenceImageMedia.role ✗ (refs nunca chegam ao render) API direta — medias ROLED (o caminho do site) medias: [{ role: "image", data: { id, type: "media_input" } }] job aceito → render com a parede e o logo REAIS da C.D · 720×1280 ✓
O mesmo asset, dois envelopes. O papel ('role') é o que o gemini_omni exige — higgsfield-api.ts:134-135 reproduz o shape do site. Cap: até 7 imagens de referência por geração.
No dry-run (CLI fake, $0) as refs não são enviadas — o preview de custo apenas NOTA que os assets anexam na run real da API. Um pin --model-id seedance_2_0 volta a condicionar pelo caminho do CLI.
7

Dupla trava, QA opt-in e o campaign $0


A dupla trava. Todo caminho pago nasce dry-run: "Spend-safe by construction: dryRun defaults to true … Real generation requires BOTH dryRun: false and approve: true" (ads.ts:28-31; o curto-circuito em ads.ts:245: if (dryRun || !opts.approve)). No CLI isso vira: --approve E --yes juntos, ou nunca gasta — uma flag sozinha imprime o custo estimado e para. estimateCost roda ANTES em toda cena.

QA sob medida. validateVideo (packages/marketing-factory/src/validate.ts) roda ffprobe/ffmpeg/whisper sobre o mp4 local e deriva o aspect de w:h — mas todo check de conteúdo é OPT-IN: --script liga transcript-vs-roteiro + frases obrigatórias + pronúncia da marca (script "OPTIONAL — omit it for a speechless video", validate.ts:87-91); --forbidden liga a varredura de visuais proibidos (validate.ts:104); --no-text liga o check de texto queimado no frame. Anúncio mudo, de música ou com texto proposital: omita os checks que não fazem sentido — o gate serve a QUALQUER tipo de vídeo.

O one-shot. marketing campaign (runCampaign, packages/marketing-factory/src/campaign.ts) encadeia discover → generate → validate com $0 em TODOS os estágios por default: uma run "bare" toca ZERO rede e gasta ZERO — factory offline com Higgsfield fake, e a validação fica PLAN-ONLY (os critérios que uma QA real IMPORIA, derivados do brief — nenhum artefato real existe ainda). Três cadeados opt-in: --online (research pago), --approve --yes (geração paga), e a QA real só quando houver mp4.

produto · 0006-marketing-factory · s4
Camada técnica — copy-paste
# Stage 3 — dry-run SEMPRE primeiro (preview de custo, $0)
alembic marketing video shotlist.json --aspect 9:16 --client ./clientes/cd-advocacia
# render pago: SÓ com as duas flags juntas
alembic marketing video shotlist.json --aspect 9:16 --client ./clientes/cd-advocacia --approve --yes

# capstone multi-tenant (3 estágios em 1 comando; $0 por default)
alembic marketing ad ./clientes/cd-advocacia brief.json --aspect 9:16
alembic marketing ad ./clientes/cd-advocacia brief.json --aspect 9:16 --approve --yes

# QA gate — cada check é opt-in; monte o gate do SEU tipo de vídeo
alembic marketing validate out/scene-01.mp4 --aspect 9:16 --lang pt \
  --script "Proteja a sua aposentadoria…" --brand "cê-dê advocacia" --forbidden "tribunal,martelo"

# one-shot $0 de ponta a ponta (nenhuma rede, nenhum gasto)
alembic marketing campaign request.json --out plan.json
Quiz — você roda alembic marketing video shots.json --approve (só uma flag). O que acontece?
Uma flag sozinha NUNCA gasta: o caminho real exige dryRun: false E approve: true (ads.ts:28-31, curto-circuito em ads.ts:245) — no CLI, --approve E --yes juntos. O default é sempre o preview de custo via estimateCost.
Recall: pasta curada dirige (1) → Stage 1 trava ambiente e personagens REAIS (3) → Stage 2 dirige beat a beat na ordem PERSONAGEM→…→CONTINUIDADE (4) → Stage 3 renderiza no modelo FIXO com aspect como parâmetro (5), refs como medias ROLED (6) — tudo atrás da dupla trava, com QA opt-in e um one-shot $0 (7).
Teaser da Lição 09: sai o diretor, entra o operador — a rotina diária que mantém esse motor honesto: baseline com safe-test, derive-counts como lei, e as 8 gotchas que já custaram noites.