mcpField
A
audit-ppmplugin

audit-ppmplugin

github.com/microsoftテスト・品質1.1k71 回インストール今日更新
リポジトリ

audit-ppmplugin について

audit-ppmpluginは、完成した.ppmpluginをアップロード前に検証する読み取り専用スキルです。パッケージ形式、マニフェスト、バイナリ、ランタイムの読み込みに関する問題をローカルで確認します。

主な機能

  1. 01パッケージの読み取り専用検査
  2. 02ZIP構成とマニフェストの検証
  3. 03マニフェストとバイナリの整合性確認
  4. 04Android DEXの整合性と漏出スキャン
  5. 05iOSフレームワーク構造の確認

活用シーン

  1. 01アップロード前のパッケージ検証
  2. 02マニフェストとプラットフォームバイナリの不一致特定
  3. 03Android DEXやiOSフレームワークの読み込みリスク検出

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/microsoft/power-platform-skills --skill audit-ppmplugin

インストール後、Claude Code で/audit-ppmpluginを入力して読み込まれたか確認します。

詳細

/audit-ppmplugin

検証ゲートです。/assemble-ppmplugin はバンドルが正しい形式であることを証明します。このスキルは、wrap ランタイムで実際に読み込まれ、ディスパッチされることを証明します。ほとんどの .ppmplugin の失敗は表面化しません。バンドルのアップロードは問題なく完了した後、メソッドが native module 'X' not loaded を返したり、ランタイムがパッケージクラスをインスタンス化できなかったり、0x80040265(正規プレフィックス違反)でアップロードが拒否されたりします。こうした問題の発見には、wrap のビルドを一往復する手間がかかります。このスキルを使えば、ディスク上で数秒のうちに問題を見つけられます。

バンドルは読み取り専用です。検査のために一時ディレクトリへ展開しますが、.ppmplugin は変更しません。修正は上流に戻して行います(マニフェストの問題は /generate-ppmplugin-manifest、バイナリの問題はビルドスキル)。その後、再度アセンブルして監査します。

shared/ppmplugin-format.md の §1(レイアウト)、§3(正規プレフィックス)、§4(バリデーターのルール)、§5(Android DEX の要件)を読んでください。このスキルは、ビルド済みの成果物に対してこれらすべてを検証します。

このスキルで行わないこと

  • ビルド、zip 化、作成は行いません。完成した .ppmplugin を検査します。修正は上流で行い、再度アセンブルしてください。
  • zip をその場で修正しません。バンドルは変更不可の成果物です。変更すると、ステージング済みのソースとの整合性が失われます。代わりに、上流のスキルを案内します。
  • Dataverse へのアップロードやキャンバスアプリへの組み込みは行いません(ステージ 3 — 保留中)。「アップロード準備完了」とはローカル検証に合格したという意味であり、アップロード済みという意味ではありません。

ステップ 1 — 共通ドキュメントの確認、成果物の特定、前提条件の確認

  1. shared/shared-instructions.md と shared/ppmplugin-format.md を読んでください。
  2. 監査するバンドルを特定します。
    • ユーザーがパスを指定した場合は、そのパスを使います。ディレクトリの場合は、<dir>/*.ppmplugin を探します(複数ある場合はユーザーに確認します)。
    • 指定がなければ、アセンブルの出力を既定値として使います。リポジトリのルートにある ppmplugin/<name>.ppmplugin のうち、単一のもの(複数ある場合は最新ビルド)です。
    • 見つからない場合は、NEEDS_CONTEXT: no .ppmplugin to audit — run /assemble-ppmplugin first, or pass a path で停止します。
  3. 前提条件の確認(shared-instructions §9.2)。方針:すぐに諦めず、解決方法を探す(§1.5)。失敗とする前に、パスからツールを探してください。
確認項目検証方法見つからない場合
jar(JDK)jar --version — バンドルの一覧表示と展開に使用BLOCKED: jar not found — install a JDK (JDK 17) で停止
DEX スキャナーdexdump(推奨。/build-android-binary が $D8 を解決するのと同様に、/build-android-binary のように Android SDK の build-tools/*/dexdump 内を探します)。それ以外は strings(mac/Linux には付属)どちらもない場合、DEX の文字列チェックは警告に切り替わります(「DEX scan skipped — no dexdump/strings」)。構造チェック(マジック値、サイズ)は引き続き実行されます。Windows でどちらもない場合は、その不足を記録します。
  1. 展開:元のファイルは読み取り専用のまま、新しい一時ディレクトリにバンドルを展開して検査します。
    work=$(mktemp -d); ( cd "$work" && jar xf "<abs path to .ppmplugin>" )
    
    $work = New-Item -ItemType Directory -Force (Join-Path $env:TEMP "ppm-audit"); Push-Location $work; jar xf "<abs path>"; Pop-Location
    

以降の各ステップでは、検出事項を [SEVERITY] <check-id>: <result> の形式で一覧に追加します。最初の CRITICAL で止めず、ユーザーが一度に修正できるよう、すべての項目を収集してください。重要度は次のとおりです。CRITICAL(アップロード時またはデバイス上で失敗するため、ブロックします)、WARNING(誤りの可能性が高いものの、動作する場合もあります)、INFO(スタイルまたは参考情報)。


ステップ 2 — カテゴリ A:ZIP の構造

jar tf "<.ppmplugin>" の出力を使用します。バンドルはネイティブのみです(形式 §1。ルートに manifest.json があり、宣言された android/ / ios/ スライスのみを含みます)。

チェック検証内容重大度
zip-manifest-at-rootmanifest.json がアーカイブのルートにあるCRITICAL
zip-only-native-slicesトップレベルのエントリ ⊆ { manifest.json、android/、ios/ }CRITICAL
zip-no-ts-js-layersrc/、*.ts、*.tsx、*.js、extension.js、extension.hbc がどこにもない — バンドルに TS/JS レイヤーは含まれませんCRITICAL
zip-no-build-dirsandroid-build/、ios-build/、node_modules/、dist/、build/ が混入していないCRITICAL
zip-no-stray-filesMETA-INF/、.DS_Store、ドットファイル、入れ子になった *.zip / *.ppmplugin がないWARNING
zip-reasonable-sizeバンドルが 50 MB 未満(これを超える場合、通常は React または node_modules が誤って含まれています)WARNING

src/ ツリー、extension.js / .hbc、または任意の *.ts がある場合は、SDK 時代/JS レイヤーの混入です — CRITICAL としてフラグを立て、形式 §6 を参照します(バンドルに含まれるのはネイティブバイナリのみです)。


ステップ 3 — カテゴリ B:マニフェストのスキーマ、バリデーターのルール、フィールドの混入

抽出した manifest.json を読み込みます。構築済みのアーティファクトに対して、ppmplugin-format §4 のすべてのルールを再実行します(二重の防御 — /generate-ppmplugin-manifest の後にマニフェストが手作業で編集されている可能性があります)。

チェック検証内容重大度
mf-name-shapename が ^[a-z0-9][a-z0-9-]{0,63}$ に一致するCRITICAL
mf-canonical-prefix各 receivers[].nativeModule が name の正規プレフィックスで始まる(- / _ で分割し、各セグメントを PascalCase にして結合 — §3)(Ordinal、大文字と小文字を区別)CRITICAL
mf-reserved-prefixnativeModule が予約済みプレフィックスで始まらない(§4 の大文字と小文字を区別しないリスト:Microsoft、MS、Intune、Wrap、Pcf、PowerApps、…)CRITICAL
mf-reserved-exact-knownnativeModule が、ローカルで確認済みの互換性のない名前のサブセット(§4:DeviceInfo、AuthenticationHelper、NetworkClient、DataverseOfflineProvider、IntuneMAM)に一致しない。すべてを網羅しているわけではありません。修正するには、getName() を予約されていない形式に変更し(Module サフィックスまたはベンダープレフィックスを追加)、再導出しますCRITICAL
mf-nativeModule-generic-nounnativeModule が汎用的なプラットフォーム名詞のヒューリスティック ^(Device|Network|File|Audio|Camera|Sensor|Location|Storage|Notification|Bluetooth|Wifi|Media|Photo|Contact|Calendar|Battery) に一致しない — こうした裸の名前は拒否リストに載りやすく、共有 NativeModules 名前空間で衝突しやすいものです。(ヒューリスティックであり、実際のリストではありません — DeviceInfo はローカルでは READY と判定されましたが、サーバーに拒否されました。これがあれば警告が出ていたでしょう。)WARNING
mf-methodsすべての receivers[].methods が存在し、空でなく、32 件以下で、それぞれが ^[a-zA-Z_$][a-zA-Z0-9_$]{0,127}$ に一致するCRITICAL
mf-receiver-name各 receivers[].name が JS 識別子の正規表現に一致するCRITICAL
mf-version-presentversion が空でない文字列であるWARNING
mf-no-sdk-fieldsマニフェストに entrypoints.js、entrypoints.ts、extension.js、extension.hbc、extensionClassName、jsLayer フィールドがない。これは当方のネイティブのみのポリシーであり、サーバー側のゲートではありません — wrap injector は entrypoints.js を任意として扱い、受け入れます。ネイティブのみのバンドルであることを明確に保つため、これらにフラグを立てます。ブロックはせず、WARNING とします。WARNING

レポートには、互換性のない完全一致名の既知のサブセットのみがローカルでチェックされる (mf-reserved-exact-known)ため、ローカルで問題がなくても、アップロードサービスがその名前を 受け入れるとは限らないことを記載してください。


ステップ 4 — カテゴリー C:マニフェストとバンドルの整合性

宣言された entrypoints は、zip 内の実際のバイナリと完全に一致している必要があります。(/assemble-ppmplugin はビルド時にこの整合性を取りますが、完成したバンドルを監査することで、手編集されたマニフェストや手作業で zip 化されたバンドルを検出できます。)

チェック検証内容重大度
consistency-has-nativeentrypoints.android / entrypoints.ios の少なくとも一方が宣言されている(マニフェストのみのバンドルには読み込む対象がない)CRITICAL
consistency-android-dexentrypoints.android が宣言されている場合 → zip 内に android/<entrypoints.android.dex> が存在するCRITICAL
consistency-ios-frameworkentrypoints.ios が宣言されている場合 → zip 内に ios/<entrypoints.ios.framework>.framework/ が存在する(フラットなフレームワーク。カテゴリー E の ios-flat-framework-not-xcframework を参照)CRITICAL
consistency-no-orphanzip 内のすべての android/ または ios/ スライスに対応する宣言済みエントリーポイントがある(ランタイムがルーティングしない未宣言のバイナリがない)WARNING
consistency-packageClass-shapeentrypoints.android.packageClass が完全修飾 Java クラス名である(^([a-z][a-z0-9_]*\.)+[A-Z][A-Za-z0-9_]*$)CRITICAL
dispatch-composite-key複合ルーティングキー <name>/<receivers[].name> の形式が正しい(両方のセグメントが存在し、サフィックスが JS 識別子として有効)こと。このキーをディスパッチャー PCF が ReceiverKey としてバインドし、NativeModules.<nativeModule>.<method> に到達します(形式 §2 — ランタイムディスパッチの契約)WARNING

ステップ 5 — カテゴリー D:DEX の整合性と SDK 漏洩のバイトスキャン(Android)

entrypoints.android が宣言されている場合のみ実行します。DEX は DexClassLoader 経由で読み込まれます(形式 §5)。以下のチェックでは、読み込み可能であり、SDK 時代の漏洩がないことを確認します。利用可能な場合は dexdump -l plain <dex>、それ以外の場合は strings <dex> を使用します。どちらもチェック対象の文字列/型プールを出力します。

チェック検証内容重大度
dex-magic先頭のバイトが dex\n03[5-9] である(有効な DEX)CRITICAL
dex-has-packageClassentrypoints.android.packageClass の型記述子(Lcom/.../<Pascal>Package;)が DEX 内にある。それ以外の場合、ランタイムのクラス読み込みに失敗しますCRITICAL
dex-has-module-classモジュールクラスの記述子が DEX 内にあるCRITICAL
dex-no-INativeExtensionINativeExtension、INativeOperation、INativeExtensionContext の文字列がない。SDK 時代のシンボルがネイティブ専用バンドルに含まれています。クリーンさに関するポリシーであり、サーバーのゲートではありません。プレーヤーはこれらを無視します(古いパターンのプラグインでも、receivers[] が実在するネイティブモジュールに対応していればディスパッチされます)。そのため検出結果は示しますが、ブロックはしません。WARNING
dex-no-jslayer-loaderHermesBytecodeLoader、WrapPluginJsLayerLoader の文字列がない。JS レイヤーローダーはネイティブ専用 DEX に含まれるべきではありません(同様に、警告のみでブロックしません)。WARNING
dex-no-sendAsyncsendAsync シンボルがない。SDK トランスポートが漏洩している可能性がありますWARNING

これらの SDK 漏洩チェックは、ネイティブ専用の契約の要です。コンパイルが正常に完了しても、これらのシンボルが推移的に取り込まれる可能性があります。ビルド済み DEX をスキャンすることだけが、それらを検出できる手段です。


ステップ 6 — カテゴリ E:iOS フレームワークの構造(wrap-CI ゲート)

entrypoints.ios が宣言されている場合のみ実行します。CRITICAL セットは、ランタイムの dlopen による読み込みと wrap 署名パイプラインで必要となる項目に限定されています。アンブレラヘッダーとモジュールマップはビルド時の衛生管理にすぎません(ローダーはバイナリを dlopen しますが、モジュールを import することはありません)。そのため、これらは INFO とし、存在しないことを理由に有効なバンドルをブロックしないでください。

チェック検証内容重大度
ios-flat-framework-not-xcframeworkバンドルに ios/<framework>.framework/ が含まれており、ios/<framework>.xcframework/ ではないこと。.xcframework が存在する場合は、wrap-CI のエラーをそのまま出力して FAIL にします("Framework '.framework' not found in plugin")。修正方法は /build-ios-binary で再ビルドすることです(デバイス用スライス、フラットなフレームワーク。-create-xcframework を削除)。CRITICAL
ios-binary-presentios/<framework>.framework/<framework>(Mach-O バイナリ。名前は正確に <framework>)が存在すること。ローダーは Frameworks/<framework>.framework/<framework> を dlopen するため、バイナリがない場合や名前が異なる場合は読み込めませんCRITICAL
ios-framework-has-infoplistios/<framework>.framework/Info.plist が存在し、CFBundlePackageType=FMWK、CFBundleExecutable=<framework>、CFBundleIdentifier、CFBundleShortVersionString、MinimumOSVersion が設定されていること。Info.plist のないフレームワークバンドルは無効で、wrap の codesign ステップで拒否されますCRITICAL
ios-framework-has-umbrellaios/<framework>.framework/Headers/<framework>.h のアンブレラヘッダーが存在すること。ビルド時の衛生管理のみであり、dlopen ローダーはモジュールを import しないため、存在しなくても読み込みには影響しませんINFO
ios-framework-has-modulemapios/<framework>.framework/Modules/module.modulemap が存在すること。ビルド時の衛生管理のみ(理由は同じ)であり、ランタイムでは必須ではありませんINFO
ios-moduleClass-matchentrypoints.ios.moduleClass が文書化された Obj-C クラス(RCT<Pascal>Module)であり、ソースにアクセスできる場合は .h/.m のクラス名も一致することWARNING
ios-moduleName-matchesソースにアクセスできる場合、.m が receivers[].nativeModule を返す + (NSString *)moduleName を宣言し、RCT_EXPORT_MODULE(...) を使用していないこと(wrap プラグインは +load ではなくランタイムに登録されます)CRITICAL

(Dynamic と Static の違い、デッドコードストリッピング、弱い React リンクなどの詳細な Mach-O イントロスペクションは、ビルド時に /build-ios-binary が担当します。ここでは、同梱されたフレームワークがローダーと wrap CI に取り込まれる形式であることを検証します。)


ステップ 7 — カテゴリ F:ソースとレシーバーの契約(リポジトリにアクセスできる場合)

これらのチェックには拡張機能のソースが必要です。監査が制御リポジトリで実行され(バンドルのソースツリーが存在する場合)は実行してください。それ以外の場合は、各項目を SKIPPED — source not reachable とし、ビルドスキルがビルド時にすでに検証済みであることを記載してください。

チェック検証内容重大度
src-getName-matchesKotlin モジュールの getName() リテラル == receivers[].nativeModule — 不一致の場合、デバイス上で native module 'X' not loaded が発生します(コンパイルは正常に成功するため、検出が最も困難です)CRITICAL
src-methods-annotatedreceivers[].methods[] の各エントリーに、対応する @ReactMethod(Android)/RCT_EXPORT_METHOD(iOS)があることCRITICAL
src-package-fqn-matchesReactPackage の FQN == entrypoints.android.packageClassCRITICAL
src-package-no-arg-ctor<Pascal>Package(Android)に public な引数なしコンストラクターがあること — ランタイムは getDeclaredConstructor().newInstance() を使用します。プライマリーコンストラクターにパラメーターがある場合(class <Pascal>Package(...))、プラグインの読み込みはエラーなしで失敗します(Loaded 0 plugin package(s))。iOS での同等条件:モジュールクラスが [cls new] でインスタンス化されること(カスタム引数付きイニシャライザーがないこと)。CRITICAL
src-ios-module-no-arg-init(iOS)モジュールクラスに引数付きのイニシャライザーしかない状態でないこと — プレーヤーは NSClassFromString(moduleClass) → [cls new] を実行するため、引数を取るイニシャライザーしかないクラスはインスタンス化されず、読み込み時にスキップされます(§5b)。CRITICAL
src-ios-requires-main-queue(iOS)+ (BOOL)requiresMainQueueSetup が宣言されている場合、戻り値が NO であること(YES を返すと起動時にメインスレッドでセットアップが実行され、重い処理や例外を発生させる init があると、起動が停止またはクラッシュします — §5b)。WARNING
src-method-single-map-param各 @ReactMethod/RCT_EXPORT_METHOD が、リクエストパラメーターとして ReadableMap/NSDictionary を 1 つ(続いて Promise/リゾルバー)受け取ること — ラッププロキシは PCF の args:[request] を位置引数として展開するため、位置引数を複数取るメソッドにはペイロードが届きません(Promise のみを取る引数なしの操作は問題ありません)。WARNING
pcf-composite-key-matches-receiver隣接する PCF(pcf/<…>/index.ts)が COMPOSITE_KEY = "<name>/<receiver>" を宣言している(または ReceiverKey をバインドしている)場合、その値が <manifest.name>/<receivers[].name> と一致すること。一致しない場合、PCF はマニフェストに登録されていないレシーバーへディスパッチし、最初の呼び出しで失敗します(実際のバグ:PCF→Snapshot、マニフェスト→DeviceInfoExtension)。WARNING
pcf-uses-sendasync隣接する PCF(pcf/<…>/index.ts)が存在する場合、必ず window.PowerApps.NativeExtension.sendAsync(...) 経由でディスパッチし、cordova.exec(または任意の cordova.*)を直接呼び出してはいけません。 cordova.exec の直接呼び出しは CRITICAL な欠陥です。生の cordova グローバルは PCF サンドボックスに公開されていないため、デバイス上では呼び出しが何もせずに終了します(特に Android で顕著です)。zip/マニフェスト/DEX/キーのチェックには合格し、デバイス上でのみ失敗します。修正方法:ホストから注入される sendAsync グローバル経由で呼び出します(形式 §2)。CRITICAL
pcf-envelope-is-raw-object隣接する PCF が存在する場合、sendAsync ペイロードは 生の { method, args: [...] } オブジェクトでなければなりません。PCF 側で事前に JSON.stringify してはいけません(sendAsync 内部で文字列化されます)。sendAsync(key, JSON.stringify({...})) のように二重エンコードすると、プロキシの JSON.parse の結果が {method,args} ではなく文字列になり、BRIDGE_FAILED が発生します。修正方法:文字列化した値ではなく、オブジェクトリテラルを渡します。CRITICAL
pcf-args-is-array隣接する PCF が存在する場合、sendAsync ペイロード内の args は単独のオブジェクトではなく、JSON 配列(args: [request])であること — パース後、ラッププロキシは Array.isArray(parsed.args) ? parsed.args : [] を実行し、配列以外の値を破棄するため、ネイティブメソッドにペイロードが届きません(形式 §2)。WARNING
pcf-has-ambient-dts隣接する PCF が存在する場合、ホスト SDK パッケージをインポートする代わりに、window.PowerApps.NativeExtension.sendAsync の型を定義するローカルの PowerAppsNativeExtension.d.ts アンビエント宣言を同梱していること — これにより PCF のホスト非依存性を維持できます。INFO
src-no-ReactModule-annotationモジュールに @ReactModule アノテーションがないこと(DexClassLoader と互換性がありません — 形式 §5)CRITICAL
src-ctor-no-throwable-sideeffectsスキャン範囲はファイル全体ではなく、モジュールの構築クロージャに限定します — プライマリー/セカンダリーコンストラクター、Kotlin の init{} ブロック、構築時に実行されるプロパティ初期化子(private val x = …)、およびそれらが呼び出すすべてのプライベート関数(foo()/this.foo() を 1 段階たどる)です。この範囲内で次を検出します:(a)register*Callback(…, null)、または明示的な Looper を指定しない Handler()/Handler(...)。(b)try { } catch の中にない副作用を伴う呼び出し(register*/add*Listener/observe/getSystemService とその後の使用/ファイルまたはネットワーク I/O/runBlocking)。@ReactMethod の本体は範囲外です(構築時ではなく、呼び出しごとに実行されます)。モジュールは、Looper がない可能性のあるスレッド上でブリッジの起動時に即時に構築されます — 例外を捕捉しないと、UI が表示される前にホストの起動時クラッシュを引き起こします(形式 §5)。修正方法:初回呼び出しまで初期化を遅延する、Handler(Looper.getMainLooper()) を渡す、避けられない初期化を try/catch で囲む。**iOS での同等条件:**例外を発生させる/重い -init に同じスキャンを適用します(§5b に従い、[cls new] で即時にインスタンス化されます)。これはヒューリスティックです。決定的な検出は、/test-native-extension の Layer 1 による読み込み/初期化準備状況のアサーション(静的)と、Layer 5 によるランタイム起動クラッシュスキャンです。WARNING
pcf-response-unwraps-message隣接する PCF が存在する場合、sendAsync の成功パスでラップのレスポンスコンテナー({isUpdate, message:"<json>"})をアンラップすること — つまり、単一の JSON.parse だけで処理するのではなく、extractResponse のようなヘルパー(result.data をパースし、message を調べる)を呼び出します。単一のパースだけを行うハンドラーはコンテナーを受け取り、ネイティブ処理が成功しているにもかかわらず、すべての呼び出しで UNEXPECTED_PAYLOAD が発生します(形式 §2)。WARNING
src-promise-always-settledPromise/resolver+rejecter を取る各 @ReactMethod/RCT_EXPORT_METHOD に、到達可能な経路上で少なくとも 1 つの promise.resolve/promise.reject(または resolve(...)/reject(...))があること。Promise を取るメソッドが解決も拒否もせずに戻る可能性があると、呼び出し元で制御がハングし、コードやメッセージも返されません。WARNING
src-listener-releasedモジュール内の各 register*/add*Listener/observe/取得したマネージャーに対し、invalidate()/onCatalystInstanceDestroy()/ティアダウン内で対応する解放処理(unregister*/remove*Listener/.close()/.release())があること。解放処理のないコールバック登録はリークを引き起こし、破棄済みのモジュールに対して呼び出される可能性があります。WARNING

ステップ 8 — 判定を報告し、修正先へ案内する

判定を、見えるフェンス付きブロックとして出力します(shared-instructions §9.1)。各重要度には shared-instructions.md §9.3 に従って接頭辞を付けます。絵文字が表示されないターミナルでも意味が伝わるよう、文字列は残してください。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 audit-ppmplugin — pen-input.ppmplugin
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 Path: ppmplugin/pen-input.ppmplugin   (v0.1.4, Android-only, 3.4 KB)
 🔴 CRITICAL: 0   🟡 WARNING: 1   🔵 INFO: 0   SKIPPED: 4 (source not reachable)

 🟡 [WARNING] zip-no-stray-files: .DS_Store present in android/
   Fix: re-run /assemble-ppmplugin (it excludes dotfiles).

 ⓘ The upload service may apply additional name checks not represented locally.
 🟢 ✓ READY TO UPLOAD
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

CRITICAL がある場合、判定行は 🔴 で始まり、最終行は 🔴 ✗ NOT READY — fix the CRITICAL findings above とします。

クリーンでない各指摘には、具体的な次のアクションを示す Fix: を付け、担当スキルへ案内します(zip パッチは禁止です)。

  • マニフェストのルール/漏えいフィールド → 「/generate-ppmplugin-manifest を編集して再実行し、その後 /assemble-ppmplugin を実行します。」
  • DEX の漏えい/クラスの不足 → 「Android ソースに SDK シンボルが取り込まれています。インポートを削除し、/build-android-binary を再実行してから /assemble-ppmplugin を実行します。」
  • Zip 内の余分なファイル/構造 → 「/assemble-ppmplugin を再実行します。」

次に、AskUserQuestion を使って次のステップを提示します(shared-instructions §9.1)。

  • クリーン(CRITICAL 0 件): Re-run /generate-ppmplugin-manifest(該当先を示す WARNING がある場合のみ)/Stay — 手動でアップロードします(ステージ 3 はまだスキルではありません)を提示します。ユーザーが Run /… オプションを選択した場合は、同じターンで Skill ツールを使って実行します(§8 および §9.1「説明するのではなく実行する」)。
  • 問題あり(CRITICAL 1 件以上): 最初の CRITICAL の担当スキルに移るオプション、Run /generate-ppmplugin-manifest または Run /build-android-binary を提示し、その後、再アセンブルして再監査します。選択されたら実行します。

.extension-state.md の ## ppmplugin (third-party controls) ブロックを更新します。Audit: <name>.ppmplugin <ISO timestamp> — <CRITICAL>C/<WARNING>W (READY | BLOCKED)。

終了コード:

  • DONE — CRITICAL 0 件(クリーン、または WARNING のみの場合はその旨を記載)。バンドルはローカル検証に合格しています。
  • BLOCKED: audit found <N> CRITICAL — see findings — READY 判定を拒否します。アップロード可能であると主張しないでください。

コメント