プロダクトチェンジログ
プロダクトの変更点をユーザーに伝える定期的なメールです。人間らしい言葉で書き、安全に送信し、ソーシャル投稿に再利用します。メールが各号の真実の源であり、他のすべてのチャンネルはそれから派生します。
このスキルの実行方法
- 最初に一度だけ質問します。 初回実行時(または設定が不足している場合や必要な値が空白の場合)は、設定質問を一度に簡潔に行い、
config.yamlに書き込み、その後は質問なしで続行します。 - その後はすべてを実行します。
autonomy.mode: autonomous(デフォルト)では、項目の選択、下書き、リント、自己レビュー、スクリーンショット、テスト送信、テストの読み上げ、オーディエンスの構築、ドライラン、全員への送信、確認、オプトアウトの取得、ソーシャル投稿の作成と投稿、アーカイブまでを行います。承認ステップはありません。各対外アクションは最初に自己レビューゲートを通過します。 - お金だけが承認を必要とします。 費用がかかるものはすべて、すべてのモードでユーザーからの明示的な「はい」をチャットで待ちます。プロバイダープランの含まれる割当を超える送信(
provider.monthly_quota)、プロバイダープランのアップグレード、ドメインの購入、有料ツールやライセンス(例:有料のRemotion会社ライセンス)などです。これら以外は「はい」を必要としません。 - ハードストップは承認ではありません。 そのアクションを停止します。安全な他の処理をすべて完了し、報告します。ハードストップを参照してください。
- 最後に報告します。 1つの要約:送信された件名と全文、カウント(キュー、送信、失敗、除外、配信停止)、配信サンプル、テスト読み上げ結果、各投稿とそのリンク、スキップされたものとその理由。
reviewモード は、従来のアクションごとの承認ゲートを維持します。レビューモードを参照してください。
すべてのスクリプトはデフォルトでドライランです。実際の送信には明示的なフラグ(--send、--yes-send-to-everyone、--yes-send-broadcast)が必要です。自律モードでは、自分でこれらのフラグを渡しますが、対応する自己レビューゲートを通過した後に限ります。スクリプト以外でアドホックにメールを送信しないでください。スクリプトがオプトアウトを強制するからです。
ステップ 0: 設定を読み込む
- 現在のプロジェクトルート、次にこのスキルのフォルダ(または
$CHANGELOG_CONFIG)でconfig.yamlを探します。 - ない場合は、
config.example.yamlをプロジェクトルートにconfig.yamlとしてコピーし、1つのメッセージで以下を質問します。- プロダクト名、ウェブサイト、ダッシュボードとドキュメントのURL
- 送信者名と役職、送信元アドレス、返信先アドレス
- テスト受信者(名前とアドレス)
- プロバイダー:ResendオーディエンスID、APIキーを保持する環境変数、および(わかれば)プランに含まれる月間メール数(
provider.monthly_quota、オプション) - 配信停止モード(
broadcastまたはown_endpoint) - 画像ホスティング:スクリーンショットの保存先とその公開ベースURL
- オプション:頻度、ブランドのリンク色、各ソーシャルチャンネルの投稿方法(
social.post_via) どれも推測しないでください。回答をconfig.yamlに書き込み、続行します。
- 大文字のプレースホルダーまたは
yourproduct.exampleアドレスに設定されている値は、すべて欠落と見なされます。 autonomy.mode(デフォルトはautonomous)、voice.notes、voice.banned_phrasesを読み取ります。Voice設定はリファレンスのデフォルトを上書きします。- シークレットは設定やチャットに決して入れません。APIキーと配信停止署名シークレットは環境変数に保存します。
以降の号では何も質問しません。唯一のオプションの質問は「今回git以外で出荷されたものはありますか(価格設定、パートナーシップ、ドキュメントなど)?」です。ユーザーが不在の場合は、この質問をスキップし、gitとリリースノートから作業します。
ステップ 1: 含める項目を選ぶ
最初に references/voice-and-format.md を読んでください。
paths.issues_dirの最後の号(NNN-YYYY-MM-DD-slugという名前のフォルダ)を見つけます。最新のものが次の号のテンプレートです。- その日付以降の候補を収集します:
git log --since=<日付> --no-merges --oneline、マージされたプルリクエスト、リリースノート、さらにユーザーが言及したもの。 - ユーザーに見える変更のみを保持します。3~5項目で、読者にとっての有用性でランク付けします。最も有用な変更を先頭に、それはほとんどの読者が今日使えるものです。
- すべての主張を実際のプロダクトとウェブサイトで確認します。機能、数値、日付を決して捏造しないでください。主張が検証できない場合は、その項目を削除します。
- リストと順序を号の
NOTES.mdに書き込み、次に進みます。
ステップ 2: メールの下書きを作成する
paths.issues_dir/NNN-YYYY-MM-DD-slug/ を作成し、以下を書き込みます。
subject.txt:ベネフィット主導でプロダクトファースト(「シートにレポートをエクスポートできるようになりました」)。「Changelog #N」は絶対に使わないでください。email.html:templates/email.html(または前回の号)から、設定と内容に基づいてすべての[[...]]マーカーを埋めます。email.txt:同じ内容をプレーンテキストで、ベアURLを記載します。
{{NAME}} と {{UNSUB_URL}} はリテラルプレースホルダーとして保持します。送信スクリプトが受信者ごとに埋めます。
コアルール(詳細はリファレンスを参照):
Hi {{NAME}},で始め、設定からの1行のオープナー、そしてすぐに最初の項目に入ります。- 各項目:何が変わったか、次になぜそれが重要なのかを、流れるような文章で記述します。ラベル付きの箇条書きは使いません。
- 短い文、平易な言葉、「we built」ではなく「you can now」を使います。
- 段落ごとに最大1つの太字アクセント。
- 項目ごとに最大1つのスクリーンショット。ヒーロー項目には常に1つ付け、その機能にリンクします。
- スタイルを最小限に抑えたHTML:600pxのラッパー、システムフォント、インラインスタイル、ボタンやバナーは使いません。
- 「excited to announce」などの禁止リストにある表現は使いません。
voice.no_dashesがオンの場合、emダッシュやenダッシュも使いません。 - オプションのカジュアルな追伸(「BTW、...」)。
- 設定からの署名。配信停止行は最後の行で、小さく目立たないようにします。
ステップ 3: スクリーンショット
references/screenshots.md に従います。デモアカウントで実際の状態をステージングし(顧客データは使わない)、Playwrightで2倍の解像度でキャプチャし、パネルに切り抜き、ファイルを <issue>-<slug>.png と名付け、paths.image_base_url でホストし、ブロードキャストの前に各URLが200を返すことを確認します。
ステップ 4: リントと自己レビュー(ゲート 1: コピー)
python3 scripts/lint_email.py ISSUE_DIR --check-links
すべての失敗を修正し、以下のチェックリストを実行します。すべてのボックスが合格するまで修正と再実行を繰り返します。
-
lint_email.py --check-linksが終了コード0で終了する。 - 忙しい顧客として上から下まで読む:5秒以内に最も有用な変更が明確にわかる。
- すべての項目がユーザーが今日見たり使ったりできるものである。
- すべての主張、数値、リンクが実際のプロダクトとサイトと一致する。
- プレスリリースのようには読めない。送信者が話しているように聞こえる。
- 件名が60文字未満で、ベネフィット主導であり、「Changelog #N」を含まない。
- すべての画像にaltテキストがあり、ホストされたURLが200を返す(またはステップ7の前にデプロイされる)。
ステップ 5: テスト送信と読み上げ(ゲート 2: 配信されたコピー)
python3 scripts/send_test.py ISSUE_DIR --send # test_recipients のみ、[TEST] プレフィックス
python3 scripts/send_test.py ISSUE_DIR --readback # 1分後
テストコピーは test_recipients にのみ送信され、まだデプロイされていない画像はインラインで埋め込まれます。--readback は、プロバイダーAPIから配信された各メッセージを取得し、最後のイベント、件名、レンダリングされた挨拶文と配信停止行をチェックし、プレースホルダーが残っていないことを確認します。また、返されたHTMLを自分で一度読みます。何か問題があれば、修正し、再リントし、再テストします。
own_endpoint モードでは、テストコピーの配信停止リンクを開き、オプトアウトが記録されたことを確認し、テストアドレスを再購読します。
ステップ 6: オーディエンスを構築する
詳細は references/sending.md を参照してください。
- ユーザーのデータベースから
ISSUE_DIR/audience.jsonlを新たに構築します(1行につき{"email", "name"})。古いリストを再利用しないでください。 - オプトアウトテーブルから
ISSUE_DIR/unsubs.txtを構築します。ブロードキャストモードでは、最初にpython3 scripts/broadcast_unsubs.py --out <issues_dir>/unsubs.txtを実行してコピーします。 - 創業者、テスト、デモアカウントは設定の
excludeにあり、代わりにテストコピーを受け取ります。
ステップ 7: 全員に送信する(ゲート 3: オーディエンス)
unsubscribe.mode からパスを選択します。最初にドライランを実行し、カウントを確認します。
ブロードキャストモード(プロバイダーが配信停止をホスト):
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl # 計画
python3 scripts/broadcast_sync.py ISSUE_DIR/audience.jsonl --apply # 連絡先のupsert
python3 scripts/broadcast_create.py ISSUE_DIR --create # 下書きのみ
python3 scripts/broadcast_create.py --send BROADCAST_ID # 受信者数を表示、拒否する
python3 scripts/broadcast_create.py --send BROADCAST_ID --yes-send-broadcast
独自エンドポイントモード(自分のページへの署名付きリンク):
python3 scripts/blast.py ISSUE_DIR # カウント付きドライラン
python3 scripts/blast.py ISSUE_DIR --send --yes-send-to-everyone
オーディエンスチェックリスト。送信フラグの前にすべて合格する必要があります。
- ゲート1と2がこの正確なコピーで合格している(読み上げ以降何も変更されていない)。
- オプトアウトリストが今回の実行で取得または構築されており、製品に一度でも配信停止があった場合は空ではない。
- ドライランのカウントが妥当である:キューに入った数がユーザー数からオプトアウト数と除外数を引いたものに近い。前回の号の
NOTES.mdと比較して驚くような増加がない。 - ブロードキャストモード:
broadcast_sync.pyが予期しない古い連絡先を報告していない。 - ホストされているすべての画像URLが200を返す。
- クォータ:
provider.monthly_quotaが設定されている場合、--sent-this-month Nを渡します(今月の号のNOTES.mdに記録された送信数と、同じプランでの他のメールがわかればその合計)。スクリプトが送信がクォータを超えると言った場合、停止してユーザーに尋ねます。ユーザーが明示的に「はい」と言った後にのみ--yes-over-quotaを渡します。
1件ずつのブロードキャストは再開可能です。停止した場合、同じコマンドを再実行すると、既に送信済みのアドレスはスキップされます。
ステップ 8: 確認と同期
- 1件ずつ:
python3 scripts/verify_delivery.py ISSUE_DIR --sample 40。ブロードキャスト:プロバイダーAPIまたはダッシュボード経由でGET /broadcasts/{id}を実行します。 - 1日後にオプトアウトを取得します(
broadcast_unsubs.py)。ユーザーがオプトアウトを独自のデータベースで管理しており、書き込みアクセス権がある場合はインポートします。そうでない場合は、レポートでその旨を伝えます。
ステップ 9: ソーシャル投稿
最終版のメールから、references/social.md に従って作成します。
- Xスレッド:
social.hook_prefixで始まるフックツイート。すべての項目に名前を付け、リンクはなく、最も強力なスクリーンショットを添えます。次に、項目ごとに番号付きのツイートを1つ、そのリンクとスクリーンショットを添えます。さらに、創業者による1行の個人的な引用投稿を追加します。 - LinkedIn:1つの投稿、最初の行にフック、項目ごとに1つの短い段落、プレーンテキスト、イメージギャラリー。
- コミュニティ(DiscordまたはSlack):オープナー行、項目ごとに1行(ベアリンク付き)、スレッドリンクは公開されたら1つ。
最大4~5項目、ポリシー通知はなし、主張は実際のサイトと一致。social.md で同じ禁止フレーズとダッシュチェックを実行します(lint_email.py が読み取ります)。次に、social.post_via にツールがある各チャンネルについて、自分で投稿し、投稿URLを記録します。ツールがないチャンネルについては、最終的なテキストと画像をユーザーに渡します。創業者の引用投稿は個人アカウントからのものなので、そのアカウント用のツールが設定されていない限り、ユーザーに渡します。
ステップ 10: 動画(オプション、リクエストがあった場合のみ)
ユーザーがリクエストした場合、references/video.md に従います。号のスクリーンショットから15~25秒のモーションリール。タイトルスライドはなし。項目ごとに大きな見出し。スクリーンショットが支配的。アウトロにプロダクトURL。特定のチームサイズを超える場合、Remotionには有料の会社ライセンスが必要です。これはユーザーへの金銭的な質問です。
ステップ 11: アーカイブと報告
号のフォルダに以下を保持します:subject.txt、email.html、email.txt、スクリーンショット、social.md、test_results.jsonl、および NOTES.md(項目リスト、送信日、使用されたパス、オーディエンスサイズ、送信数と失敗数、ブロードキャストID、投稿URL、1日後の配信停止を含む)。プロダクトに公開ウェブチェンジログページ(product.changelog_page)があり、そのソースがこのリポジトリにある場合は、そこにも号を追加します。ユーザーが文体やフォーマットを修正した場合、その教訓を設定の voice.notes に追加し、次の号からそれを反映させます。その後、冒頭で説明した1つの要約を送信します。
ハードストップ
そのアクションを停止し、安全な他のすべての処理を完了し、何が停止したかとその理由を報告します。ストップを回避しようとしないでください。
- 修正できないリントエラー。
- 修正できない壊れたリンクや画像(デプロイまたは修正後も200にならない)。
- テストの読み上げに失敗し、原因が見つからない。
- オプトアウトフィルタリングの失敗:オプトアウトリストを取得または構築できない、オーディエンスクエリがエラーになる、またはスクリプトが
unsubs.txtを拒否する。なしでは絶対に送信しないでください。 - 送信中にしきい値を超えるプロバイダーエラー(
blast.pyがprovider.max_failuresまたはprovider.max_failure_rateで終了コード2を返す)。エラーを報告します。ブロードキャストは修正されれば再開可能です。 - クォータを超える送信、プランアップグレード、またはその他の出費:ユーザーに尋ねます(お金のルール)。
- ログイン、2FA、再認証のプロンプト、CAPTCHA、投稿ツールでのプラットフォーム警告やレート制限通知、支払いや請求フォーム。
- オプトアウトやスパム対策法に違反するもの(配信停止行の欠落、オプトアウトした人の再購読)。
- 検証できない主張。推測する代わりに削除します。
レビューモード
autonomy.mode: review では、3つのゲートもユーザーの承認を必要とします。
- ドラフトゲート。 件名、メール、項目リストをチャットに表示します。ユーザーがコピーを承認するまで何も送信されません。
- テストゲート。
test_recipientsにのみ送信します。ユーザーが実際の受信箱でそれを読み、「送信」または修正を指示します。 - ブロードキャストゲート。 ドライランのカウント(キュー、除外、配信停止)を表示します。ユーザーがチャットでその特定の送信に対して「はい」と言った場合にのみ、全員に送信します。以前のステップで「はい」と言ったことは、このステップに対する「はい」ではありません。
ソーシャル投稿は投稿前に表示され、項目リストは作成前に提案されます。対応するゲートを通過する前に、送信フラグを決して渡さないでください。
「購読者数」への回答
購読者数 = ユーザー数 - オプトアウト数。ユーザーのオプトアウトテーブルまたは unsubs.txt からオプトアウト数をカウントします(ブロードキャストモードでは、最初に broadcast_unsubs.py を実行します)。最新の号の配信停止率も提供します。
ファイル
config.example.yaml:すべての会社固有の値、さらにautonomy、provider.monthly_quota、social.post_via。templates/email.html、templates/email.txt:開始レイアウト。scripts/:lint_email.py、send_test.py(--readback付き)、broadcast_sync.py、broadcast_create.py、blast.py、verify_delivery.py、broadcast_unsubs.py。references/:voice-and-format.md、screenshots.md、sending.md、social.md、video.md。
スクリプトの完全なコマンドラインインターフェースについては、各スクリプトのヘルプテキストを参照してください。