debug-extension について
ビルド済み `.ppmplugin` コントロールをデバッグするエンジニア向けの調査・修正スキルです。PCF、ディスパッチ契約、ネイティブモジュール、ビルド設定をたどり、証拠に基づいて修正を提案します。
主な機能
- 01症状を候補となる障害レイヤーに分類
- 02PCF からネイティブへのディスパッチを追跡
- 03プラットフォーム実装と応答形式を比較
- 04診断と確認後に修正を適用
活用シーン
- 01PCF のエラーコードや無反応の調査
- 02プラットフォーム間の挙動差の調査
- 03ネイティブモジュールが読み込まれない原因の特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill debug-extensionインストール後、Claude Code で/debug-extensionを入力して読み込まれたか確認します。
詳細
/debug-extension
ユーザーがビルド済みの .ppmplugin コントロールをテスト中に確認した障害を調査し、根本原因を特定して修正します。ラップバイナリはお客様のシェル内で実行され、logcat/Xcode コンソール/ネイティブデバッガーにはアクセスできません。そのため、通常得られる証拠は、PCF の ErrorCode/ErrorMessage、生の <name>Json 診断出力、ホストログの1行、またはユーザーが見た現象についての説明だけです。このスキルは、限られた証拠から根本原因を特定し、修正につなげます。
まず調査し、修正で解決します。 単に「この変更を適用する」フローとは異なり、/debug-extension は 症状 から始め、コードに触れる前に 原因 をさかのぼって調べます。原因が見つかったら、慎重な編集と同じ手順(仕様と実装の乖離の診断、コントラクトの整合性、最小限の編集、ゲート)に従って修正を提案し、適用します。
これは選択肢の一つであり、唯一の方法ではありません。 shared/shared-instructions.md §7.5 にあるとおり、修正は どの スキルからでも、通常の会話のターンでも適用できます。ユーザーがこのスキルを使うよう強制されたり、修正を妨げられたりすることはありません。このスキルが加えるのは、報告された問題への 構造化された対応 です。つまり、症状からレイヤーへのトリアージ、ディスパッチ経路の追跡、編集前に証拠に基づいて原因を特定する診断です。デバイス上で何かが壊れたものの、原因がまだわからないときに使用してください。すでに編集箇所がわかっている計画済みの機能変更なら、直接編集してください。
使用する場合:
- 「ボタンをタップしても何も起きません。エラーも UI もありません。」(無言の何も起きない状態)
- 「PCF に
ErrorCode: PARSE/ErrorMessage: ...と表示されます。」(追跡可能なコード) - 「画面の読み込みと同時にアプリがクラッシュします。」(起動時のクラッシュ)
- 「ホストログに
Loaded 0 plugin package(s)と表示されています。」(ネイティブ読み込みの兆候) - 「iOS では動きますが、Android では何も起きません。」(互換性/トランスポートの不具合)
- 「Done ボタンが誤ったデータを返します。」(動作の乖離)
使用しない場合:
- リポジトリがまだない →
/generate-native-extension。 - 新しいオペレーションを追加する →
/design-native-extension-feature、その後に生成。 - 報告された障害がなく、編集箇所がわかっている計画済みの変更 → そのまま編集(任意のスキル/チャット)。
- バイナリが生成されなかったビルド → これは ビルド の失敗です。まず
/generate-ppmpluginを実行し、そのステージ出力を確認してください。
generate- とは独立しています。* 拡張機能のリポジトリが検出されない場合、このスキルは実行されません。スキャフォールドの作成、依存関係のインストール、バイナリのビルド、バンドルの組み立ては行いません。診断してから、ファイルを編集します。
Step 0 — 拡張機能のリポジトリであることを確認する
次の順序でリポジトリを検出します。必須のシグナルが不足している場合は、BLOCKED: not an extension repo (no <X> found) として停止します。
| シグナル | 必須? | 確認方法 |
|---|---|---|
リポジトリルートに PRD.md が存在する | はい | 仕様は、観察された動作を比較する基準です。 |
リポジトリルートに package.json が存在する | はい | 任意のディレクトリではなく、生成された拡張機能リポジトリであることを確認します。 |
リポジトリルートに ARCHITECTURE.md が存在する | いいえ | 強く推奨されます。追跡対象となるディスパッチコントラクトとオペレーションごとの実装が記載されています。存在しない場合は懸念事項として記録してください。 |
リポジトリルートに .extension-state.md が存在する | いいえ | 参考情報です。過去の編集や乖離の記録は、デバッグの手がかりになります。存在しない場合は Step 9 で作成します。 |
ControlManifest.Input.xml を含む pcf/ フォルダー | いいえ | PCF の検出に使用します。Glob で pcf/ 内を検索してマニフェストを特定し、`has_pcf: true |
ppmplugin/ のビルド出力/.ppmplugin アーティファクト | いいえ | 参考情報です。バイナリがビルド済みであることを確認できます(このスキルは ビルド済み コントロールをデバッグします)。存在しない場合、障害はビルド前に起きている可能性があるため、その旨を記録してください。 |
PRD または package.json がない場合は、/generate-native-extension を提案して停止してください。
Step 1 — 共有ドキュメント、PRD、ARCHITECTURE、manifest、状態を読む
次の順序で読みます。
shared/shared-instructions.md— 定数、戻り値ステータスコード(DONE/DONE_WITH_CONCERNS/BLOCKED/NEEDS_CONTEXT)、安全規則。shared/error-codes.md— 正式なカタログと、症状 → 考えられる原因 → 確認箇所の対応表。トリアージ(Step 3)の中核となる入力です。最後まで読んでください。shared/naming-conventions.md— PRD の識別情報から、トレース対象のファイルパスを特定します。shared/repo-layout.md— 想定されるファイルツリー。shared/ppmplugin-format.md— ディスパッチの契約、ラップされたsendAsyncトランスポート、{ isUpdate, message }レスポンスコンテナー、ネイティブ読み込みモデル(§2、§5、§5b)。トランスポートや読み込みの失敗をトレースするうえで不可欠です。./PRD.md— 全体を読みます(識別情報、操作、想定される動作)。./ARCHITECTURE.md— 全体を読みます(SDK のピン留め、操作ごとの実装の解説、メッセージ契約、§5 のエラーコード、manifest の実装)。トレースはこれに沿って進めます。./manifest.json— コミット済みのディスパッチ契約(name、receivers[].method、receivers[].nativeModule)。./.extension-state.md— 以前の## Edits/## Debugエントリと、記録されている差分。多くの場合、最も早く手掛かりが見つかります。
shared/prereq-check.md は読み飛ばしてください。 デバッグではインストールや認証は行いません。修正の過程で PCF の npm run build が必要になった場合は、Step 8 でツールチェーンの不足が明らかになります。
shared-instructions.md §9.2 に従い、Step 1 の開始時に前提条件について1行で通知します。
Prereq check — /debug-extension: skipped (skill does no installs / auth / network — investigation only until a fix's smoke check).
Step 2 — バグレポートを収集する
症状を確認します。ユーザーが詳細を指定せずにスキルを実行した場合は、情報を尋ねます。次のうち、提示できるものをまとめて一度に尋ねてください(5回に分けて尋ねないでください)。
- 実際に起きたことと、期待していたこと(観察できる動作)。
- PCF に表示された
ErrorCode/ErrorMessage(または Power Fx でのSelf.ErrorCode/Self.ErrorMessage)。 - 生の
<name>Json診断出力(ワイヤ上のバイト列。トランスポートレベルのフォレンジックに使用します)。 - ホストログの行(例:
Loaded 0 plugin package(s)、native module '<x>' not loaded、method '<m>' not found、スタックトレース)。 - プラットフォーム(iOS / Android / 両方)と、発生するタイミング(起動時 / タップ時 / 操作後)。
- 再現手順(ある場合)。
トレース中に参照できるよう、生のレポートを作業コンテキストに保持してください。詳細を言い換えて失わないでください。正確なコードやメッセージは、最も有力な手掛かりです。そのままの内容を永続化しないでください。 .extension-state.md はリポジトリにコミットされており、貼り付けられたレポートには、生のレスポンス、スタックトレース、ホストログの行、ファイルパス、URL、トークン、顧客データが含まれることがよくあります。Step 9 では、代わりに機密情報を削除した1行の要約を記録します。詳しくは、そこでの機密情報削除ルールを参照してください。
ステップ 3 — トリアージ:症状を候補レイヤーに対応付ける
shared/error-codes.md(§2 モジュールコード、§3 トランスポートコード、§4 コードなしのシグネチャ)を使って、症状を可能性の高い順に1つ以上の候補レイヤーに分類します。
| レイヤー | 症状の例 |
|---|---|
PCF / トランスポート(pcf/<Pascal>PCF/index.ts) | PARSE、UNEXPECTED_PAYLOAD、BRIDGE_FAILED、NOT_IN_WRAP。タップしても何も起こらない。すべての呼び出しが同じように失敗する。 |
ディスパッチ契約(./manifest.json ↔ ネイティブ名 ↔ PCF キー) | method '<m>' not found、native module '<x>' not loaded、ルーティングに関するメッセージを伴う BRIDGE_FAILED。一方のプラットフォームでのみ動作する。 |
ネイティブモジュール — Android(android/.../<Pascal>Module.kt) | Android での INTERNAL_ERROR / PERMISSION_DENIED / NO_ACTIVITY。Android のみでクラッシュする。Loaded 0 plugin package(s)。 |
ネイティブモジュール — iOS(ios/RCT<Pascal>Module.m) | iOS での INTERNAL_ERROR / PERMISSION_DENIED。iOS のみでクラッシュする、または何も起こらない。+moduleName / requiresMainQueueSetup の読み込みに関する問題。 |
| ネイティブの読み込み / ライフサイクル(コンストラクター、パッケージクラス) | UI が表示される前の起動時にクラッシュする。モジュールが読み込まれない。 |
ビルド設定 / RN の固定バージョン(package.json、android/build.gradle、.podspec) | React ヘッダー / 未定義シンボルのエラー。SDK レベルに依存する動作。ホスト側の RN と固定バージョンが異なる。 |
| 動作 / 仕様(ネイティブ操作の本体と PRD/ARCHITECTURE の不一致) | 結果が誤っている、コントロールがない、ペイロードが正しくない — エラーコードはなく、出力が誤っているだけ。 |
1つの報告が複数のレイヤーにまたがる場合があります(例:UNEXPECTED_PAYLOAD は通常 PCF の問題ですが、仕様に準拠していないネイティブ応答が原因の場合もあります)。可能性のあるレイヤーをすべて挙げてください。ステップ 4 で確認または除外します。
トリアージするには報告の情報が不足している場合は、対象を絞った確認の質問を1つしてください(例:「両方のプラットフォームで失敗しますか、それとも片方だけですか?」)。それでも不明な場合は、NEEDS_CONTEXT: <不明な点> を出して終了してください。
ステップ 4 — 調査:経路をたどり、証拠を集める
候補となる各レイヤーについて、該当するファイルを読み、具体的な証拠に基づいて仮説を確認するか、除外します。 推測してはいけません。ファイルを開き、行を引用してください。
規約から導き出されるファイル(shared/naming-conventions.md を参照し、PRD の識別情報から <Pascal> / <lower> を置き換えます):
- PCF / トランスポート →
pcf/<Pascal>PCF/index.ts(invokeBridge、extractResponse、onTriggerの結果分岐、args: [request]、複合キー)、pcf/<Pascal>PCF/ControlManifest.Input.xml。 - ディスパッチ契約 →
./manifest.jsonのreceivers[]、ネイティブ側のgetName()(Android)/+moduleName(iOS)、PCF の複合キー<name>/<receiver>とmethod。3 つすべてが一致するか、相互に照合します。 - Android ネイティブ →
android/src/main/java/com/powerapps/<lower>/<Pascal>Module.kt(および<Pascal>CaptureActivity.kt)、ReactPackageクラス(public な引数なしコンストラクター)、android/src/main/AndroidManifest.xml。 - iOS ネイティブ →
ios/RCT<Pascal>Module.{h,m}(+moduleName、+requiresMainQueueSetup、引数なしの初期化)、表示される VC。 - ビルド / ピン留め →
package.json(RN ピン留め0.79.7)、android/build.gradle、ios/<Pascal>Extension.podspec。
トレース手法:
- ディスパッチ経路を最初から最後までたどる:PCF キー →
sendAsyncエンベロープ({ method, args: [request] })→ マニフェストのreceivers[]→ ネイティブメソッド → レスポンス JSON →extractResponse→ PCF 出力。どこかで途切れていれば、それがバグです。 - 報告にある特定のシンボル(エラーコード、フィールド名、メソッド名)を
ios/、android/、pcf/全体で grep し、それを出力または消費するすべての箇所を見つけます。 - 症状がプラットフォーム固有の場合は iOS と Android を比較します。差分が手がかりです。
- 生の
<name>Jsonが{ status, result?/error?, message? }の規約とラップ用の{ isUpdate, message }コンテナーに合っているか確認します。形状の不一致があれば、extractResponseと直接のパース処理の違いが原因である可能性があります。 error-codes.md §4 signaturesと照合します:Loaded 0 plugin package(s)→ Android パッケージの引数なしコンストラクター、PCF 内のcordova.exec→ 禁止(何も起きない)、React ヘッダーエラー → RN ピン留めの不一致。
トレースが操作ごとの実装に関わる場合は、shared/self-critique-protocol.md を読んでください。そのゲート(状態網羅、クロスプラットフォームの同等性、ライフサイクル)によって、仮説を絞り込めます。
ステップ 5 — 根本原因の診断とゲート
優先順位を付けて診断を提示します。各仮説は直感ではなく、証拠に基づいています。
Diagnosis for: "<verbatim symptom>"
1. [HIGH confidence] <one-line root cause>
Evidence: <file>:<line> — <what the code does / doesn't do>
Why it produces this symptom: <one sentence tied to error-codes.md>
Layer: PCF | dispatch contract | native-android | native-ios | native-load | build/pin | behavior
2. [MEDIUM confidence] <alternative cause>
Evidence: ...
Ruled out: <hypothesis> — <why the evidence eliminates it>
Recommended fix (for #1): <what would change, in which file(s)>
ゲート:Proceed with the fix for #1? (yes / investigate #2 instead / show me <file> / stop)。
stopの場合 →BLOCKED: user stopped after diagnosis(編集は行わず、診断はステップ 9 で記録します)。investigate #2の場合 → その仮説を掘り下げ、再度提示します。- トレース後に MEDIUM 以上の確度に達する仮説がない場合 →
NEEDS_CONTEXT: <what additional evidence is needed>を示して停止します(例:「生の<name>Json出力を貼ってください」または「クラッシュ時のホストログの行を提示してください」)。原因が確認されていないまま、修正をでっち上げてはいけません。
Step 6 — 修正計画 + 仕様と実装の乖離チェック + ゲート
ここで修正内容を導きます。まず、慎重に編集する場合と同じように分類してください。修正はコードパッチだけとは限りません。
| ケース | 修正内容 | アクション |
|---|---|---|
| B — コードの乖離(最も一般的) | すでに正しい仕様からコードが乖離しています(例:extractResponse の欠落、複合キーの誤り、例外の握りつぶし)。 | コードのみを修正します。<doc> §<n> にはすでに正しい動作が規定されています — コードのみを修正します。 と出力します。 |
| A — 仕様の誤り | 実際の動作は現在の PRD/ARCHITECTURE の記述どおりですが、その仕様が誤っています。 | まず PRD/ARCHITECTURE の編集を提案し(独立したミニゲート)、適用してからコードを導きます。 |
| C — 両方 | 仕様が曖昧/不完全で、コードも不完全です。 | ドキュメントの詳細を更新してから、コードを修正します。 |
次に、コード修正計画を提示します。
Fix plan:
<path/to/file>
- Replace: <specific symbol / region> → <replacement> (rationale tied to the diagnosis)
- Add: <specific addition>
Contract impact: <"none" | "method set / receiver / nativeModule moves — ./manifest.json + PCF key updated in this same change, re-staged via /generate-ppmplugin-manifest">
Total: N files changed.
Apply? (yes / no / show me <file>)
契約の境界。 修正によってメソッドのセット、レシーバー/ルーティング名、またはネイティブモジュール名(Android の getName()/iOS の +moduleName = <Pascal>Module)が変わる場合、この修正では 3 つの成果物を同時に更新します。ネイティブソース、コミット済みの ./manifest.json(receivers[]/methods を必要最小限の変更で編集)、および PCF の複合キー <name>/<receiver> です。その後、/generate-ppmplugin-manifest によってマニフェストを再検証し、再ステージします。これらに変更がない純粋な動作修正では、マニフェストに触れません。
修正に PCF の編集が必要なのに has_pcf: false の場合は、BLOCKED: this fix requires PCF edits but pcf/ is not scaffolded — run /generate-pcf-companion first として停止します。
ゲート:明示的な yes を待ちます。no の場合は BLOCKED: user declined fix plan とします(診断は引き続き記録されます)。show me <file> の場合は、提案する内容を出力して、再度確認を求めます。
Step 7 — 修正の適用
Edit ツールで計画した編集を適用します。ルール:
- 一括ではなく、必要最小限に。 診断で特定した行を変更し、関数やファイルを書き直さないでください。
Writeは本当に新しいファイルが必要な場合に限ります(デバッグではまれです)。 - ファイルごとにアトミックに。 1 つのファイルに対する編集はすべて連続して適用し、ファイルが中途半端な状態にならないようにします。
- ファイル間でもアトミックに(可能な範囲で)。 複数ファイルの一括編集が途中で失敗した場合は停止し、書き込まれたファイルと書き込まれていないファイルを報告して、途中まで書き込まれたファイルに
git diff/git checkoutを実行するようユーザーに伝えます。自動で元に戻してはいけません(破壊的であり、安全な操作の一覧にありません)。 - 付随する編集は禁止。 計画に記載されたファイルだけを編集します。無関係な問題はサマリーに記載し、この作業では修正しません。
Step 7.5 — 先回りプロトコルに照らした自己批評
適用後、変更したすべてのファイルを読み直し、
shared/self-critique-protocol.md に沿って確認します。報告された症状を解消する修正によって、新たな問題が生じることがあります(初期化を遅らせて Android のクラッシュを修正すると初回呼び出し時に競合が残る場合があり、複合キーを修正すると出力が孤立する場合があります)。
- 読み直し — 編集した各ファイルを、記憶に頼らずディスクから改めて読み込みます。
- 各ゲートを確認 — PRD の網羅性、ユーザージャーニー、レイアウト、状態、クロスプラットフォームの整合性、可逆性、ライフサイクル、仕様との乖離に加え、3P ゲート 10(ビルド可能性/バンドル適合性)と 11(PCF↔ネイティブの往復)を確認します。特にゲート 11を念入りに確認してください。デバッグ修正の多くは、まさに問題が発生した往復処理に関わります。
- 報告と修正 — プロトコルの重大度/自動修正の進め方に従って対応します。機械的な修正は 1 回にまとめて適用し(
yesは 1 回)、構造的な修正はそれぞれ個別にゲートを設け、判断が必要な事項は懸念点として提示します。最大 3 回まで繰り返します。
戻り値への影響:すべてのゲートに問題がなければ続行します。ブロッカーを保留した場合は BLOCKED: self-critique blockers — <list> とします(修正は適用済みのままです。ユーザーは判断後に再実行します)。懸念が残る場合は DONE_WITH_CONCERNS として続行します。
ステップ 8 — 修正を検証する
編集した範囲に絞って、修正が実際に症状に対処していることを検証します。
補間する前に検証してください。 <Pascal> は定数ではなく、PRD の識別情報に由来します。細工された値や不正な値を使うと、以下のコマンドが任意のシェルコマンドになったり、プロジェクトディレクトリの外に出たりするおそれがあります。実行前に、<Pascal> が ^[A-Za-z][A-Za-z0-9]*$ に一致すること(区切り文字、ドット、パスセグメントがないこと)を確認し、pcf/<Pascal>PCF を解決して実際のパスが pcf/ の内側にとどまることを確認したうえで、シェル構文に埋め込まず、単一の引用符付き引数として渡してください。失敗した場合は、BLOCKED: refusing to run a build command with an invalid <Pascal> value — <value> として停止してください。
| 編集したファイル | 検証 | 理由 |
|---|---|---|
任意の pcf/<Pascal>PCF/ .ts / ControlManifest.Input.xml | npm run build --prefix "$PCF_DIR" | リポジトリ内で唯一の TS ビルドです。型エラーとマニフェストエラーをすぐに検出します。 |
.kt / .m / XML のみ | 次を出力します:Native files fixed — compile + on-device validation defer to /build-android-binary // /build-ios-binary (via /generate-ppmplugin) and /test-native-extension Layer 5. Rebuild + retest on device to confirm the symptom is gone. | ここではネイティブコードの単独コンパイルは信頼できません。実際のコンパイルはビルドスキルで行います。 |
コントラクトを変更した場合(./manifest.json / 名前) | /generate-ppmplugin-manifest を再実行(再検証 + 再ステージ)し、その後 /generate-ppmplugin(再ビルド + /audit-ppmplugin)が必要であることを記載します。 | 修正をデバイスに反映するには、ステージされたマニフェストとバイナリを再生成する必要があります。 |
フィールドのバグに対する決定的な検証方法は、再ビルドしてデバイス上で再テストすることです。スモークチェックに合格しても、修正がコンパイルできたことを確認するだけで、症状がなくなったことまでは確認できません。サマリーでその点を明記してください。スモークチェックに失敗した場合は、失敗したコマンドと最も関連性の高いエラー行を報告し、自動で元に戻さず、BLOCKED: smoke check failed — <one-line cause> として停止してください(ステップ 9 には引き続き記録します)。
ステップ 9 — 状態ログ + サマリー
9.1 .extension-state.md を更新する
## Debug セクション(存在しない場合は作成)に追記します(上書きしないでください)。
## Debug
- <ISO timestamp> — <one-line summary of the bug + fix>
- Symptom: "<redacted one-line summary — see the redaction rule below>"
- Root cause: <located cause> (<file>:<line>)
- Diagnosis case: A | B | C
- Docs changed: <sections, or "none">
- Code changed: <file paths>
- Contract moved: <"none" | "receiver/method/nativeModule changed — ./manifest.json updated; re-staged via /generate-ppmplugin-manifest">
- Verification: <PCF npm run build → PASS | native — rebuild + device retest required | etc.>
- Status: DONE | DONE_WITH_CONCERNS: <reasons> | BLOCKED — <reason>
書き込む前に機密情報を削除してください。 .extension-state.md はリポジトリにコミットされるため、すべての項目が公開されるものとして扱ってください。Symptom 行は短い言い換えにします。エラーコード、影響を受ける操作、観測された動作を記載し、貼り付けられた報告内容をそのまま使わないでください。すべての項目から、秘密情報とトークン、PII と顧客データ、リクエスト/レスポンスのペイロードとその断片、絶対パスまたは内部ファイルシステムのパス、内部 URL とホスト名、原因の位置を示す 1 つのフレームを除くスタックトレースを削除してください。Root cause は、ソース管理にすでに存在するリポジトリ相対の <file>:<line> にしてください。修正の根拠として必要な詳細でも、安全に機密情報を削除できない場合はファイルには記載せず、チャット内に残してください。
9.2 最終サマリー + 次のステップ
1 つの段落で、特定した根本原因、修正内容、検証結果を述べ、症状が解消したことを確認するには、再ビルド後にデバイス上で再テストする必要があることを明確に伝えてください。その後、AskUserQuestion(shared-instructions §9.1)を使って次のステップを提案し、修正内容に合う選択肢を選んでください。
- /generate-ppmplugin を実行する —
.ppmpluginを再ビルドして再監査します(最初に./manifest.jsonを再検証 + 再ステージします)。ネイティブまたはコントラクトの修正に推奨される次のステップです。 - /test-native-extension を実行する — コントラクトを再検証します(レイヤー 0 のクロスチェック、レイヤー 4 の PCF コンパイル)。本格的な再ビルドの前に PCF またはコントラクトを修正した場合に適しています。
- /generate-pcf-companion を実行する — 修正に PCF の編集が必要で、
pcf/がまだスキャフォールドされていない場合にのみ実行します。 - このままにする — まずデバイス上で再テストします。
実行して説明する、ではなく実行するという HARD RULE(§9.1)に従い、ユーザーが Run /… の選択肢を選んだ場合は、同じターン内に Skill ツールを使ってそのスキルを実行してください。自分の判断で自動的に次のスキルまで実行してはいけません。
厳守事項
- 原因を特定せずに修正してはいけません。 すべての修正は、Step 4/5 のファイルと行番号の証拠に基づく必要があります。原因が確認できない場合は、推測でパッチを当てずに
NEEDS_CONTEXTとします。 - ゲートは2段階です。診断の後に修正計画を立てます。 コードを黙って編集してはいけません。ケースA/Cの仕様修正では、ドキュメントの編集にも先に独立したミニゲートを設けます。
- 無関係なファイルを消してはいけません。 変更するのはStep 6の計画に含まれるファイルだけです。ついでのリファクタリングは禁止です。
- 失敗時に自動で元に戻してはいけません。 状況を提示して停止し、ユーザーが
git diffを確認できるようにします。 - ファイル編集ポリシー — 3つのカテゴリ(生成/編集の規律と同じです):
- ツール管理 — 編集禁止:
.git/、ロックファイル(pnpm-lock.yaml、package-lock.json、Podfile.lock)、生成されたバンドルとそのステージング領域(ppmplugin/staging/manifest.json、ppmplugin/の出力、すべての.ppmplugin)、PCF 生成アーティファクト(pcf/<Pascal>PCF/generated/)、ビルド出力(lib/、dist/、build/、pcf/<Pascal>PCF/out/)、*.bak.*、.claude/。(コミット済みでリポジトリルートにある./manifest.jsonはその逆です。コントラクトが変更された場合に編集する、コンシューマー側のファイルです。) - スキル管理 — 正規の状態更新ステップからのみ更新:
.extension-state.md(このスキルのStep 9)。フローの途中で直接編集してはいけません。 - ユーザーの依頼に応じて編集可能:
.gitignore、CHANGELOG.md、LICENSE、README.md、PCF のeslint.config.js/tsconfig.json、すべてのソース(ios/**、android/**、pcf/<Pascal>PCF/{index.ts,ControlManifest.Input.xml})。
- ツール管理 — 編集禁止:
- ファイルごとにアトミックに、ファイル間では可能な範囲で対応します。
- コントラクトの整合性を保ちます。 修正によってメソッドセット/レシーバー/nativeModule が変わる場合は、
./manifest.json、ネイティブ側、PCF をまとめて変更し、その後/generate-ppmplugin-manifestで再ステージングします。/test-native-extensionLayer 0 で検証します。 - 仕様とコードの整合性を保ちます。 ケースA/Cの修正では、まずPRD/ARCHITECTUREを更新します。ケースBの修正では、仕様との乖離として記録します。バグに合わせて仕様を黙って更新してはいけません。
- PCF は自動検出し、あるものと決めつけません。
pcf/<Pascal>PCF/ControlManifest.Input.xmlがない場合は、pcf/に書き込まず、修正にPCFが必要なら/generate-pcf-companionに進みます。 - 自動で次のフローへつなげてはいけません。Step 9.2で明示的に選択された場合はそれに従います。
- スモークチェックは、デバイス上での確認ではありません。 フィールドの症状が解消したことを確認するには、修正を再ビルドしてデバイス上で再テストする必要があると、必ずユーザーに伝えます。
シナリオ — フローの進み方
シナリオ1 — Androidで何も起こらない(PCFのトランスポートのバグ)
Step 2: Symptom = "tap does nothing, no error, only on Android."
Step 3: Triage → PCF/transport (error-codes.md §4 top row) + dispatch contract.
Step 4: Read pcf/.../index.ts — invokeBridge calls cordova.exec directly, no sendAsync.
Evidence: index.ts:NN. Matches the §4 signature (cordova undefined in PCF sandbox).
Step 5: [HIGH] cordova.exec used instead of the host-injected sendAsync → silent no-op,
worst on Android. Gate: proceed.
Step 6: Case B (ppmplugin-format §2 already specifies sendAsync). Fix plan: replace
cordova.exec with window.PowerApps.NativeExtension.sendAsync + args:[request].
Step 7: Apply. 7.5: self-critique Gate 11 (round-trip) clean.
Step 8: cd pcf && npm run build → PASS. Note: rebuild PCF + retest on device.
Step 9: Log case B; suggest /test-native-extension then /generate-pcf-companion publish path.
シナリオ2 — 起動時のクラッシュ(ネイティブの読み込み)
Step 2: Symptom = "app crashes the moment the control's screen opens", host log
"Loaded 0 plugin package(s)".
Step 3: Triage → native-load + native-android.
Step 4: Read the ReactPackage class — constructor takes an argument (no public no-arg
ctor). Matches error-codes.md §4 "Loaded 0 plugin package(s)".
Step 5: [HIGH] ReactPackage has no public no-arg constructor → runtime instantiation fails
fails → 0 packages loaded. Gate: proceed.
Step 6: Case B. Fix: add the public no-arg constructor.
Step 7: Apply. 7.5: Gate 10 buildability clean.
Step 8: Native-only → defer to /build-android-binary; rebuild + device retest required.
Step 9: Log; recommend /generate-ppmplugin (rebuild + audit).
シナリオ3 — 出力が誤っている(動作の乖離、仕様は正しい)
Step 2: Symptom = "Done returns the image without the drawing layer."
Step 3: Triage → behavior/spec + native (both platforms).
Step 4: Read both native op bodies vs ARCHITECTURE §3.n export step. Android composites
only the base bitmap; iOS composites both. Evidence: <Pascal>Module.kt:NN.
Step 5: [HIGH] Android export omits the ink layer; ARCHITECTURE §3.1 says composite both.
Gate: proceed.
Step 6: Case B (spec already correct). Fix Android export to match. Cross-platform parity.
Step 7: Apply. 7.5: Gate 5 parity now holds.
Step 8: Native-only → rebuild + device retest. Step 9: log; suggest /generate-ppmplugin.
失敗モード
| 発生すること | スキルの対応 |
|---|---|
PRD / package.json がない | BLOCKED: not an extension repo で停止し、/generate-native-extension を提案します。 |
| 確認質問を1回した後も、症状の情報が少なすぎてトリアージできない | NEEDS_CONTEXT: <what's needed> で停止します(例:<name>Json またはホストログの1行を貼り付けるよう依頼します)。 |
| MEDIUM以上の確信度に達する仮説がない | NEEDS_CONTEXT で停止します。修正をでっち上げてはいけません。 |
| ユーザーが診断後に停止する | BLOCKED: user stopped after diagnosis。診断は記録済みです。 |
| ユーザーが修正計画を拒否する | BLOCKED: user declined fix plan。診断(およびすでに適用されたドキュメント編集)は記録済みです。 |
修正にPCFの編集が必要だが、pcf/ がない | 停止し、/generate-pcf-companion に進みます。 |
| スモークチェックに失敗する | BLOCKED: smoke check failed — <cause>。編集内容はそのままにし、失敗を記録します。 |
編集ツールが失敗する(例:old_string が一意でない) | 正確なエラーを提示します。むやみに再試行せず、どのファイルに変更が適用されたかを報告します。 |
戻り値のステータス
各実行は、次のいずれかで終了してください。
DONE— 根本原因を特定し、修正を適用して、スモークチェックに合格しました(またはネイティブのみの場合は、再ビルドと再テストの注意書きを出力済みです)。DONE_WITH_CONCERNS: <list>— 修正を適用しましたが、懸念事項があります(自己批評による懸念、必要なファイルの欠落、確認できていない二次仮説)。BLOCKED: <reason>— ユーザーによる中断・辞退、スモークチェックの失敗、PRD の欠落、または必要な PCF の欠落。NEEDS_CONTEXT: <what's unclear>— 症状の情報が不足しているか、十分な確信を持てる原因に至っていません。