朝、メールボックスに「App Review」からの通知が届いている日があります。件名を見た瞬間、その日の予定が崩れる感覚。個人開発で複数のアプリを運営していると、審査リジェクトは年に数回、忘れた頃にやってきます。
私自身、以前は文面を読んでから調べ始めていました。どのガイドライン番号なのか、前回はどう直したのか、返信はどう書いたのか。記録が散らばっているせいで、毎回ゼロから調査のやり直し。半日が消えます。
現在は、この一次対応を Antigravity のエージェントに渡しています。人間の判断が要る部分は残したまま、分類・照合・チェックリスト生成という「調査の下ごしらえ」だけを固定する構成です。ここからは、その設計を実際に使っているプレイブックとスクリプトごと共有いたします。
リジェクト対応が遅れる本当の理由
対応が遅れる原因は、技術的な難しさではありません。私の場合は次の3つでした。
まず、感情の抵抗。リジェクト文面は淡々としていますが、受け取る側は身構えます。開くまでに時間がかかる。
次に、情報の散逸。過去のリジェクトと対応履歴が Resolution Center・メール・コミットログに分散していて、「前回どうしたか」を思い出す作業が最初の壁になります。
最後に、分類の不在。Apple の指摘は App Store Review Guidelines の条番号に紐づいていますが、条番号ごとに「やるべきこと」を自分の言葉で整理していないと、毎回ガイドライン本文の読み直しから始まってしまいます。
エージェントに任せるべきはこの3つ目、分類と手順の固定です。ここが決まれば、1つ目の感情の抵抗も軽くなります。「開けばエージェントが仕分けてくれる」と分かっているメールは、開くのが怖くなくなるものです。
Step 1: リジェクト文面の取り込みと構造化
前提を1つ。Resolution Center のメッセージは、App Store Connect API では取得できません。審査ステータス自体は API で追えますが、指摘の本文は画面かメールにしかない。ここは手動でのコピーまたは添付が入口になります。
Antigravity は 6 月末の更新で PDF 添付に対応したため、私は審査メールを PDF で保存してそのまま会話に添付しています。スクリーンショットでも動きますが、複数ページの指摘は PDF の方が欠落しません。
エージェント側には、添付を次の JSON に構造化させます。
{
"app": "wallpaper-app-A",
"submission_date": "2026-06-24",
"guidelines": [
{
"number": "5.1.1",
"topic": "Data Collection and Storage",
"demand": "写真ライブラリへのアクセス目的の説明が不十分",
"target": "Info.plist / NSPhotoLibraryUsageDescription"
}
],
"reviewer_questions": [],
"binary_or_metadata": "binary"
}
ポイントは binary_or_metadata の判定です。バイナリ修正が必要なのか、メタデータ(説明文・スクリーンショット・目的文字列)の修正で済むのかで、再提出までの経路がまったく違います。メタデータのみなら再ビルド不要。この判定を最初に固定するだけで、無駄な再ビルドが1回減ります。
Step 2: ガイドライン別プレイブックを YAML で固定する
構造化した指摘は、リポジトリに置いたプレイブックと照合させます。私が実際に使っているものの抜粋です。
# .antigravity/review-playbook.yaml
playbook:
"2.1":
label: "App Completeness / 情報提供の要求"
first_actions:
- "クラッシュログの有無を確認(指摘にログが添付されていれば最優先)"
- "審査環境(最新OS・クリーンインストール)での再現手順を書き出す"
- "デモアカウント・レビューノートの記載漏れを確認"
reply_tone: "再現手順と修正内容を事実だけで簡潔に"
"4.3":
label: "Design - Spam / 類似アプリ"
first_actions:
- "指摘対象と自アプリの差分(機能・対象ユーザー)を列挙"
- "テンプレート由来と誤認されうるUIを洗い出す"
human_only: true # 反論の組み立ては人間が書く
"5.1.1":
label: "プライバシー / 権限の目的文字列"
first_actions:
- "全 UsageDescription を lint スクリプトで機械検査"
- "権限要求のタイミングが機能文脈と一致しているか確認"
reply_tone: "修正後の文字列をそのまま引用する"
human_only: true を付けた条項は、エージェントが返信ドラフトを書かない取り決めです。4.3(スパム判定)のように審査官との対話が本質になる指摘は、下調べだけ任せて文章は自分で書きます。任せる範囲を YAML に明記しておくと、エージェントの挙動が日によって揺れません。
Step 3: 機械検査できる指摘は再提出前のゲートにする
5.1.1 系の指摘で一度学んだのは、「同じ轍を踏む再リジェクトが一番もったいない」ということです。目的文字列の不備は機械検査できるので、再提出前に必ず通すリントを書きました。
// scripts/lint-usage-descriptions.mjs
// 実行: node scripts/lint-usage-descriptions.mjs path/to/Info.plist
import { readFileSync } from "node:fs";
const GENERIC_PATTERNS = [
/このアプリの機能のため/,
/for app functionality/i,
/required by the app/i,
];
const MIN_LENGTH = 20; // 「なぜ・何に使うか」を書くと自然にこの長さを超える
const plist = readFileSync(process.argv[2], "utf8");
const keys = [...plist.matchAll(
/<key>(NS\w+UsageDescription)<\/key>\s*<string>([\s\S]*?)<\/string>/g
)];
let failed = false;
for (const [, key, value] of keys) {
const text = value.trim();
if (text.length < MIN_LENGTH) {
console.error(`✗ ${key}: ${text.length}文字。目的と用途を具体的に書いてください`);
failed = true;
}
if (GENERIC_PATTERNS.some((p) => p.test(text))) {
console.error(`✗ ${key}: 汎用文言です。「何を・なぜ」を明示してください`);
failed = true;
}
}
if (keys.length === 0) console.warn("⚠ UsageDescription キーが見つかりません");
process.exit(failed ? 1 : 0);
このリントは CI ではなく、エージェントの再提出チェックリストの1項目として実行させています。落ちたらエージェントが修正案の文字列を提案し、私が採否を決める流れです。
あわせて、SDK 要件の事前確認も同じゲートに入れています。今年4月末からは iOS 26 SDK でのビルドが提出の必須要件になったため、Xcode のバージョン不一致はアップロード段階で弾かれます。リジェクトとは別経路の失敗ですが、「再提出したつもりが止まっていた」を防ぐ意味で、チェックリストに含める価値があります。
Step 4: 頻出指摘の対応表を1枚に集約する
分類の受け皿として、頻出ガイドラインの一次対応を表で固定しています。エージェントにもこの表を参照させ、私自身も再提出前に目視で通します。
| ガイドライン |
典型的な指摘 |
一次対応 |
再ビルド |
| 2.1 |
クラッシュ・情報提供の要求 |
再現確認とレビューノート整備 |
場合による |
| 2.3.10 |
他プラットフォームへの言及 |
説明文・スクリーンショットの修正 |
不要 |
| 4.3 |
類似アプリ・スパム判定 |
差分整理のうえ人間が返信を執筆 |
不要(対話が先) |
| 5.1.1 |
権限の目的文字列の不備 |
リント実行と文字列修正 |
必要 |
表にしてみると、再ビルドが不要な指摘が意外に多いことに気づきます。以前の私は反射的に Xcode を開いていましたが、メタデータ修正だけで済む再提出は数時間ではなく数十分の作業です。
実測: 半日かかっていた一次対応が90分に
この体制にしてから、直近半年で4件のリジェクトを処理しました。文面の添付から、分類・該当プレイブックの提示・チェックリスト生成までがおおよそ5分。そこから修正と返信ドラフトの確認を含めて、平均90分程度で再提出まで戻れています。以前は半日、気が重い日は翌日に持ち越していた作業です。
もう1つの変化は再リジェクト率です。目的文字列のリントを入れる前は、同じ5.1.1で2回続けて差し戻された経験がありました。ゲート導入後は同一条項での再指摘はゼロ。機械検査できる指摘を人間の注意力に頼らないことの効果は、数字より精神面で大きいと感じております。
記録ベースで見ると、1件あたりの対応時間は約240分から約90分へ、およそ60%の短縮です。内訳では「過去事例の調査」に充てていた時間がほぼゼロになったことが最も効いています。分類とプレイブック照合を先に固定した設計判断が、この部分に直接返ってきました。
エージェントに任せない領域
最後に、意図的に自動化していない部分を書き添えます。
審査官への反論や説明の最終文面は、必ず自分で書きます。プレイブックの human_only がその境界線です。ガイドライン解釈の主張は開発者自身の責任範囲であり、ここを委ねると対話の精度が落ちます。
また、リジェクトを装ったフィッシングメールの見分けも人間側の仕事です。エージェントに渡す前に、送信元と Resolution Center の実物を必ず突き合わせています。
着手の順番としては、5.1.1 と 2.3.10 の2条項からプレイブック化することを推奨します。指摘の頻度が高く、機械検査との相性がよいため、効果が最も早く現れる領域です。
まずは過去のリジェクトメールを1通、構造化 JSON に起こすところから始めてみてください。2件目からは、それがそのままプレイブックの種になります。同じ朝を迎える方の参考になれば幸いです。