PTENES
Flow A# · 12 audiences · human gate

One topic becomes 12 reels, one for each audience.

The bot writes the scripts and STOPS. You record the avatars in HeyGen; when you’re done, open the gate and it downloads, assembles the 9:16 reels, and delivers them to each channel.

promoavatar — promotional reels by audience
What it is

Pipeline definition, not code

This is the repo for domain of the flow /promoavatar of the inemaccbot. There isn’t a single line of TypeScript here — only the flow.json, the prompts, and the chat help. A new workflow is an entry in the bot’s registry plus a repo like this one.

👥 One script per audience

There are 12: pessoacomum · jovens · profissionais · mulheres · empreendedores · tecnicos · 40mais · 60mais · educadores · criadores · recolocacao · familia. Each has its own channel and hook.

⏸️ Two gates, in the right places

The bot stops after the text (before spending on the avatar) and after the download (before spending on the render). Disagreeing with a script at the gate costs a rewritten text, not 12 avatars recorded by hand.

🧊 Frozen at creation

flow.json, prompts, and options are frozen when the workflow is created. Editing applies to FUTURE ones — not even /refazer gets the change.

How it works

The cycle, on one screen

Four phases and two gates. The gates are the "pausa_apos": true of the phases texto e baixar in the flow.json — the pipeline stops there on its own and only proceeds when you approve.

1. text→ ⏸️ /aprovar A#N→ 2. avatar→ 2.5 download→ ⏸️ you review→ 3. reel→ delivered in the channel

1 · text

One script per audience, recorded in textos/A<N>/<publico>.md. The chat sends you each script along with the video’s exact TITLE. Gate 1 right after.

2 · avatar

Normally you, in the HeyGen studio. There are 5 routes for this phase — the table below.

2.5 · download

Finds the video in HeyGen by title and downloads the MP4 — with the caption burned in if the studio recorded it with one, clean if not. 90-minute window. Gate 2 afterward.

3 · reel

Build the 9:16 reel (an impactful cover with the audience’s trigger) and delivery directly into that audience’s channel folder. There is no separate publishing phase.

Phase 2 has 5 routes — only one runs per flow

The four automated ones are phases with the key opcional in the flow.json. The fifth is the absence of all of them.

✋ manual (default)

No flags are enabled. You record in HeyGen and the bot has no idea how — it just waits for the /aprovar.

🖥️ studio

Phase estudio (opcional: estudio). The bot opens/prepares the studio; you finish.

🔌 api

Phase gerar (opcional: api). The BOT generates — using HeyGen’s prepaid wallet, ~US$ 1 per minute.

🎟️ credits

Phase gerar-creditos (opcional: creditos). Same generation, consuming credits.

🤖 browser

Phase navega-avatar (opcional: navega). LLM agent cloning the TEMPLATE-AVATAR. The most expensive route: ~17.8k tokens per audience, ~214k in workflow 12.

🔑 what ties the 5 together

O title. For any route, the video must be named A<N>-<publico>-v1. On the manual route, this is 100% your responsibility.

Documented in chat: | api e | estudio. The flags for creditos e navega exist as a phase in the flow.json but aren’t in the HELP.md — confirm before using.

Reel templates

Four layouts, and no one chooses at render time

Are stored in templates/. All 1080×1920, background #0E1116, amber accent #F5A623.

stacked-cover (default)

Top: 704px image + headline. Middle: 608px avatar (audio). Base: 608px text panel (hook). The high-impact cover — the original format.

stacked-explanatory

Same, but the base becomes the explainer video of that speech, muted and on a loop. Use when the explainer exists as a video.

diptych

Half and half: 960px image on top, 960px avatar on the bottom. No third track. Good for myth vs. reality and comparisons, where the image carries the contrast.

full-image

The image fills the frame; the avatar goes into a crop in the top-right. The footer is prohibited: the network interface covers the bottom corner, and the avatar would not appear.

The layout follows from the text you approved

Phase 1 writes the line Formato escolhido: in each <publico>.md (the prompt’s ZERO STEP), and the templates/mapa.json translate. In the real A#19, this gave 9 different formats for 12 audiences — a real variation, with no one deciding anything at render time.

# precedence (resolved by preparar.py)
--template explícito
  › template do ALVO no flow.json
    › mapa.json
      › template da raiz do flow.json

headline and hook are required

Each track declares a fonte: imagens, avatar, texto or explicativo. A headline goes at the top; the hook goes in the base panel. Layout with base and hook missing = black base — it was A#23, with hook in 0 of 8 images. That’s why the prompt says to write hook always, even in layouts without a base.

Prerequisites

What needs to be live

This repo doesn’t run anything on its own — the bot reads it. What you need is the bot running, a HeyGen account, and the channel folders.

inemaccbot running

The bot runs the flow, with this repo declared in config/fluxos.json.

# in the authorized chat
/fluxos
/promoavatar help

HeyGen account

This is where you record the avatars, during the break between phases 1 and 2. The download matches by the video's exact name.

# the title is the contract
A<N>-<publico>-v1
# e.g.: A8-mulheres-v1

Channel folder

The audience’s channel becomes a folder based on a derived rule — the path isn’t written down anywhere.

# create a new channel
mkdir -p ~/projetos/yt-pub-lives33/imports/videos
User guide · step by step

From topic to delivered reel

All commands are typed in the Telegram chat with the authorized bot.

1

Check in shadow mode before spending

| sombra prints phase × audience × queue × task and doesn’t queue anything. Running all 12 audiences means 12 avatars recorded by hand — normally, you filter.

/promoavatar Não comece aprendendo ferramentas | sombra
2

Create the flow

All 12 go through without filtering. To test cheaply, use just one audience. The | e o -- coexist—but a field entered without either one is REJECTED; it doesn’t silently become a topic.

/promoavatar <assunto>                        # the 12 audiences
/promoavatar <assunto> | alvos=mulheres       # cheap for testing
/promoavatar <assunto> --alvo=jovens --alvo=40mais
/promoavatar <assunto> | legenda              # default is WITHOUT caption
3

Write your position on the topic

An open-ended topic (“is this good or bad?”) made the agent explain both sides and end lukewarm — and nobody comments on a fence-sitter. Now the prompt tells it to take a clear side and say which one in the summary. The position you specify takes precedence over its own, so writing your own is still the best approach.

# best: your position + a concrete fact + the question
# what you want in the comments
4

Review the scripts at the gate

The bot sends each script in the chat and STOPS. This is where disagreeing is cheap: /refazer costs one text, not a render.

/status A#7              # phase × audience, and the titles
/refazer A#7 mulheres    # just the audience that didn’t turn out well
5

Record the avatars using the exact title

In the studio, the video must be named exactly A<N>-<publico>-v1. The download matches by exact string equality: a different name means the video is never found, and the phase expires in 90 minutes. The chat sends you the title ready to use precisely so you don’t have to type it from memory.

A7-mulheres-v1
A7-jovens-v1
# the avatar caption is decided HERE: record with it, and the reel
# it comes with it — and there’s no way to remove it afterward. In that case,
# create the flow WITHOUT | caption, or you’ll get two.
6

Open the gate

“I’m done with my part.” The bot then downloads the videos, assembles the reels, and delivers them to each channel.

/aprovar A#7     # synonyms: /pronto, /aprovado, /ok
7

Follow it through to the final link

If an audience fails, redo only that one—its attempts are reset. Canceling applies to the pipeline: what has already been created in the studio stays there.

/status A#7
/refazer A#7 mulheres
/cancelar A#7 [publico]
Where to change what

Domain, bot, and skill are different layers

The rule: decisions about the audience or campaign belong in this repo; brand visual identity belongs in the skill. The skill is global — changing it affects EVERY reel, including those triggered directly in chat.

Lives here (domain)

The channel and hook for each audience, how the scripts are written, what this pipeline asks of the reel, the closing CTA clip, and help from the chat.

flow.json              # targets.<audience>.channel / .trigger
prompts/fase1-texto.md # how the scripts are written
cta/cta-9x16.mp4       # replace the file
HELP.md                # /promoavatar help

Lives elsewhere

How the reel is ASSEMBLED (colors, fonts, positions, SFX) is handled by the global skill; queues, timeouts, model, and effort are handled by the bot.

# skill (global — changes the entire brand)
~/.claude/skills/reel-edita-inema/SKILL.md
# bot
inemaccbot/prompts/reel.md
inemaccbot/config/skills.json

Improve the reel, from cheapest to most expensive: replace the clip from cta/ → adjust the entrega of the flow.json → and only then edit the skill.

For whom, and where it lives

The domain says for whom (mulheres has "canal": "lives4"); the bot knows where — always ~/projetos/yt-pub-<canal>/imports/videos. Never put a path in the flow.json.

Legend: what’s ours and what isn’t

Who decides the avatar’s caption is the studio: the download grabs the subtitled version when available, and the clean version when it isn’t. The option | legenda is another one — it's the one our editor draws. Turning both on makes two; and burned-in captions are framed for 16:9, with no way to remove them afterward.

How to change it

Prompts, templates, targets, and destination

The four things you’ll most want to change. They all follow the same constraint: what matters is what existed when the flow was created — editing applies to FUTURE ones, and not even /refazer gets the change.

1 · The prompt (how the scripts are written)

File: prompts/fase1-texto.md. It is the entire document that phase 1 delivers to the agent — FIXED CONTEXT, STEP ZERO, HOOK WORKSHOP, the 16 WRITING RULES, and the output contract.

Two pitfalls: the 11 formats from STEP ZERO are keys of the templates/mapa.json — renamed it here, rename it there. And the five variables injected by the bot must not disappear:

{{input}}    # the topic
{{publicos}} # the flow’s REAL targets
{{pasta}}    # where to record (absolute)
{{ref}} {{saida}}

The skill inemaclub-textos provides the structure of the file; this prompt provides the rules and overrides the skill. Change the structure in the skill—and it applies to everyone.

2 · The templates (the reel layout)

a) change what a layout looks like → edit templates/<nome>.json. Three rules:

y + altura of the tracks have to close 1920 (y is absolute positioning; they don't stack by themselves — a missing strip means a black strip). The fonte is what feeds the strip: imagens · avatar · texto (o hook) · explicativo. E escurecer is the veil beneath the headline: too low, and the text disappears against the light parts of the photo.

b) change which format goes into which layout → templates/mapa.json. The keys exist with and without accents, on purpose:

"mito versus realidade": "diptico",
"comparação": "diptico",
"comparacao": "diptico"

A format outside the map falls back to the root default — don’t invent a layout. c) To lock in an audience’s layout, use the field template within the target.

3 · The targets (the audiences)

File: flow.json, key alvos.

"empreendedores": {
  "canal": "lives1",
  "gatilho": "Transforme IA em redução de custos…",
  "template": "diptico"   # optional
}

O gatilho is that audience's pain point (prompt rule 2 says to use it). Adding means one more entry; removing means delete it. To run just a few without changing anything, use --alvo= during creation.

The key is a contract, not a label: it becomes the file textos/A<N>/<publico>.md, the title A<N>-<publico>-v1, o --alvo of the reel and the seed-key of the images. Lowercase, no accents, no spaces, and without a hyphen — that's why pessoa-comum became pessoacomum.

4 · The destination (where the reel is delivered)

There’s no path written down anywhere: the destination is derived from the canal of the audience.

<canal> → ~/projetos/yt-pub-<canal>/imports/videos

To change the channel = edit alvos.<publico>.canal. Creating a new channel = creating the folder, that’s all:

mkdir -p ~/projetos/yt-pub-lives33/imports/videos

Two audiences can share the same channel. Changing the rule (the base folder, the imports/videos) isn’t here — é o destinos.ts of the bot, and changes all flows. For a one-off reel outside the flow, use montar-reel.py --saida <caminho>.

Not from this repo

What belongs to inemaccbot, not here

This repo is domain: it declares the pipeline. Whoever runs é o inemaccbot. Looking here for something that belongs there is the most common waste of time — the list below is exactly what no is in this repo.

The chat commands

/promoavatar, /status, /aprovar, /refazer, /cancelar and the | e -- belong to the bot. Here, only the HELP.md, which is the HELP TEXT — not its code.

The phase engine

Queues (texto, io, navegador, render), retries, timeouts, freezing during creation, and the very concept of a gate. The flow.json only declares; the bot is the one that follows it (inemaccbot/config/skills.json).

The tasks heygen.*

heygen.gerar, heygen.estudio, heygen.baixar are names of functions that live in the bot (inemaccbot/src/fila/tarefas/heygen.ts) — including the escolherUrl, which decides between the captioned and clean MP4.

The flow status

state/artefatos/fluxos/A<N>/ is in the BOT repo, not here. That’s where the downloaded avatars land.

The channel folders

~/projetos/yt-pub-<canal>/imports/videos is a rule derived by the bot. The path isn't written anywhere — only the channel name exists here.

How the reel is ASSEMBLED

Colors, fonts, positions, silence trimming, and SFX are handled by the global skill ~/.claude/skills/reel-edita-inema/SKILL.md. Changing that affects EVERY reel for the brand, including those triggered directly in chat.

The rule of thumb: if the answer changes the behavior of all the flows, not from here. If only promoavatar changes, it’s from here.

Parameters

The reel engines, at your fingertips

Phase 3 calls scripts/montar-reel.py. You can run it directly, outside the bot — useful for redoing a reel without spending a workflow.

montar-reel.py

--avatar        # required: the HeyGen MP4
--ws            # required: the reel workspace
--alvo          # audience; becomes the image seed-key
--textos        # the <audience>.md (## IMAGES section)
--template      # layout override (takes precedence over everything)
--flow --mapa   # where to resolve the template and map
--qualidade     # high (default) · standard · draft
--cta --sem-cta # the closing clip
--pular-preparo # reuses the --ws setup
--saida         # MP4 destination

preparar.py has the same ones, plus --explicativo, --sem-imagens, --sem-transcricao e --sem-montar.

Examples

# standard reel — layout comes from the map
python3 scripts/montar-reel.py \
  --avatar A34-jovens-v1.mp4 \
  --ws /tmp/ws-A34-jovens --alvo jovens \
  --textos textos/A34/jovens.md --flow flow.json

# cheap draft, just to check the framing
... --qualidade draft --sem-cta

# force the layout, ignoring the map
... --template imagem-plena

# change the CTA without regenerating the image
... --pular-preparo --saida saida/A34-jovens.mp4