adversarial-swift-ui について
アーキテクチャ、ライフサイクル、ビューの同一性、性能、アクセシビリティの規則に照らして SwiftUI コードを確認するスキルです。ブラインドレビューで合否を判定し、コードは修正しません。
主な機能
- 01ブラインドによる敵対的レビュー
- 02PASS/FAIL/N/A 判定
- 037 種類の SwiftUI 規則
- 04ツールチェーンとデプロイ対象のバージョン判定
- 05証拠に基づく判定レポート
活用シーン
- 01マージ前の SwiftUI 変更レビュー
- 02エージェント作成ビューの監査
- 03UIKit から SwiftUI への移植確認
- 04アクセシビリティ規範の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill adversarial-swift-uiインストール後、Claude Code で/adversarial-swift-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
gotchas.mdmetadata.jsonassets/templates/verdict.mdreferences/_sections.mdaccess-button-not-ontapgesture.mdaccess-button-title-and-icon.mdaccess-representation-for-custom-controls.mdaccess-scaledmetric-custom-spacing.mdaccess-semantic-fonts-and-colors.mdaccess-style-protocols-for-custom-controls.mdanim-body-scoped-animation-in-generic-containers.mdanim-builtin-effects-over-custom-animatable.mdidentity-anylayout-over-container-branches.mdidentity-branch-free-modifier-helpers.mdidentity-branch-in-modifier-not-around-view.mdlist-constant-foreach-view-count.mdlist-no-anyview-rows.mdlist-no-geometry-feedback-loop.mdlist-ongeometrychange-over-georeader.mdlist-visualeffect-for-render-transforms.mdreviewer-prompt.mdstate-callasfunction-env-closures.mdstate-guard-task-id-model-reinit.mdstate-no-highfreq-environment-values.mdstate-no-initialvalue-from-params.mdstate-observable-not-observableobject.mdstate-own-models-in-state.mdtask-concurrent-offload-cpu-work.mdtask-id-for-async-state-reactions.mdtask-modifier-not-onappear-task.md詳細
Adversarial SwiftUI Gate
SwiftUI アーキテクチャのレビューゲートです。合否は、一人のブラインドレビュー担当サブエージェントが敵対的な立場からこのゲートのルールに照らして判定します。すべてのルールが PASS または N/A の場合にのみ合格となります。このスキルは判定を下すものであり、成果物を修正することはありません。
ルールは、コード上の証拠だけからレビュー担当者が判定できるチェックに絞られています。このゲートが判定するのは、エージェントや経験はあるものの専門家ではない開発者が実際に起こす失敗です。レガシーなオブザベーションスタック、ID を破壊する条件分岐、誤ったライフサイクル上の位置に結び付けられた処理、遅延読み込みを妨げるリスト構築、汎用コンテンツにまで波及するアニメーション、システムのアクセシビリティセマンティクスから外れるコントロールが対象です。
適用するタイミング
- SwiftUI の差分、機能、PR がマージされようとしており、助言的なフィードバックではなく、客観的な PASS/FAIL が必要なとき。
- エージェント(Claude、Codex)がビューを作成し、独立したチェックによって、古い Observation のイディオム(
ObservableObject/@StateObject、.onAppear { Task { ... } }、AnyViewの行、計測のためのGeometryReader)や ID をリセットする条件分岐(applyIfヘルパー、コンテナ型の切り替え)を再現していないことを確認したいとき。 - UIKit 時代の画面を SwiftUI に移植しており、新しい画面領域をフレームワークの更新メカニズム(サブビューの境界、最小限の依存関係、導出値のキャッシュ、安定した ID)に照らして監査する必要があるとき。
- アクセシビリティとプラットフォーム規約への準拠を、想定ではなく検証する必要があるとき。アイコンのみのボタン、コントロールのように振る舞うタップジェスチャー、スタイルプロトコルがあるのに再構築されたプリミティブ、何も通知しない Canvas コントロール、ハードコードされたフォント、テキスト周辺の固定間隔が対象です。
SwiftUI の画面領域がないターゲットには適用しないでください。サーバーサイドの Swift、CLI ツール、非 UI パッケージなどが該当します(レビュープロンプトの前提条件チェックにより、「GATE NOT APPLICABLE」で中止されます)。Swift 言語全般の品質(並行処理の構造、API 設計、制御フロー)は、兄弟ゲートである adversarial-swift の担当であり、このゲートの対象ではありません。解決にプロジェクトより新しいツールチェーンやデプロイメントターゲットが必要なルールは、FAIL ではなく N/A です。レビュープロンプトにはバージョンゲートの表が含まれています。
レビュープロトコル
次の手順を厳密に守ってください。すべてのレビューを同じ方法で実行することが、このゲートの価値です。
ステップ 1 の前に、2 つの前提条件を満たしてください。どちらも、これらに違反したことがゲート群で記録された現場の失敗パターンだからです(2026 年 7 月:変化し続ける対象に対して、クリーンアップの手段としてゲートを実行した結果、差分が 33 ファイルに膨らむまで判定が一度も出ませんでした)。
- 対象を固定する。 コミット SHA、stash、保存済みの差分など、固定された参照に対してのみ実行し、その参照を対象マニフェストとして記録してください。レビュー対象のコードがまだ変更中(作業ツリーに編集が加わり続けている、ほかのエージェントが作業中など)なら、実行してはいけません。レビュー中に対象が変更された場合、その実行は無効です。判定の代わりに
GATE VOID — target changedと報告し、新たに固定された状態に対して再実行してください。実行されたゲートは必ず、判定、「GATE NOT APPLICABLE」、または記録された無効判定のいずれかで終わらなければなりません。何も出力しない状態は、レビュー済みと区別できないため許されません。 - 判定を行い、クリーンアップはしない。 このゲートは、完成した成果物に対する最終チェックです。「ゲートを実行し、すべてを徹底的に修正する」では契約が逆転し、作業範囲が膨張することが記録されています。先に実装し、対象を固定してからゲートを実行してください。FAIL の後、呼び出し元は元の対象内に限って修正を適用し、再ゲートでは同じ対象マニフェストに対して、新しいブラインドレビュー担当者を起動してください。ラウンド間で対象範囲を広げてはいけません。新しいコードが加わる場合は、それ専用のマニフェストで新たにゲートを実行してください。
レビュー中にほかのエージェントがワークスペースを変更する可能性がある場合、レビュー担当者はライブチェックアウトではなく、不変のスナップショット(例:git archive <ref> <skill-dir> | tar -x -C "$TMPDIR")からルールを読み込みます。また、スキルディレクトリは読み取り専用のインフラストラクチャであり、すべてのクリーンアップや修正の対象から除外してください。
- 対象を特定します。 レビュー対象(差分、ファイル一式、PR)を正確に特定し、レビューを曖昧さのない固定された対象に対して実行できるよう、参照先とパスを記録します。Swift ツールチェーンのバージョンと最小デプロイメントターゲット(
Package.swift、Xcode プロジェクト、または availability アノテーションから取得)を記録します。複数のルールがこれらの値に応じて適用されるためです。対象の説明にはリポジトリのルートを含めます。update-pass-minimal-data、state-own-models-in-state、update-cache-expensive-derivationsでは、差分の該当箇所以外も含めてファイル全体を読む必要がある場合があります。 - ルールを読み込みます。 references/_sections.md と、
references/内のすべてのルールファイル(すべてのstate-*.md、update-*.md、identity-*.md、task-*.md、list-*.md、anim-*.md、access-*.mdファイル)を読みます。 - レビュアー用プロンプトを作成します。 ルール、対象、ツールチェーンとデプロイメントに関する情報を含めて、references/reviewer-prompt.md を埋めます。作成するプロンプトは完全に自己完結している必要があります。レビュアーには会話履歴が見えないため、プロンプト外のコンテキストを参照してはいけません。
- ブラインドレビュアーを1人起動します。 作成したプロンプトだけを入力全体として、Task サブエージェントを1つ起動します。会話コンテキストを渡したり、コメントを添えたりしてはいけません。
- フェイルクローズで判定します。 レビュアーの構造化出力が判定結果です。統合ステップはありません。すべてのルールが PASS または N/A の場合に限り、全体の判定を PASS とします。FAIL が1つでもあれば、ゲート全体を失敗とします。重大度を平均したり、比較衡量したり、ルールの適用を免除したりしてはいけません。「軽微」な FAIL も FAIL です。レビュアーが「GATE NOT APPLICABLE」(SwiftUI の対象範囲なし)を返した場合は、判定を出さず、その旨を報告して終了します。
- 判定結果を出力します。 assets/templates/verdict.md を埋めます。FAIL の場合は、レビュアーの「missing for PASS」に記載された提案を修正リストにまとめ、カテゴリの重要度順に並べ、それぞれに該当箇所を記載します。最終結果が FAIL のルールはすべて修正リストに含め、記載どおりに適用できる具体的な変更を示す必要があります。レビュアーの提案が違反内容の言い換えにすぎない場合は、出力前にルールの Correct の例を参考に、修正内容を導き出してください。修正リストは検証資料です。各 FAIL が判定可能であり、修正によって PASS に変わることを証明するものであって、作業リストではありません。各修正には、宣言された対象範囲内で該当ルールを PASS に変えるための最小限の変更を記載します。ルールを満たすために、安全でない回避策(
@unchecked Sendable、try!、強制アンラップ)に頼る修正では、PASS に変わりません。対象マニフェストの範囲外でレビュアーが気づいた違反は、判定結果の対象範囲外の所見に記載します。今後のゲート実行に向けて報告するものとし、今回の実行で修正したり、判定に含めたりしてはいけません。
対象が変更されていないのに再レビューごとに同じルールの判定が変わる場合、または人が証拠を確認したうえで判定を覆す場合、それはルールの判定可能性に関するバグです。gotchas.md に記録してルールを明確化し、ゲートの判定を覆してはいけません。
判定フォーマット
レビュアーはルールごとに PASS | FAIL | N/A、証拠(file:line または引用。PASS と FAIL の両方で必須)、および FAIL ごとに、適用するとそのルールが PASS に変わる修正(変更内容と該当箇所を明記し、違反内容の言い換えは不可)を返します。最終レポートは assets/templates/verdict.md に従います。
ルールのカテゴリ
| # | カテゴリ | プレフィックス | 対象 |
|---|---|---|---|
| 1 | データモデリングと監視 | state- | ObservableObject よりも @Observable を使用する、ビューが作成したモデルの所有権を @State で管理する、イニシャライザーの引数から State(initialValue:) を設定しない、.task(id:) でモデルを再作成する際にアイデンティティを確認する、環境クロージャーを callAsFunction でラップする、高頻度で変化する値を EnvironmentValues に含めない |
| 2 | ビューの更新コスト | update- | 算出プロパティの some View 変数よりサブビューの構造体を使用する、末端ビューに渡すデータを最小限にする、ビューの init で副作用を起こさない、O(n) の派生値をキャッシュする、コレクションを含む構造体をリストの行に使わない |
| 3 | ビューの構造的アイデンティティ | identity- | ビューを分岐させず、モディファイアの値の中で分岐させる、モディファイアヘルパー(applyIf)で実行時に if/else を使わない、コンテナを分岐させるより AnyLayout を使用する |
| 4 | データ読み込みとタスクのライフサイクル | task- | .onAppear と Task の組み合わせより .task を使用する、.onChange と Task の組み合わせより .task(id:) を使用する、比較する値にはスカラー値を使う、@MainActor のオブザーバブルで CPU 処理をオフロードするには @concurrent を使用する |
| 5 | リストとジオメトリ | list- | ForEach 内の要素ごとのビュー数を一定にする、行に AnyView を使わない、計測用の GeometryReader より onGeometryChange を使用する、ジオメトリのフィードバックループを発生させない、スクロールに連動する変換には visualEffect を使用する |
| 6 | アニメーションのスコープ | anim- | 任意のコンテンツに値ベースの .animation を適用するより、汎用コンテナには animation(_:body:) を使用する、カスタム Animatable より組み込みのアニメーション可能な属性を使用する |
| 7 | プラットフォームの慣例とアクセシビリティ | access- | タイトルとアイコンを使ってボタンを作成する、onTapGesture より Button を使用する、コントロールを再構築するよりスタイルプロトコルを使用する、独自のコントロールにはアクセシビリティ表現を用意する、セマンティックなフォントと色を使う、テキストの近くに配置する独自の間隔には @ScaledMetric を使用する |
注意点
レビュアーに作業を割り当てる前に gotchas.md を読んでください。ルールへの適合性を守るための注意事項(このゲートのルールでは推奨される一方、コミュニティの通説では否定されるパターン。たとえば定数コレクションに id: \.self を使うことや、ビューのモデルを @State に保持すること)があらかじめ記載されており、レビュアーが外部のルールを持ち込まないようにします。
関連スキル
adversarial-swift— Swift 言語に関する懸念(並行処理、エラー伝播、API と型の設計、制御フロー)を扱う兄弟ゲートです。Swift と SwiftUI の両方が関わる作業では、両方を実行してください。adversarial-ios-design— レンダリングされるデザインと UX(HIG の文法、モーション、必須のエビデンス取得)を扱う兄弟ゲートです。ユーザー向けの作業ではこれも実行してください。また、差分によってジェスチャーや操作の動作が変わる場合は、こちらを最初に実行してください。レコグナイザーの調停は、このゲートのコードのみのレビューでは確認できません。ios-taste(厳選)— このゲートでは意図的に対象外としている、判断を要する iOS デザインのガイダンスです。判定ではなくセンスが求められる場合に使用してください。search-ios26-docs/search-macos26-docs— バージョンに関する条件でルールが決まる場合に、現在の SDK で API を利用できるか確認してください。
参照ファイル
| ファイル | 説明 |
|---|---|
| references/reviewer-prompt.md | ブラインドレビュアー用の自己完結型プロンプトテンプレート |
| assets/templates/verdict.md | 判定レポートのテンプレート |
| references/_sections.md | カテゴリの定義と順序 |
| metadata.json | バージョンとソースの参照情報 |