--- name: check-cross-runtime description: Python canonical (`src/python_run/piper_plus/`, `src/python/piper_train/`, `src/python/g2p/piper_plus_g2p/`) を変更した PR で、 ONNX I/O 以外の追随漏れ (phonemizer / config schema / CLI flag / data 形式 / API 変更) を 7 ランタイム + 2 docker image 範囲で fail-fast 検出する。PR #443 (Python のみ修正 → 4 runtime + 2 docker で同バグ) / PR #391 (Python のみ修正 → 5 runtime 古いまま) の silent regression パターンへの対策。`/check-runtime-parity` (ONNX グラフ I/O 限定) と `/check-new-runtime-asset` (新規アセット配布) を補完する 3 つ目の sibling skill。 disable-model-invocation: true allowed-tools: Bash(git diff *) Bash(git status *) Bash(grep *) Bash(ls *) Bash(python *) Bash(uv run *) --- # Python 変更 → N ランタイム追随漏れチェック `src/python_run/piper_plus/*.py`、 `src/python/piper_train/*.py`、 または `src/python/g2p/piper_plus_g2p/*.py` を変更した PR で、 他 7 ランタイム (C# / Rust × 2 crate / Go / JS-WASM / C API / iOS SPM / Kotlin-Android / Swift G2P) + 2 docker image (python-inference / webui) が **同じ振る舞いに 追随しているか** を PR 提出前に確認する。 ## なぜこの skill が必要か (既存 2 skill との関係) | skill | スコープ | 補完範囲 | |---|---|---| | `/check-runtime-parity` | **ONNX グラフ I/O** (入出力名・形状・dtype) の変更 | 推論バックエンドだけ | | `/check-new-runtime-asset` | **新規データファイル** (JSON/TOML/TSV) の 7 箇所 metadata 配布 | wheel/sdist 同梱漏れだけ | | **`/check-cross-runtime` (本 skill)** | **それ以外の Python 変更** = phonemizer ロジック / config field / CLI flag / data file 形式変更 / API 変更 / docker recipe | 既存 2 skill の網からこぼれる広い領域 | 3 skill を組み合わせると、 Python canonical の変更が 7 ランタイム + 2 docker 全部に伝播しているかを 1 PR ごとにチェックできる。 ## 過去事故サマリ | PR | Python 修正内容 | 追随漏れ | 検出方法 | |---|---|---|---| | **#391** | speaker_embedding 形状修正 | 5 ランタイム (Rust / Go / C# / C++ / WASM) が古い形状のまま | PR #443 で偶然発見 (silent regression) | | **#443** | MB-iSTFT speaker_embedding 未対応の修正 | 4 runtime (Rust / Go / C# / WASM) + 2 docker image (python-inference / webui) で同じバグ | 手動レビューでようやく発見 | | **#384** | ZH-EN loanword 辞書 schema 拡張 | Rust / Go / C# / WASM / C++ で旧 schema のままパース失敗 | CI gate を後付けで作成 | 共通パターン: 1. Python で 1 関数 / 1 schema を 「サクッと」 修正 2. ローカル `pytest` は green、 CI も Python 観点だけ通る 3. レビュアーも Python diff しか見ない 4. 別 PR / リリース後ユーザー報告で 「他ランタイム壊れている」 が判明 この skill は **PR 提出前** に local で同じ修正が他ランタイムにも入っているか チェックリスト化する。 ## スコープ判定 (何を見るか) ### Canonical Python の 3 source of truth | カテゴリ | パス | 対応する他ランタイム | |---|---|---| | **A. ランタイム推論** | `src/python_run/piper_plus/*.py` (voice.py, http_server.py, text_splitter.py, timing.py, model_manager.py) | C# / Rust core / Go / WASM / C API / iOS SPM | | **B. G2P / phonemizer** | `src/python/g2p/piper_plus_g2p/*.py` (japanese.py, chinese.py, multilingual.py, ssml.py 等) | Rust piper-plus-g2p / Go phonemize / C# Phonemize / WASM g2p / C++ *_phonemize.cpp / Kotlin / Swift G2P | | **C. モデルエクスポート / 学習** | `src/python/piper_train/*.py` (export_onnx.py / vits/models.py / infer_onnx.py) | **ONNX I/O 変更なら `/check-runtime-parity` を使う**。 それ以外の周辺ロジック (chunking / scaling / sentence split 等) が本 skill 対象 | ### スコープ外 (本 skill では skip) - ONNX 入出力名 / 形状 / dtype の追加・改名 → `/check-runtime-parity` - 新規 JSON/TOML データファイル追加 → `/check-new-runtime-asset` - ruff format / type stub のみ → 何もしない - `tests/`, `docs/`, `.github/workflows/`, `CHANGELOG.md` のみ → 何もしない ## 影響度の 3 段階分類 | 影響度 | 例 | 推奨アクション | |---|---|---| | **軽微 (Low)** | 内部リファクタ・docstring・型注釈・テスト追加 | 他 runtime touch 不要、 PR description に明記して skip | | **中 (Medium)** | 新規 phonemizer 関数追加 / 既存関数の挙動拡張 / 新規 CLI flag / 公開 API への field 追加 | **Rust + Go + C# を同 PR で実装**、 WASM/C++/iOS/Kotlin/Swift は follow-up issue 化 | | **重大 (High)** | config schema 変更 / data file 形式変更 / model schema 変更 / 互換性破壊 | **全 7 runtime + 2 docker 同 PR が望ましい**。 不可なら release blocker issue を立ててから merge | 判定の目安: - **「他 runtime に同じバグが潜むか?」** → Yes なら Medium 以上 - **「version field を bump したか?」** → Yes なら High - **「ユーザーが直接触る面 (CLI / HTTP / library API) か?」** → Yes なら Medium 以上 ## 実行手順 ### 1. canonical Python が触られているか確認 ```bash CANONICAL_PATTERN='^(src/python_run/piper_plus/|src/python/piper_train/|src/python/g2p/piper_plus_g2p/).*\.py$' CANONICAL_TOUCHED=$(git diff --name-only origin/dev...HEAD | grep -E "$CANONICAL_PATTERN" || true) if [ -z "$CANONICAL_TOUCHED" ]; then echo "canonical Python は変更されていない — このチェックは skip 可" exit 0 fi echo "=== Canonical Python 変更検出 ===" echo "$CANONICAL_TOUCHED" ``` ### 2. ONNX グラフ I/O 変更なら別 skill に委譲 ```bash # /check-runtime-parity が担う領域は skip して責務分離 ONNX_IO_CHANGE=$(git diff origin/dev...HEAD -- \ src/python/piper_train/export_onnx.py \ src/python/piper_train/vits/models.py | \ grep -E '^[+-].*(speaker_embedding|prosody_features|language_id|input_names|output_names)' || true) if [ -n "$ONNX_IO_CHANGE" ]; then echo "WARNING: ONNX I/O 変更を検出。 /check-runtime-parity も併用すること" fi ``` ### 3. 7 ランタイム + 2 docker の touch 状況を一覧表示 ```bash declare -A RUNTIME_PATHS=( ["Python runtime"]="src/python_run/piper_plus/" ["C# Core"]="src/csharp/PiperPlus.Core/" ["C# CLI"]="src/csharp/PiperPlus.Cli/" ["Rust core"]="src/rust/piper-core/" ["Rust CLI"]="src/rust/piper-cli/" ["Rust G2P"]="src/rust/piper-plus-g2p/" ["Rust WASM"]="src/rust/piper-wasm/" ["Go runtime"]="src/go/piperplus/" ["Go CLI"]="src/go/cmd/piper-plus/" ["Go G2P"]="src/go/phonemize/" ["WASM npm (TTS)"]="src/wasm/openjtalk-web/" ["WASM npm (G2P)"]="src/wasm/g2p/" ["C API"]="src/cpp/" ["iOS SPM"]="Sources/PiperPlus/" ["Swift G2P"]="Sources/PiperPlusG2P/" ["Kotlin Android"]="android/piper-plus-g2p/" ["Docker python-inference"]="docker/python-inference/" ["Docker webui"]="docker/webui/" ) echo "" echo "=== 7 runtime + 2 docker の touch 状況 ===" CHANGED_FILES=$(git diff --name-only origin/dev...HEAD) for label in "${!RUNTIME_PATHS[@]}"; do path="${RUNTIME_PATHS[$label]}" if echo "$CHANGED_FILES" | grep -q "^${path}"; then echo " [TOUCHED ] $label ($path)" else echo " [UNTOUCHED] $label ($path) <-- 追随確認" fi done ``` ### 4. カテゴリ別 grep ヒント (詳細調査) #### A. ランタイム推論 (`src/python_run/piper_plus/` を変更したとき) ```bash # 変更された関数名を抽出 git diff origin/dev...HEAD -- src/python_run/piper_plus/voice.py | \ grep -E '^[+-][[:space:]]*def ' | sort -u # 同名 / 類似シグネチャを他 runtime で grep SYMBOL='synthesize' # ← 上で見つけたシンボル名で置き換え echo "=== Rust ==="; grep -rn "$SYMBOL" src/rust/piper-core/src/ 2>/dev/null | head echo "=== Go ==="; grep -rn "$SYMBOL" src/go/piperplus/ 2>/dev/null | head echo "=== C# ==="; grep -rn "$SYMBOL" src/csharp/PiperPlus.Core/ 2>/dev/null | head echo "=== WASM ==="; grep -rn "$SYMBOL" src/wasm/openjtalk-web/src/ 2>/dev/null | head echo "=== C++ ==="; grep -rn "$SYMBOL" src/cpp/ 2>/dev/null | head ``` #### B. G2P / phonemizer (`src/python/g2p/piper_plus_g2p/` を変更したとき) ```bash # 変更された言語ファイル名を取得 G2P_FILES=$(git diff --name-only origin/dev...HEAD | \ grep -E '^src/python/g2p/piper_plus_g2p/[a-z_]+\.py$' | \ xargs -I{} basename {} .py) for lang in $G2P_FILES; do echo "=== $lang phonemizer の 8 runtime 同期確認 ===" echo " Python: src/python/g2p/piper_plus_g2p/${lang}.py [canonical]" ls -la "src/rust/piper-plus-g2p/src/${lang}.rs" 2>/dev/null && echo " Rust: OK" || echo " Rust: MISSING" ls -la "src/go/phonemize/${lang}.go" 2>/dev/null && echo " Go: OK" || echo " Go: MISSING" ls -la "src/csharp/PiperPlus.Core/Phonemize/" 2>/dev/null | grep -i "$lang" || echo " C#: 要確認" ls -la "src/wasm/g2p/src/${lang}" 2>/dev/null && echo " WASM: OK" || echo " WASM: MISSING" ls -la "src/cpp/${lang}_phonemize.cpp" 2>/dev/null && echo " C++: OK" || echo " C++: MISSING" ls -la "android/piper-plus-g2p/src/main/" 2>/dev/null | grep -i "$lang" || echo " Kotlin: 要確認" ls -la "Sources/PiperPlusG2P/" 2>/dev/null | grep -i "$lang" || echo " Swift: 要確認" done ``` #### C. モデルエクスポート / 学習周辺 (`src/python/piper_train/` の非 ONNX-I/O 変更) ```bash # 例: infer_onnx.py の chunking / scaling ロジックを変更したとき、 # 同じロジックが他 runtime にも存在するかを確認 git diff origin/dev...HEAD -- src/python/piper_train/infer_onnx.py | \ grep -E '^\+' | grep -iE '(scale|chunk|silence|split|trim)' | head ``` ### 5. CLI / config / schema の version field 変更チェック `docs/spec/*.toml` 内の `version` / `schema_version` を bump したかを確認: ```bash SPEC_VERSION_CHANGE=$(git diff origin/dev...HEAD -- 'docs/spec/*.toml' | \ grep -E '^[+-].*(version|schema_version) *=' || true) if [ -n "$SPEC_VERSION_CHANGE" ]; then echo "=== Spec version 変更検出 — 影響度 High の可能性 ===" echo "$SPEC_VERSION_CHANGE" echo "" echo "対応する sync gate を必ず手動実行:" echo " uv run python scripts/check_phoneme_set_version.py" echo " uv run python scripts/check_language_id_map_contract.py" echo " uv run python scripts/check_dictionary_versions.py" fi ``` ### 6. 既存 sync gate を手動実行 (影響度 Medium 以上) 該当する canonical 変更ごとに、 対応する sync gate を **PR 提出前に local で** 通しておく (CI で気付くと cycle of shame): ```bash # 全 sync gate を一気に回す (推奨) uv run python scripts/check_loanword_consistency.py uv run python scripts/check_loanword_forward_compat.py uv run python scripts/check_pua_consistency.py uv run python scripts/check_dictionary_consistency.py uv run python scripts/check_dictionary_versions.py uv run python scripts/check_cli_flag_parity.py uv run python scripts/check_phoneme_set_version.py uv run python scripts/check_language_id_map_contract.py uv run python scripts/check_inference_input_contract.py uv run python scripts/check_short_text_contract.py uv run python scripts/check_text_splitter_contract.py uv run python scripts/check_streaming_api_contract.py uv run python scripts/check_ssml_contract.py uv run python scripts/check_speaker_encoder_contract.py uv run python scripts/check_audio_format_contract.py uv run python scripts/check_swift_g2p_contract.py uv run python scripts/check_pt_dialect_contract.py ``` 該当領域別の gate 早見表: | Python 変更箇所 | 走らせるべき gate | |---|---| | `chinese.py` (loanword 関連) | `check_loanword_consistency.py` + `check_loanword_forward_compat.py` | | `phonemize/` の PUA テーブル | `check_pua_consistency.py` | | 言語コード追加・変更 | `check_language_id_map_contract.py` | | 辞書フォーマット変更 | `check_dictionary_consistency.py` + `check_dictionary_versions.py` | | 新規 CLI flag 追加 | `check_cli_flag_parity.py` | | SSML 機能拡張 | `check_ssml_contract.py` | | `text_splitter.py` 変更 | `check_text_splitter_contract.py` | | streaming API 変更 | `check_streaming_api_contract.py` | | `speaker_encoder/` 変更 | `check_speaker_encoder_contract.py` | | 短テキスト戦略変更 | `check_short_text_contract.py` | | PT BR/EU 切替変更 | `check_pt_dialect_contract.py` | | Swift G2P 変更 | `check_swift_g2p_contract.py` | ### 7. Docker image の追随確認 (PR #443 の盲点) `src/python_run/piper_plus/` 変更時は、 ベースとなる docker image 2 個も touch されて いるかを確認 (Python ランタイムを wheel として再 install するだけで OK な場合と、 recipe を変える必要がある場合がある): ```bash echo "=== Docker image の touch 状況 ===" for d in docker/python-inference docker/webui; do if git diff --name-only origin/dev...HEAD | grep -q "^${d}/"; then echo " [TOUCHED ] $d" else echo " [UNTOUCHED] $d ← Python wheel の bump で十分か確認" fi done # requirements.txt / pyproject 内の piper-plus pin が古いか確認 grep -rn 'piper-plus' docker/python-inference/ docker/webui/ 2>/dev/null | \ grep -E '(==|>=|~=)' | head ``` ## 確認すべき事項 (チェックリスト) PR 提出前にメンタルチェック: - [ ] canonical Python の変更カテゴリ (A: 推論 / B: G2P / C: 学習) を分類した - [ ] 影響度 (Low / Medium / High) を判定した - [ ] Low 以外なら他 runtime の touch 状況を `git diff --name-only` で確認した - [ ] Medium なら Rust / Go / C# を同 PR で更新 (or follow-up issue 化) - [ ] High なら全 7 runtime + 2 docker を同 PR で更新 (or release blocker 立て) - [ ] 該当する `scripts/check_*.py` sync gate を local で pass - [ ] `docs/spec/*.toml` の version bump が必要か検討した - [ ] PR description に 「Python canonical + N 他ランタイム追随」 を明記 - [ ] follow-up issue がある場合は PR description にリンク - [ ] ONNX I/O 変更なら `/check-runtime-parity` も併用した - [ ] 新規データファイル追加なら `/check-new-runtime-asset` も併用した ## 影響度別 quick action ### Low (内部リファクタ・テスト追加・docstring) ```text PR description に以下を明記: > Python のみ変更。 他 runtime への追随不要 (内部リファクタ)。 > /check-cross-runtime: skip (Low) ``` ### Medium (新機能・CLI flag・API field 追加) 1. Rust + Go + C# を **同 PR で実装** 2. WASM / C++ / iOS / Kotlin / Swift G2P は **follow-up issue 化** 3. PR description に follow-up issue link ```bash # follow-up issue を作る例 gh issue create --title "[follow-up] を WASM/C++/iOS/Kotlin/Swift に追随" \ --body "PR #XXX で Python + Rust + Go + C# に実装済み。 残り 5 runtime に追随必要。" ``` ### High (config schema / data 形式 / 互換性破壊) 1. **全 7 runtime + 2 docker を同 PR** が原則 2. 不可なら release blocker label を付けて release から外す 3. `docs/spec/*.toml` の version bump 4. `docs/migration/` にマイグレーションガイド追加 ## トラブルシューティング | 症状 | 対処 | |---|---| | Python 変更が大きすぎて何が canonical か分からない | `git diff --stat origin/dev...HEAD` で行数の多いファイルから 3 カテゴリ (A/B/C) に分類 | | 影響度判定に迷う | 「他 runtime に同じバグが潜むか?」 を考える。 Yes なら Medium 以上 | | sync gate が無い領域を触ってしまった | `scripts/check_dictionary_consistency.py` をテンプレートに新規 gate を作成 (`/check-new-runtime-asset` 参照) | | 全 runtime 同 PR が大きすぎる | High なら release blocker、 Medium なら Rust/Go/C# だけ同 PR + follow-up issue | | docker image の wheel pin が古い | `docker/*/requirements.txt` または `pyproject.toml` で `piper-plus==X.Y.Z` を bump | | CI で sync gate fail | 該当 sync gate の PAIRS / mirror に追加する canonical entry が漏れている可能性 | ## 関連ドキュメント / skill - 姉妹 skill: [`/check-runtime-parity`](../check-runtime-parity/SKILL.md) — ONNX グラフ I/O 限定 - 姉妹 skill: [`/check-new-runtime-asset`](../check-new-runtime-asset/SKILL.md) — 新規データファイル 7 箇所配布 - 姉妹 skill: [`/check-loanword`](../check-loanword/SKILL.md) — ZH-EN loanword 7 mirror 同期 - 姉妹 skill: [`/check-pua`](../check-pua/SKILL.md) — PUA テーブル / fixture / config 整合 - 仕様: `docs/spec/inference-input-contract.toml` - 仕様: `docs/spec/short-text-contract.toml` - 仕様: `docs/spec/text-splitter-contract.toml` - 仕様: `docs/spec/phoneme-timing-contract.toml` - マイグレーション: `docs/migration/v1.11-to-v1.12.md` - memory: `feedback_data_asset_distribution.md` (7 箇所配布の背景) - PR #391 / PR #443 — Python のみ修正 → N runtime 追随漏れ silent regression の参考事例 - PR #384 — ZH-EN loanword schema 拡張で 5 runtime fail の事例