Changelog de produto
Um e-mail recorrente que conta aos usuários o que mudou no produto, escrito como uma pessoa, enviado com segurança e depois reutilizado para posts sociais. O e-mail é a fonte da verdade para cada edição; todos os outros canais são derivados dele.
Como esta skill funciona
- Pergunte uma vez, no início. Na primeira execução (ou quando a configuração estiver faltando ou um valor necessário estiver em branco), faça as perguntas de configuração em um lote curto, escreva
config.yamle continue sem mais perguntas. - Então faça tudo. No modo
autonomy.mode: autonomous(o padrão), você seleciona os itens, rascunha, faz lint, auto-revisa, captura screenshots, envia teste, lê o teste de volta, constrói a audiência, executa um dry run, envia para todos, verifica, puxa os opt-outs, escreve e publica os posts sociais e arquiva. Nenhuma etapa de aprovação. Cada ação externa passa por um portão de auto-revisão primeiro. - Dinheiro é a única aprovação. Qualquer coisa que custe dinheiro aguarda o "sim" explícito do usuário no chat, em todos os modos: um envio que excederia a cota incluída no plano do provedor (
provider.monthly_quota), atualizar o plano do provedor, comprar um domínio, ferramentas ou licenças pagas (por exemplo, uma licença paga da Remotion Company). Nada mais precisa de um "sim". - Paradas forçadas não são aprovações. Elas interrompem aquela ação específica. Termine o que for seguro e relate. Veja Paradas forçadas.
- Relate no final. Um resumo: o assunto e o texto completo que foram enviados, contagens (na fila, enviados, falhos, excluídos, cancelados), amostra de entrega, resultado da leitura de retorno do teste, cada post com seu link, o que foi pulado e por quê.
- Modo
reviewmantém os antigos portões de aprovação por ação. Veja Modo de revisão.
Cada script é um dry run por padrão. Envios reais precisam de flags explícitas (--send, --yes-send-to-everyone, --yes-send-broadcast). No modo autônomo, você mesmo as passa, e só depois que o portão de auto-revisão correspondente for aprovado. Nunca envie e-mails ad hoc fora dos scripts, porque os scripts são o que garantem a exclusão de opt-outs.
Etapa 0: carregar a configuração
- Procure por
config.yamlna raiz do projeto atual, depois na pasta desta skill (ou$CHANGELOG_CONFIG). - Se não houver, copie
config.example.yamlparaconfig.yamlna raiz do projeto e pergunte, em uma única mensagem:- nome do produto, site, URLs do dashboard e da documentação
- nome e cargo do remetente, endereço de envio, reply-to
- destinatários de teste (nomes e endereços)
- provedor: o ID da audiência do Resend, a variável de ambiente que contém a chave da API e os e-mails mensais incluídos no plano, se souberem (
provider.monthly_quota, opcional) - modo de cancelamento (
broadcastouown_endpoint) - hospedagem de imagens: para onde vão os screenshots e sua URL base pública
- opcional: cadência, cor do link da marca e como postar em cada canal social (
social.post_via) Não adivinhe nenhum deles. Escreva as respostas noconfig.yamle continue.
- Qualquer valor ainda definido como um placeholder em MAIÚSCULAS ou um endereço
yourproduct.exampleé considerado ausente. - Leia
autonomy.mode(padrãoautonomous),voice.notesevoice.banned_phrases. As configurações de voz substituem os padrões nas referências. - Segredos nunca vão para a configuração ou para o chat. Chaves de API e a chave secreta de cancelamento de assinatura ficam em variáveis de ambiente.
Edições posteriores não perguntam nada. A única pergunta opcional é "Algo foi enviado fora do git desta vez (preços, parcerias, documentação)?" Se o usuário não estiver por perto, pule e trabalhe a partir do git e das notas de release.
Etapa 1: escolher o que incluir
Leia references/voice-and-format.md primeiro.
- Encontre a última edição em
paths.issues_dir(pastas nomeadasNNN-YYYY-MM-DD-slug). A mais nova é o modelo para a próxima. - Reúna candidatos desde essa data:
git log --since=<data> --no-merges --oneline, pull requests mesclados, notas de release, além de qualquer coisa que o usuário mencionou. - Mantenha apenas mudanças visíveis ao usuário. 3 a 5 itens, classificados por utilidade para o leitor. Comece com a mudança mais útil, aquela que a maioria dos leitores pode usar hoje.
- Verifique cada afirmação com o produto e site ao vivo. Nunca invente um recurso, um número ou uma data. Se uma afirmação não puder ser verificada, descarte o item.
- Escreva a lista e a ordem no arquivo
NOTES.mdda edição e prossiga.
Etapa 2: rascunhar o e-mail
Crie paths.issues_dir/NNN-YYYY-MM-DD-slug/ e escreva:
subject.txt: focado no benefício e no produto primeiro ("Agora você pode exportar relatórios para o Sheets"). Nunca "Changelog #N".email.html: a partir de templates/email.html (ou da última edição), preenchendo todos os marcadores[[...]]com a configuração e o conteúdo.email.txt: as mesmas palavras em texto simples com URLs puras.
Mantenha {{NAME}} e {{UNSUB_URL}} como placeholders literais; os scripts de envio os preenchem por destinatário.
Regras principais (detalhes na referência):
Olá {{NAME}},, depois a linha de abertura de uma linha da configuração, em seguida direto ao item principal.- Cada item: o que mudou, depois por que é importante, em frases fluidas. Sem bullet points rotulados.
- Frases curtas, palavras simples, "agora você pode" em vez de "nós construímos".
- No máximo um destaque em negrito por parágrafo.
- No máximo um screenshot por item; o item principal sempre recebe um, e ele linka para o recurso.
- HTML minimalista e estilizado: wrapper de 600px, fonte do sistema, estilos inline, sem botões ou banners.
- Sem "animados em anunciar" e o resto da lista proibida. Sem travessões ou travessões longos se
voice.no_dashesestiver ativo. - P.S. casual opcional ("A propósito, ...").
- Assinatura da configuração. A linha de cancelamento é a última linha, pequena e discreta.
Etapa 3: screenshots
Siga references/screenshots.md: prepare um estado real em uma conta de demonstração (sem dados de clientes), capture em 2x com Playwright, recorte para o painel, nomeie os arquivos <issue>-<slug>.png, hospede-os em paths.image_base_url e confirme que cada URL retorna 200 antes de qualquer envio.
Etapa 4: lint e auto-revisão (portão 1: cópia)
python3 scripts/lint_email.py ISSUE_DIR --check-links
Corrija todas as falhas e depois percorra esta lista de verificação. Corrija e reexecute até que cada item passe.
-
lint_email.py --check-linkssai com código 0. - Leia de cima para baixo como um cliente ocupado: a mudança mais útil está clara em 5 segundos.
- Cada item é algo que um usuário pode ver ou usar hoje.
- Cada afirmação, número e link corresponde ao produto e site ao vivo.
- Nada parece um press release; soa como o remetente falando.
- Assunto com menos de 60 caracteres, focado no benefício, sem "Changelog #N".
- Cada imagem tem texto alternativo e sua URL hospedada retorna 200 (ou será implantada antes da Etapa 7).
Etapa 5: envio de teste e leitura de retorno (portão 2: cópia entregue)
python3 scripts/send_test.py ISSUE_DIR --send # apenas para test_recipients, prefixo [TEST]
python3 scripts/send_test.py ISSUE_DIR --readback # um minuto depois
Cópias de teste vão apenas para test_recipients e incorporam imagens inline se ainda não estiverem implantadas. --readback puxa cada mensagem entregue da API do provedor e verifica o último evento, o assunto, a saudação renderizada e a linha de cancelamento, e que nenhum placeholder sobreviveu. Leia o HTML retornado você mesmo uma vez. Se algo estiver errado, corrija, refaça lint, reteste.
No modo own_endpoint, abra o link de cancelamento da cópia de teste, confirme que o opt-out foi registrado e depois reassine o endereço de teste.
Etapa 6: construir a audiência
Detalhes completos em references/sending.md.
- Construa
ISSUE_DIR/audience.jsonldo zero a partir do banco de dados do usuário ({"email", "name"}por linha). Nunca reutilize uma lista antiga. - Construa
ISSUE_DIR/unsubs.txta partir da tabela de opt-out. No modo broadcast, primeiro executepython3 scripts/broadcast_unsubs.py --out <issues_dir>/unsubs.txte copie-o. - Fundadores, contas de teste e demonstração estão na configuração
exclude; eles recebem cópias de teste.
Etapa 7: enviar para todos (portão 3: audiência)
Escolha o caminho a partir de unsubscribe.mode. Execute o dry run primeiro e verifique as contagens.
Modo Broadcast (provedor hospeda o cancelamento):
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl # plano
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl --apply # upsert contatos
python3 scripts/broadcast_create.py ISSUE_DIR --create # apenas rascunho
python3 scripts/broadcast_create.py --send BROADCAST_ID # mostra a contagem de destinatários, recusa
python3 scripts/broadcast_create.py --send BROADCAST_ID --yes-send-broadcast
Modo de endpoint próprio (links assinados para sua própria página):
python3 scripts/blast.py ISSUE_DIR # dry run com contagens
python3 scripts/blast.py ISSUE_DIR --send --yes-send-to-everyone
Lista de verificação da audiência, tudo deve passar antes da flag de envio:
- Os portões 1 e 2 passaram nesta cópia exata (nada mudou desde a leitura de retorno).
- A lista de opt-out foi puxada ou construída nesta execução e está não vazia se o produto já teve algum cancelamento.
- As contagens do dry run fazem sentido: enfileirados está próximo de usuários menos opt-outs menos excluídos; nenhum salto surpresa em relação ao
NOTES.mdda última edição. - Modo broadcast:
broadcast_sync.pynão relatou contatos obsoletos inesperados. - Cada URL de imagem hospedada retorna 200.
- Cota: se
provider.monthly_quotaestiver definido, passe--sent-this-month N(some as contagens de envio deste mês noNOTES.mdda edição mais outro e-mail no mesmo plano, se conhecido). Se o script disser que o envio excederia a cota, pare e pergunte ao usuário; passe--yes-over-quotaapenas após o "sim" explícito dele.
O envio individual é retomável: se parar, reexecute o mesmo comando e ele pula os endereços já enviados.
Etapa 8: verificar e sincronizar
- Individual:
python3 scripts/verify_delivery.py ISSUE_DIR --sample 40. Broadcasts:GET /broadcasts/{id}através da API do provedor ou do painel. - Puxe os opt-outs de volta após um dia (
broadcast_unsubs.py). Se o usuário mantiver os opt-outs em seu próprio banco de dados e você tiver acesso de escrita, importe-os; caso contrário, informe no relatório.
Etapa 9: posts sociais
Escreva a partir do e-mail final, seguindo references/social.md:
- X thread: tweet de gancho começando com
social.hook_prefixque nomeia cada item, sem link, screenshot mais forte. Depois um tweet numerado por item com seu link e screenshot. Plus uma citação pessoal de uma linha para um fundador. - LinkedIn: um post, gancho na primeira linha, um parágrafo curto por item, texto simples, galeria de imagens.
- Comunidade (Discord ou Slack): linha de abertura, uma linha por item com um link puro, o link da thread quando estiver ao vivo.
Máximo de 4 a 5 itens, sem avisos de política, afirmações correspondem ao site ao vivo. Execute as mesmas verificações de frases proibidas e travessões em social.md (lint_email.py o lê). Depois, para cada canal com uma ferramenta em social.post_via, publique você mesmo e registre a URL do post. Para canais sem ferramenta, entregue ao usuário o texto final e as imagens. A postagem de citação do fundador é de uma conta pessoal, portanto, entregue-a a menos que uma ferramenta para essa conta esteja configurada.
Etapa 10: vídeo (opcional, apenas sob solicitação)
Se o usuário pedir, siga references/video.md: um reel de movimento de 15 a 25 segundos a partir dos screenshots da edição, sem slide de título, manchete grande por item, screenshots dominam, encerramento com a URL do produto. O Remotion precisa de uma licença paga da empresa acima de um certo tamanho de equipe; isso é uma questão de dinheiro para o usuário.
Etapa 11: arquivar e relatar
Na pasta da edição, mantenha: subject.txt, email.html, email.txt, os screenshots, social.md, test_results.jsonl e um NOTES.md com a lista de itens, data de envio, caminho usado, tamanho da audiência, contagens de enviados e falhos, ID do broadcast, URLs dos posts e cancelamentos após um dia. Se o produto tiver uma página pública de changelog web (product.changelog_page) e sua fonte estiver neste repositório, adicione a edição lá também. Quando o usuário corrigir a voz ou formatação, adicione a lição a voice.notes na configuração para que a próxima edição comece a partir dela. Em seguida, envie o resumo descrito no início.
Paradas forçadas
Pare essa ação, termine tudo o que for seguro e relate o que parou e por quê. Nunca contorne uma parada.
- Falhas de lint que você não consegue corrigir.
- Um link ou imagem quebrados que você não consegue corrigir (ainda não 200 após implantar ou corrigir).
- A leitura de retorno do teste falha e você não consegue encontrar a causa.
- A filtragem de opt-out falha: a lista de opt-out não pode ser puxada ou construída, a consulta de audiência erra, ou um script recusa por causa de
unsubs.txt. Nunca envie sem ela. - Erros do provedor acima do limite no meio do envio (
blast.pysai com código 2 emprovider.max_failuresouprovider.max_failure_rate). Relate o erro; o envio é retomável uma vez corrigido. - Um envio que excederia a cota, uma atualização de plano ou qualquer outro gasto: pergunte ao usuário (regra do dinheiro).
- Solicitações de login, 2FA ou reautenticação, CAPTCHAs, avisos da plataforma ou notificações de limite de taxa em uma ferramenta de postagem, formulários de pagamento ou faturamento.
- Qualquer coisa que viole a lei de opt-out ou anti-spam (linha de cancelamento faltando, reassinando alguém que fez opt-out).
- Uma afirmação que você não pode verificar. Descarte-a em vez de adivinhar.
Modo de revisão
Com autonomy.mode: review, os três portões também precisam da aprovação do usuário:
- Portão de rascunho. Mostre o assunto, o e-mail e a lista de itens no chat. Nada é enviado até que o usuário aprove a cópia.
- Portão de teste. Envie apenas para
test_recipients. O usuário lê em uma caixa de entrada real e diz "vai" ou dá correções. - Portão de envio. Mostre as contagens do dry run (enfileirados, excluídos, cancelados). Envie para todos apenas quando o usuário disser "sim" para aquele envio específico no chat. Um "sim" para uma etapa anterior não é um "sim" para esta.
Os posts sociais são mostrados antes de postar, e a lista de itens é proposta antes de escrever. Nunca passe uma flag de envio antes do portão correspondente.
Respondendo "quantos assinantes?"
Assinantes = usuários menos opt-outs. Conte os opt-outs da tabela de opt-out do usuário ou de unsubs.txt (no modo broadcast, execute broadcast_unsubs.py primeiro). Informe também a taxa de cancelamento da última edição.
Arquivos
config.example.yaml: cada valor específico da empresa, além deautonomy,provider.monthly_quotaesocial.post_via.templates/email.html,templates/email.txt: o layout inicial.scripts/:lint_email.py,send_test.py(com--readback),broadcast_sync.py,broadcast_create.py,broadcast_unsubs.py,blast.py,verify_delivery.pyebuild_audience.sqloubuild_audience.py.references/:voice-and-format.md,screenshots.md,sending.md,social.md,video.md.- O diretório de edições (
issues_dir): uma pasta por edição nomeadaNNN-YYYY-MM-DD-slug, cada uma contendosubject.txt,email.html,email.txt,audience.jsonl,unsubs.txt(se aplicável), screenshots (<issue>-<slug>.png),social.md,test_results.jsonl,NOTES.mde opcionalmentebroadcast_id.txt.
Variáveis de ambiente
RESEND_API_KEY: chave de API do Resend.UNSUB_SIGNING_SECRET: segredo para assinar links de cancelamento no modo de endpoint próprio (se usado).