Journal des modifications produit
Un e-mail récurrent qui informe les utilisateurs de ce qui a changé dans le produit, rédigé comme une personne, envoyé en toute sécurité, puis réutilisé pour les posts sociaux. L'e-mail est la source de vérité pour chaque numéro ; tous les autres canaux en sont dérivés.
Comment cette compétence fonctionne
- Poser une fois, au début. Lors de la première exécution (ou lorsque la configuration est manquante ou qu'une valeur nécessaire est vide), posez les questions de configuration en un seul lot court, écrivez
config.yaml, puis continuez sans autres questions. - Ensuite, tout faire. Dans
autonomy.mode: autonomous(par défaut), vous sélectionnez les éléments, rédigez, vérifiez le style, vous auto-relisez, capturez des captures d'écran, effectuez un envoi test, lisez le test en retour, construisez l'audience, faites un test à blanc, envoyez à tout le monde, vérifiez, récupérez les désabonnements, écrivez et publiez les posts sociaux, et archivez. Pas d'étape d'approbation. Chaque action externe passe d'abord par une porte d'auto-vérification. - L'argent est la seule approbation. Tout ce qui coûte de l'argent attend le « oui » explicite de l'utilisateur dans le chat, dans tous les modes : un envoi qui dépasserait le quota inclus du plan du fournisseur (
provider.monthly_quota), la mise à niveau du plan du fournisseur, l'achat d'un domaine, des outils ou licences payants (par exemple une licence d'entreprise Remotion payante). Rien d'autre n'a besoin d'un « oui ». - Les arrêts d'urgence ne sont pas des approbations. Ils interrompent cette action spécifique. Terminez tout ce qui est sûr, puis signalez. Voir Arrêts d'urgence.
- Rapport à la fin. Un résumé : l'objet et le texte complet qui sont partis, les comptes (en file d'attente, envoyés, échoués, exclus, désabonnés), un échantillon de livraison, le résultat de la relecture du test, chaque post avec son lien, tout ce qui a été sauté et pourquoi.
- Mode
reviewconserve les anciennes portes d'approbation par action. Voir Mode Review.
Chaque script est un test à blanc par défaut. Les envois réels nécessitent des indicateurs explicites (--send, --yes-send-to-everyone, --yes-send-broadcast). En mode autonome, vous les passez vous-même, et seulement après que la porte d'auto-vérification correspondante est passée. N'envoyez jamais d'e-mails ad hoc en dehors des scripts, car ce sont les scripts qui appliquent les désabonnements.
Étape 0 : charger la configuration
- Cherchez
config.yamldans la racine du projet actuel, puis dans le dossier de cette compétence (ou$CHANGELOG_CONFIG). - S'il n'y en a pas, copiez
config.example.yamlversconfig.yamlà la racine du projet et demandez, en un seul message :- nom du produit, site web, URL du tableau de bord et de la documentation
- nom et titre de l'expéditeur, adresse d'envoi, adresse de réponse
- destinataires de test (noms et adresses)
- fournisseur : l'ID d'audience Resend, la variable d'environnement contenant la clé API, et le nombre d'e-mails mensuels inclus du plan s'ils le connaissent (
provider.monthly_quota, facultatif) - mode de désabonnement (
broadcastouown_endpoint) - hébergement d'images : où vont les captures d'écran et leur URL de base publique
- facultatif : cadence, couleur du lien de la marque, et comment poster sur chaque réseau social (
social.post_via) Ne devinez aucun d'entre eux. Écrivez les réponses dansconfig.yaml, puis continuez.
- Toute valeur toujours définie sur un espace réservé EN MAJUSCULES ou une adresse
votreproduit.examplecompte comme manquante. - Lisez
autonomy.mode(par défautautonomous),voice.notesetvoice.banned_phrases. Les paramètres vocaux remplacent les valeurs par défaut dans les références. - Les secrets ne vont jamais dans la configuration ou dans le chat. Les clés API et la clé secrète de signature de désabonnement résident dans les variables d'environnement.
Les numéros ultérieurs ne demandent rien. La seule question facultative est « Quoi de neuf en dehors de git cette fois-ci (tarifs, partenariats, documentation) ? » Si l'utilisateur n'est pas là, sautez-la et travaillez à partir de git et des notes de version.
Étape 1 : choisir ce qui entre
Lisez d'abord references/voice-and-format.md.
- Trouvez le dernier numéro dans
paths.issues_dir(dossiers nommésNNN-YYYY-MM-DD-slug). Le plus récent est le modèle pour le suivant. - Rassemblez les candidats depuis cette date :
git log --since=<date> --no-merges --oneline, pull requests fusionnées, notes de version, plus tout ce que l'utilisateur a mentionné. - Gardez uniquement les modifications visibles par l'utilisateur. 3 à 5 éléments, classés par utilité pour le lecteur. Commencez par le changement le plus utile, celui que la plupart des lecteurs peuvent utiliser aujourd'hui.
- Vérifiez chaque affirmation par rapport au produit en direct et au site web. N'inventez jamais une fonctionnalité, un nombre ou une date. Si une affirmation ne peut pas être vérifiée, supprimez l'élément.
- Écrivez la liste et l'ordre dans
NOTES.mddu numéro et passez à l'étape suivante.
Étape 2 : rédiger l'e-mail
Créez paths.issues_dir/NNN-YYYY-MM-DD-slug/ et écrivez :
subject.txt: axé sur les avantages et le produit en premier (« Vous pouvez maintenant exporter les rapports vers Sheets »). Jamais « Changelog #N ».email.html: à partir de templates/email.html (ou du dernier numéro), en remplissant chaque marqueur[[...]]à partir de la configuration et du contenu.email.txt: les mêmes mots en texte brut avec des URL nues.
Conservez {{NAME}} et {{UNSUB_URL}} comme espaces réservés littéraux ; les scripts d'envoi les remplissent par destinataire.
Règles de base (détails dans la référence) :
Bonjour {{NAME}},, puis la ligne d'ouverture de la configuration, puis directement dans l'élément principal.- Chaque élément : ce qui a changé, puis pourquoi c'est important, en phrases fluides. Pas de puces étiquetées.
- Phrases courtes, mots simples, « vous pouvez maintenant » plutôt que « nous avons construit ».
- Au plus un accent en gras par paragraphe.
- Au plus une capture d'écran par élément ; l'élément principal en a toujours une, et elle renvoie à la fonctionnalité.
- HTML minimal stylé : wrapper 600px, police système, styles en ligne, pas de boutons ni de bannières.
- Pas de « ravis d'annoncer » et le reste de la liste interdite. Pas de tirets cadratins ou demi-cadratins si
voice.no_dashesest activé. - P.S. facultatif et décontracté (« Au fait, ... »).
- Signature de la configuration. La ligne de désabonnement est la dernière ligne, petite et atténuée.
Étape 3 : captures d'écran
Suivez references/screenshots.md : mettez en scène un état réel sur un compte de démonstration (pas de données client), capturez en 2x avec Playwright, recadrez sur le panneau, nommez les fichiers <issue>-<slug>.png, hébergez-les à paths.image_base_url, et confirmez que chaque URL renvoie un 200 avant tout envoi groupé.
Étape 4 : vérification du style et auto-relecture (porte 1 : copie)
python3 scripts/lint_email.py ISSUE_DIR --check-links
Corrigez chaque échec, puis parcourez cette liste de vérification. Corrigez et réexécutez jusqu'à ce que chaque case soit cochée.
-
lint_email.py --check-linksse termine avec le code 0. - Lisez de haut en bas comme un client pressé : le changement le plus utile est clair en 5 secondes.
- Chaque élément est quelque chose qu'un utilisateur peut voir ou utiliser aujourd'hui.
- Chaque affirmation, nombre et lien correspond au produit et au site en direct.
- Rien ne ressemble à un communiqué de presse ; cela ressemble à l'expéditeur qui parle.
- Objet de moins de 60 caractères, axé sur les avantages, pas de « Changelog #N ».
- Chaque image a un texte alternatif et son URL hébergée renvoie un 200 (ou sera déployée avant l'étape 7).
Étape 5 : envoi test et relecture (porte 2 : copie livrée)
python3 scripts/send_test.py ISSUE_DIR --send # uniquement aux test_recipients, préfixe [TEST]
python3 scripts/send_test.py ISSUE_DIR --readback # une minute plus tard
Les copies de test vont uniquement aux test_recipients et intègrent les images en ligne si elles ne sont pas encore déployées. --readback récupère chaque message livré depuis l'API du fournisseur et vérifie le dernier événement, l'objet, la salutation rendue et la ligne de désabonnement, et qu'aucun espace réservé n'a survécu. Lisez également le HTML renvoyé vous-même une fois. Si quelque chose ne va pas, corrigez, revérifiez le style, re-testez.
En mode own_endpoint, ouvrez le lien de désabonnement de la copie de test, confirmez que le désabonnement est enregistré, puis réabonnez l'adresse de test.
Étape 6 : construire l'audience
Détails complets dans references/sending.md.
- Construisez
ISSUE_DIR/audience.jsonlà partir de la base de données de l'utilisateur ({"email", "name"}par ligne). Ne réutilisez jamais une ancienne liste. - Construisez
ISSUE_DIR/unsubs.txtà partir de la table de désabonnement. En mode broadcast, exécutez d'abordpython3 scripts/broadcast_unsubs.py --out <issues_dir>/unsubs.txtet copiez-le. - Les fondateurs, les comptes de test et de démonstration sont dans la configuration
exclude; ils reçoivent des copies de test à la place.
Étape 7 : envoyer à tout le monde (porte 3 : audience)
Choisissez le chemin à partir de unsubscribe.mode. Exécutez d'abord le test à blanc et vérifiez les comptes.
Mode Broadcast (le fournisseur héberge le désabonnement) :
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl # plan
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl --apply # mettre à jour les contacts
python3 scripts/broadcast_create.py ISSUE_DIR --create # brouillon uniquement
python3 scripts/broadcast_create.py --send BROADCAST_ID # montre le nombre de destinataires, refuse
python3 scripts/broadcast_create.py --send BROADCAST_ID --yes-send-broadcast
Mode propre endpoint (liens signés vers votre propre page) :
python3 scripts/blast.py ISSUE_DIR # test à blanc avec comptes
python3 scripts/blast.py ISSUE_DIR --send --yes-send-to-everyone
Liste de vérification de l'audience, tout doit passer avant l'indicateur d'envoi :
- Les portes 1 et 2 ont été passées sur cette copie exacte (rien n'a changé depuis la relecture).
- La liste de désabonnement a été récupérée ou construite dans cette exécution et n'est pas vide si le produit a déjà eu un désabonnement.
- Les comptes du test à blanc ont du sens : la file d'attente est proche du nombre d'utilisateurs moins les désabonnés moins les exclus ; pas de saut surprise par rapport au
NOTES.mddu dernier numéro. - Mode Broadcast :
broadcast_sync.pyn'a signalé aucun contact obsolète inattendu. - Chaque URL d'image hébergée renvoie un 200.
- Quota : si
provider.monthly_quotaest défini, passez--sent-this-month N(additionnez les comptes d'envoi dans leNOTES.mdde ce mois-ci plus les autres e-mails sur le même plan si connu). Si le script indique que l'envoi dépasserait le quota, arrêtez-vous et demandez à l'utilisateur ; passez--yes-over-quotauniquement après son « oui » explicite.
L'envoi un par un est reprisable : s'il s'arrête, réexécutez la même commande et il ignore les adresses déjà envoyées.
Étape 8 : vérifier et synchroniser
- Un par un :
python3 scripts/verify_delivery.py ISSUE_DIR --sample 40. Broadcast :GET /broadcasts/{id}via l'API du fournisseur, ou le tableau de bord. - Récupérez les désabonnements après un jour (
broadcast_unsubs.py). Si l'utilisateur conserve les désabonnements dans sa propre base de données et que vous avez un accès en écriture, importez-les ; sinon, dites-le dans le rapport.
Étape 9 : posts sociaux
Écrivez à partir de l'e-mail final, en suivant references/social.md :
- Fil X : tweet d'accroche commençant par
social.hook_prefixqui nomme chaque élément, pas de lien, capture d'écran la plus forte. Puis un tweet numéroté par élément avec son lien et sa capture d'écran. Plus une citation personnelle d'une ligne pour un fondateur. - LinkedIn : un post, accroche en première ligne, un court paragraphe par élément, texte brut, galerie d'images.
- Communauté (Discord ou Slack) : ligne d'ouverture, une ligne par élément avec un lien nu, le lien du fil une fois en ligne.
4 à 5 éléments maximum, pas d'avis de politique, les affirmations correspondent au site en direct. Exécutez les mêmes vérifications de phrases interdites et de tirets sur social.md (lint_email.py le lit). Ensuite, pour chaque canal avec un outil dans social.post_via, publiez-le vous-même et enregistrez l'URL du post. Pour les canaux sans outil, remettez le texte et les images finaux à l'utilisateur. La citation du fondateur provient d'un compte personnel, transmettez-la donc à moins qu'un outil pour ce compte ne soit configuré.
Étape 10 : vidéo (facultatif, uniquement sur demande)
Si l'utilisateur le demande, suivez references/video.md : un reel animé de 15 à 25 secondes à partir des captures d'écran du numéro, pas de diapositive de titre, gros titre par élément, les captures d'écran dominent, outro avec l'URL du produit. Remotion nécessite une licence d'entreprise payante au-delà d'une certaine taille d'équipe ; c'est une question d'argent pour l'utilisateur.
Étape 11 : archiver et faire rapport
Dans le dossier du numéro, conservez : subject.txt, email.html, email.txt, les captures d'écran, social.md, test_results.jsonl, et un NOTES.md avec la liste des éléments, la date d'envoi, le chemin utilisé, la taille de l'audience, les comptes d'envoi et d'échec, l'ID du broadcast, les URL des posts, et les désabonnements après un jour. Si le produit a une page de changelog web publique (product.changelog_page) et que sa source est dans ce dépôt, ajoutez-y également le numéro. Lorsque l'utilisateur corrige le ton ou le format, ajoutez la leçon à voice.notes dans la configuration afin que le prochain numéro commence à partir de là. Ensuite, envoyez le résumé unique décrit au début.
Arrêts d'urgence
Arrêtez cette action, terminez tout le reste qui est sûr, et signalez ce qui s'est arrêté et pourquoi. Ne contournez jamais un arrêt.
- Échecs de vérification du style que vous ne pouvez pas corriger.
- Un lien ou une image cassé que vous ne pouvez pas corriger (toujours pas 200 après déploiement ou correction).
- La relecture du test échoue et vous ne trouvez pas la cause.
- Le filtrage des désabonnements échoue : la liste de désabonnement ne peut pas être récupérée ou construite, la requête d'audience génère une erreur, ou un script refuse à cause de
unsubs.txt. N'envoyez jamais sans cela. - Erreurs du fournisseur au-dessus du seuil en cours d'envoi (
blast.pyse termine avec le code 2 àprovider.max_failuresouprovider.max_failure_rate). Signalez l'erreur ; l'envoi groupé est reprisable une fois corrigé. - Un envoi qui dépasserait le quota, une mise à niveau du plan ou toute autre dépense : demandez à l'utilisateur (règle de l'argent).
- Invites de connexion, 2FA ou ré-authentification, CAPTCHA, avertissements de la plateforme ou notifications de limite de débit sur un outil de publication, formulaires de paiement ou de facturation.
- Tout ce qui enfreindrait le désabonnement ou la loi anti-spam (ligne de désabonnement manquante, réabonnement de quelqu'un qui s'est désabonné).
- Une affirmation que vous ne pouvez pas vérifier. Supprimez-la plutôt que de deviner.
Mode Review
Avec autonomy.mode: review, les trois portes nécessitent également l'accord de l'utilisateur :
- Porte de rédaction. Montrez l'objet, l'e-mail et la liste des éléments dans le chat. Rien n'est envoyé tant que l'utilisateur n'a pas approuvé la copie.
- Porte de test. Envoyez uniquement aux
test_recipients. L'utilisateur le lit dans une vraie boîte de réception et dit « go » ou donne des corrections. - Porte d'envoi groupé. Montrez les comptes du test à blanc (en file d'attente, exclus, désabonnés). Envoyez à tout le monde uniquement lorsque l'utilisateur dit « oui » à cet envoi spécifique dans le chat. Un « oui » à une étape précédente n'est pas un « oui » à celle-ci.
Les posts sociaux sont montrés avant la publication, et la liste des éléments est proposée avant la rédaction. Ne passez jamais un indicateur d'envoi avant la porte correspondante.
Répondre à « Combien d'abonnés ? »
Abonnés = utilisateurs moins désabonnés. Comptez les désabonnements à partir de la table de désabonnement de l'utilisateur ou de unsubs.txt (en mode broadcast, exécutez d'abord broadcast_unsubs.py). Donnez également le taux de désabonnement du dernier numéro.
Fichiers
config.example.yaml: chaque valeur spécifique à l'entreprise, plusautonomy,provider.monthly_quotaetsocial.post_via.templates/email.html,templates/email.txt: la mise en page de départ.scripts/:lint_email.py,send_test.py(avec--readback),broadcast_unsubs.py,broadcast_sync.py,broadcast_create.py,blast.py,verify_delivery.py.references/:voice-and-format.md,screenshots.md,sending.md,social.md,video.md. Lisez-les une fois et appliquez-les ; chacun contient une règle « une fois apprise, incarnez-la » qui signifie que vous devez l'assimiler et ne pas avoir besoin de la relire à chaque fois.