stream-feeds-migration について
アプリに実際に保存されたデータを基に、Stream の v2 から v3 への同期マッピングを導くスキルです。必要に応じて書き込みコードも調べ、コードを変更せずにマッピングと根拠、移行計画を提示します。
主な機能
- 01保存済みアクティビティとリアクションのサンプリング
- 02フィードグループと代表データの確認
- 03サンプルと v2 書き込みコードの照合
- 04v3 同期マッピングの作成
- 05コード移行計画の作成
活用シーン
- 01v2 から v3 への同期設定の準備
- 02保存データのフィールドやリアクション形式の確認
- 03アプリの読み書きパスに関する移行計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ getstream/ agent-skills --skill stream-feeds-migrationインストール後、Claude Code で/stream-feeds-migrationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
code-inventory.mdfetch_sample.pymapping-keys.mdporting-plan.mdscan_v2_calls.py詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
Stream Feeds - v2 から v3 への同期マッピング
最初にお読みください(毎回):
../stream/SKILL.mdを Glob し、../stream/RULES.mdを Read してください(どちらもこのスキルに同梱されています)。以下のすべての手順には Secrets ルールが適用されます。アプリシークレットが会話に入ることは決してありません。RULES.md には、必要に応じて他のパックスキルをインストールして呼び出すための Peer skills 手順も記載されています。
誰かがアプリのデータに何が含まれていると考えているかではなく、実際に含まれている内容を確認し、アプリの v2 → v3 同期設定用の mapping オブジェクトを作成します。
マッピングは、両方の移行経路で共用されます。ライブ同期ツールと一括エクスポーターは同じ設定を読み取ります。一度正しく設定すれば両方で機能し、誤って設定すると両方で同じように問題が発生します。
ワークフロー
1. 認証情報を環境変数に設定する
v2 アプリの API キーとシークレットが必要です。シークレットはローカルで HS256 JWT に署名するために使われます。JWT の iat/exp を設定し、生成から 5 分後に期限切れになるようにします。シークレット自体がサンプルファイルに書き込まれたり、出力されたり、署名以外の用途で送信されたりすることはありません。
../stream/RULES.md > Secrets に従い、シークレットをチャットに貼り付けるようユーザーに求めないでください。また、.env ファイルからシークレットを探すために cat、grep、Read を使わないでください。会話の記録に残らないよう、ユーザー自身にエクスポートを依頼してください。
! export STREAM_API_KEY=<v2 app key> STREAM_API_SECRET=<v2 app secret>
! プレフィックスを付けると、ユーザー自身のセッションでコマンドが実行されるため、会話を経由せずに値が環境変数に設定されます。値を出力しない存在確認で、設定されたことを確認してください。
test -n "$STREAM_API_SECRET" && echo SECRET_SET || echo SECRET_MISSING
有効なキーとシークレットがなければ、先に進まないでください。架空のアプリを対象に分析しても意味はなく、シークレットが間違っていると 403 が返るだけです。
2. 対象を選択する
デフォルトは本番環境です - https://api.stream-io-api.com を使用してください。別の環境(EU、専用環境、ステージング)をユーザーが指定した場合に限り、--base-url <that URL> を渡します。
代替環境をこちらから持ち出さないでください。本番環境が前提であり、別の環境を希望する場合はユーザーが伝えてくれます。
3. サンプルを取得する
python3 <skill-dir>/scripts/fetch_sample.py --out v3sync-sample.json
<skill-dir> はこの SKILL.md があるディレクトリです。実行前に絶対パスに解決してください。このスキルはユーザーがその時点で作業している任意のディレクトリから実行されるため、相対パスでは偶然動く場合しかありません。getstream skills はパックを次のいずれかの場所にリンクします。
.agents/skills/stream-feeds-migration(Cursor、Codex).claude/skills/stream-feeds-migration(Claude Code)
Glob {.agents,.claude}/skills/stream-feeds-migration/SKILL.md を実行して、どちらかを確認してください。
キーとシークレットは STREAM_API_KEY と STREAM_API_SECRET から読み取られます。スクリプトに --secret フラグがないのは意図的です。引数として渡したシークレットはプロセス一覧とシェル履歴に残りますが、環境変数を使えばそれを避けられます。(--api-key は存在します。キーは機密情報ではないためです。)サンプルは作業ディレクトリに書き込まれます。これが目的の保存先です。
標準ライブラリのみを使用し、pip のインストールは不要です。サーバー側専用の次の 2 つのエンドポイントを呼び出します。
GET /api/v1.0/migration/sample/activities- 直近 100 件のアクティビティGET /api/v1.0/migration/sample/reactions- 種類ごとに 5 件のリアクション
どちらも保存された生のペイロードを返します。extra_context はフラット化されず、リアクションの data は変更されません。実際のフィールド名とネスト構造を確認することが目的です。
401 または 403 が返る場合は、シークレットが間違っているか、サーバー側用ではなくユーザートークンとしてトークンが作成されています。404 が返る場合は、アプリのデプロイ環境でこれらのエンドポイントがまだ利用できません。Stream サポートに問い合わせて確認できます。
200 が返っているのに activities 配列が空の場合は、回避策を探すのではなく、その点を伝えてください。アプリに実際にデータがないか、アクティビティストアがこのスキャンに対応できない可能性があります。リアクションには activity_id が含まれるため、サンプルにリアクションがあればアクティビティの存在が確認でき、後者の可能性が示されます。
4. 分析する
v3sync-sample.json を読み、以下のチェックリストに沿って確認します。キーの完全なカタログ、デフォルト値、正確な解決ルール、よくある形式の具体例については
references/mapping-keys.md を読んでください。キー名やデフォルト値を記憶で推測してはいけません。
最もよくある間違いは、設定しすぎることです。すべてのキーには、一般的な v2 アプリに適したデフォルト値があります。サンプルからアプリがそのデフォルト値と異なることが分かる場合にのみ、キーを出力してください。マッピングが空でも有効な回答です。
5. マッピングを出力する
アプリの既存の同期設定に貼り付けられるよう、mapping オブジェクトのみを出力してください。外側の設定(enabled、base_url、api_key、secret、sample_rates、feed_groups_synced)は出力しないでください。これらはデプロイに関する設定であり、サンプルから導き出せるものではありません。また、これらを再掲してプレースホルダーの認証情報を残すと、<v3 app key> が残ったままの設定を誰かがデプロイしてしまうおそれがあります。
設定した各キーについて、サンプル中の根拠を示して説明してください。
サンプルから判断できなかったことは、以下のサンプルの限界を参照し、必ず明示してください。何も記載しないと「確認済み」と受け取られますが、実際にはそうではありません。
分析チェックリスト
アクティビティ
100 件すべての activities[].extra_context を確認します。
- カスタムフィールド名 - v3 の予約済みキーと名前が重複するフィールド、または
customのトップレベルに表示すべきフィールドには、名前の変更エントリーが必要です。慣例に沿った名前がすでに付いているフィールドには必要ありません。 - アクティビティ ID - フィールドに安定した外部 ID(
external_id、post_id、uuid)が格納されていますか?格納されている場合は、idをそのフィールドに設定します。それとは異なり、foreign_idに値があり、サンプル全体を通して意味のある値なら、foreign_id: "id"の方が簡潔です。どちらにも該当しない場合は、両方とも省略し、v2 の UUID を引き継ぎます。 - 再シェア/返信 -
SA:<uuid>形式の参照(一般的にはshared_origin_post)を格納するフィールドが、parent_idフィールドです。SA:形式の参照は自動検出もされるため、その参照が自動検出で最初に見つけられない場所に埋め込まれていることをサンプルが示す場合にのみ設定してください。 - テキスト - 投稿本文は
text(デフォルトのため設定不要)にありますか?それともbody/message/contentなど別のフィールドにありますか?異なる場合にのみ、textを設定します。 - 添付ファイル - カスタムパスにあるメディアオブジェクトのリストには、
activity_attachments_fieldが必要です。 - アクターの形式 - サンプル全体の
actorを確認します。SU:<id>はネイティブに処理されます。異なるプレフィックス(User:<id>)にはreplacesエントリーが必要です。アクターが JSON の全体オブジェクトである場合は、既知のレガシー形式なのでマッピングは不要です。 - extra_context のネスト - オブジェクトを
custom.extra_contextの下にネストしたままにする(デフォルト)か、名前を変更するか、フラット化する("")かを決めます。これは好みによるもので、サンプルだけでは判断できません。確認してください。
リアクション
まず kinds を確認してください。このリストが、アプリで使われているリアクションの語彙全体です。
- 各 kind を分類し、コメント、ブックマーク、コメント付きブックマーク、通常のリアクションのいずれか1つだけに割り当ててください。そのうえで、
comments、bookmarks、comments_bookmarksを適切に設定してください。いずれにも含めなかった kind は通常のリアクションとなり、多くの場合それで正しい設定です。 - デフォルトを忘れないでください。
commentsにはすでに["comment", "reply"]が含まれています。アプリのコメント kind がこれだけなら、改めて指定する必要はありません。 - コメント本文 — コメント kind のリアクションの
dataを調べてください。デフォルトはdata.messageです。本文がbodyやtextの下にある場合は、comment_fieldを設定してください。 - 返信 — 親コメントの ID はトップレベルの
parentにありますか(デフォルトのため、設定不要)、それともdataの中にネストされていますか?ネストされている場合は、そのパスをparent_id_fieldに設定してください。返信ではないコメントでも確認してください。トップレベルのコメントではそのパスが空でなければなりません。そうでないと、すべてのコメントが返信として扱われます。 - コメントへのリアクション — コメントではないリアクションの
dataにコメント ID が含まれている場合は、reaction_comment_id_fieldが必要です。 - kind の名前変更 — アプリで v3 の型名を別の名前にする必要がある場合に限ります。
- 添付ファイル — コメントの添付ファイルが
data.attachmentsにない場合は、attachments_fieldを設定してください。
1つの kind が互換性のない2種類の形で使われている場合に注意してください。たとえば、一部の like 行ではあるパスにコメント ID があり、別の行では異なるパスにある場合です。単一のパスしか指定できないキー(parent_id_field、reaction_comment_id_field)では両方に対応できません。どちらかを黙って選ぶのではなく、このマッピングで対応できない行を明記してください。
アプリによってはレガシーレイアウトフラグを使用しています。詳細は references/mapping-keys.md の Legacy layout flags を参照してください。決して設定しないでください。
ユーザー
サンプルのエンドポイントが返すのはアクティビティとリアクションであり、ユーザーではありません。そのため、ユーザーフィールドのマッピングはサンプルから推測できません。表示名やアバターが標準以外のキーに保存されている場合は確認したうえで、name / image を設定してください。この2つのキーは、ほかのすべての文字列キーとは逆向きに、v3 のキーから v2 のソースを参照します。
出力形式
マッピングオブジェクトのみを記載してください。
{
"bookmarks": ["bookmark"],
"parent_id_field": "data.comment.parent.id",
"attachments_field": "media.files"
}
含めるキーはすべて、サンプルによって根拠が確認できるものにしてください。v2 のデータがすでに v3 の規約に合致しているアプリでは、空のマッピング({})が有効かつ正しい答えです。デフォルト値で水増しするのではなく、その旨を明確に伝えてください。
オブジェクトに加えて、意図的に省略したキーとその理由(どのデフォルトがアプリの形にすでに対応しているか)を列挙してください。これにより、省略が見落としではなく意図的な判断だったと読者に伝わります。
省略リストに含めるのは、汎用キーのみです。 レガシーレイアウトフラグ(references/mapping-keys.md を参照)は、省略リスト、マッピング、その他回答のどこにも記載しないでください。行としても、補足としても、検討したうえで却下したと述べる場合であっても記載してはいけません。これらは対象のアプリには関係がないため、説明する必要はありません。何も言わずに省いてください。
最初の検証実行では、マッピングに "debug": true を指定するよう提案してください。これにより、同期されたすべてのアクティビティの custom._v3sync_debug に、その時点のマッピングが記録されます。マッピングを確認したら、この設定を削除するようユーザーに伝えてください。
サンプルの制限
生成したマッピングと併せて、該当する制限を明記してください。これらの制限によって、「このマッピングは正しい」と「このマッピングはサンプルに適合している」の違いが生じます。
- アクティビティは最新の100件です。 古いアクティビティにしか含まれないフィールドは表示されません。スキーマが時間の経過とともに変わったアプリでは、特にこの影響を受けます。
- リアクションは、直近の一定期間から取得されます。 最近使われていない種類は
kindsにまったく現れないことがあり、長く運用されているアプリでは、語彙が不完全な場合があります。 - ユーザーはサンプリングされません。
name/imageのマッピングは、別途確認する必要があります。 - まれな形状はサンプルの末尾に隠れています。 アクティビティの1%にしか存在しないフィールドは、100行では見つからない場合があります。フィールドの有無がマッピングの判断を左右する場合は、どのサンプル行に含まれていたかを明記してください。
- サンプルは変化します。 後で再実行すると、種類ごとの上限5件によって行が入れ替わり、新しい形状が加わる場合があります。そのため、単一の行を根拠にした判断を確かなものにするには、もう一度実行するのが手軽です。
最終決定の前に、出力した各キーをreferences/mapping-keys.mdの使用例と照合してください。ドキュメントに記載された形状と一致すれば、パスとネストが正しい有力な証拠になります。一致しない場合は、サンプルを再確認してください。
引き継ぎ
- v3 Feeds APIに関するSDKの質問(特定のSDKでv3のアクティビティ、コメント、ブックマークがどのような形式か) ->
stream-docs。 - マッピングが確定した後に、v3に対応したアプリを構築または更新する場合 ->
../stream/peers.yamlに記載されている、そのアプリのプラットフォームパック(stream-react、stream-swift、stream-android、stream-react-native、stream-flutter)。
自動実行せず、提案してください。詳細は../stream/RULES.md > トラックをまたぐフォローアップを参照してください。
サポート
ユーザーからサポートや問い合わせ先について質問された場合は、 getstream.io/contactを案内してください。