fix(pua): 多コードポイント音素レグレッションの完全修正と再発防止CI - #389
Merged
Merged
Conversation
v1.12.0 で配布された tsukuyomi config.json に未PUA化のmulti-codepoint音素 (ɔɪ, œ̃, ɐ̃) が混入し、Windows の C++ 推論で "is not a single codepoint" エラーが発生していた。 直接修正に加え、同種バグの再発を CI で防止する仕組みを導入。 ## データ修正 (PUA v2) - pua.json: version 1 → 2、ɔɪ (U+E062) / œ̃ (U+E063) / ɐ̃ (U+E064) を追加 - 全6ランタイムテーブルを同期: Python runtime, Rust, Go, JS, C# - token_mapper.py に欠落していた Swedish 9 エントリも追加 (Phase 1 完了) ## Fail-fast 化 - map_token(strict=True): 未登録 multi-codepoint で UnmappedMultiCodepointTokenError raise - _build_*_id_map() 末尾で全キー単一コードポイント不変条件を assert - update_phoneme_id_map(strict=True) を default に、--validate-only モード追加 ## CI ゲート (今回のバグを将来検出する仕組み) - scripts/check_pua_consistency.py: pua.json と全 6 ランタイムを entry-by-entry で比較 - scripts/regenerate_tsukuyomi_config.py: 配布config の PUA v2 化スクリプト - .github/workflows/pua-consistency.yml: 新規ワークフロー (consistency / inventory / fail-fast / Rust / Go / JS / config-key-validator) - g2p-cross-platform-ci.yml: 既存のcount-only検証を完全entry比較に置換 ## テスト追加 (L1-L5) - tests/test_pua_invariants.py: 27 tests (schema / inventory coverage / fail-fast / 生成物) - tests/test_update_model_config.py: 8 tests (strict / --validate-only / 警告動作) - 既存テスト 96 → 99 entries に更新 ## ドキュメント - docs/spec/pua-contract.toml: PUA契約 (canonical / 不変条件 / CI項目) - docs/spec/pua-test-matrix.md: L1-L7 テスト分類と各実装位置 - docs/spec/pua-codegen-design.md: 将来の codegen 化検討 (現状はStrategy C) - docs/guides/fix-multi-codepoint-config.md: 配布config修復手順 Closes: ɔɪ/œ̃/ɐ̃ leak in v1.12.0 tsukuyomi config Refs: docs/spec/pua-contract.toml
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR fixes a regression where multi-codepoint phoneme tokens leaked into distributed configs (breaking Windows C++ inference), and adds CI + tests to prevent future cross-runtime PUA mapping drift.
Changes:
- Adds 3 missing PUA v2 entries (ɔɪ/œ̃/ɐ̃), bumps PUA compat version, and synchronizes all runtime tables.
- Makes build-time mapping fail-fast (strict mode) and asserts generated
phoneme_id_mapkeys are single-codepoint. - Introduces cross-runtime consistency tooling + CI workflow plus new invariants/tests and operational docs/scripts.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/fixtures/g2p/phoneme_test_cases.json | Updates fixture expected PUA entry count to match v2. |
| src/wasm/g2p/test/test-pua-map.js | Updates JS/WASM tests for new entry count and range. |
| src/wasm/g2p/src/pua-map.js | Bumps compat version and adds v2 PUA mappings in JS table. |
| src/rust/piper-plus-g2p/src/token_map.rs | Bumps compat version; adds new fixed PUA mappings + updates tests. |
| src/python_run/piper/phonemize/token_mapper.py | Expands fixed mapping and adjusts dynamic PUA start. |
| src/python/tests/test_update_model_config.py | Adds tests for strict fail-fast + validate-only CLI behavior. |
| src/python/piper_train/update_model_config.py | Implements strict mode, validate-only path, and improved erroring. |
| src/python/g2p/tests/test_pua_invariants.py | Adds invariant tests for schema, coverage, strict mapping, and generated map keys. |
| src/python/g2p/tests/test_encode.py | Updates tests for strict/non-strict behavior and new entry count. |
| src/python/g2p/tests/test_compat.py | Updates expected mapping size. |
| src/python/g2p/piper_plus_g2p/encode/pua.py | Adds strict mode + new exception type; bumps compat version. |
| src/python/g2p/piper_plus_g2p/encode/id_maps.py | Forces strict mapping at build-time and asserts single-codepoint keys. |
| src/python/g2p/piper_plus_g2p/data/pua.json | Bumps version to 2 and adds 3 new PUA entries. |
| src/go/phonemize/pua_test.go | Updates exhaustive golden tests/counts and dynamic allocation expectations. |
| src/go/phonemize/pua.go | Adds new fixed PUA entries and adjusts dynamic allocation start. |
| src/csharp/PiperPlus.Core/Mapping/OpenJTalkToPiperMapping.cs | Extends fixed PUA mapping with v2 additions and updates metadata. |
| scripts/regenerate_tsukuyomi_config.py | Adds a repair/regeneration script for affected HF-distributed configs. |
| scripts/check_pua_consistency.py | Adds an entry-by-entry cross-runtime consistency checker against canonical pua.json. |
| docs/spec/pua-test-matrix.md | Documents the regression-prevention test layers and CI mapping. |
| docs/spec/pua-contract.toml | Formalizes the PUA contract/invariants and CI requirements. |
| docs/spec/pua-codegen-design.md | Proposes future codegen strategies to reduce manual sync burden. |
| docs/guides/fix-multi-codepoint-config.md | Adds an operator guide to detect/fix distributed multi-codepoint configs. |
| .gitignore | Ensures the new consistency script isn’t ignored by broad patterns. |
| .github/workflows/pua-consistency.yml | Adds a dedicated CI gate executing cross-runtime checks and tests. |
| .github/workflows/g2p-cross-platform-ci.yml | Replaces count-only validation with full cross-runtime comparison. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- ruff: import順、f-string、行長を修正 (scripts, g2p) - Swedish PUA テスト: 上限を v2 範囲 (0xE064) に更新 - C# テスト: PUA総数 96→99 に更新 (3 entry 追加分) - WASM init テスト: pua_compat_version 1→2 - pua-consistency.yml: piper_train deps を `--extra test` に修正
- test_update_model_config: dead setup を削除し、v1.12.0 回帰トークンが PUA v2 で正常マップされるテストに分離 - update_model_config CLI: --validate-only ヘルプを「any multi-codepoint key」に修正 (C++ は PUA mapping 有無に関わらず multi-CP 拒否) - pua-map.js: ヘッダ範囲を U+E064 に更新、PUA v2 entries の key/value を ASCII escape に統一 (フォント崩れ回避) - pua-contract.toml: 実在しないテストパス参照 (test_pua_inventory_integrity.py 等) を実装ファイル名に修正 - pua_test.go: dynamic PUA boundary コメントを 0xE064 (PT ɐ̃) ベースに更新
- test_g2p_golden.rs: pua_map_count assert を 99 (PUA v2) に - test_token_map_parity.rs: FIXED_PUA_MAP.len() assert を 99 に - ruff format で 3 ファイル再フォーマット
Rust の test_pua_map_individual が `pua_map.len() (96) != pua_map_count (99)` で fail していた。fixture の pua_map dict 自体が PUA v2 の 3 entries (ɔɪ/œ̃/ɐ̃ → 0xE062-0xE064) を含んでいなかったため。 - tests/fixtures/g2p/phoneme_test_cases.json: pua_map に 3 entries 追加 - pua.py / golden_test.go / pua_test.go: 「96 entries」コメントを 99 に統一
This was referenced May 5, 2026
ayutaz
added a commit
that referenced
this pull request
May 5, 2026
PR #389 で踏んだ「pua_map_count を 99 に bump したが pua_map dict は 96 のまま」 というドリフトの再発防止。fixture を pua.json から自動生成 + drift 検知 CI、 各ランタイムテストの絶対値 `99` を fixture/pua.json/サニティ由来に置換。 # B (fixture 自動生成) - scripts/regenerate_test_fixture.py 新設 - pua.json から phoneme_test_cases.json の pua_map / pua_map_count を再生成 - fixture の他フィールド (test cases) のフォーマットは触らない - --check モードでドリフト検出 (CI 用) - .github/workflows/pua-consistency.yml に "fixture-drift" job 追加 # B' (数値ハードコード集約) - Python: test_compat.py / test_encode.py — `== 99` を fixture pua_map_count 由来に - Rust: - test_g2p_golden.rs — `pua_map_count, 99` 削除 (FIXED_PUA_MAP.len() vs fixture で十分) - test_token_map_parity.rs — `assert_eq!(.., 99)` を pua.json 直読みに - token_map.rs — unit test を `>= 50` サニティに弱める (強い不変条件は CI gate) - Go: pua_test.go — `const want = 99` を `len(allFixedPUA)` に - JS: test-pua-map.js — `99` を `FIXTURE.pua_map_count` 参照に - C#: PhonemeConverterTests / SwedishPhonemizerTests — サニティ (>= 50) に弱める - SwedishPhonemizerTests のメソッド名 `_Is99` → `_IsPopulated` にリネーム ローカル検証: Python 31/31, JS 140/140, Go all pass.
ayutaz
added a commit
that referenced
this pull request
May 5, 2026
* chore(pua): fixture 自動生成 + 数値ハードコード集約 (B + B') PR #389 で踏んだ「pua_map_count を 99 に bump したが pua_map dict は 96 のまま」 というドリフトの再発防止。fixture を pua.json から自動生成 + drift 検知 CI、 各ランタイムテストの絶対値 `99` を fixture/pua.json/サニティ由来に置換。 # B (fixture 自動生成) - scripts/regenerate_test_fixture.py 新設 - pua.json から phoneme_test_cases.json の pua_map / pua_map_count を再生成 - fixture の他フィールド (test cases) のフォーマットは触らない - --check モードでドリフト検出 (CI 用) - .github/workflows/pua-consistency.yml に "fixture-drift" job 追加 # B' (数値ハードコード集約) - Python: test_compat.py / test_encode.py — `== 99` を fixture pua_map_count 由来に - Rust: - test_g2p_golden.rs — `pua_map_count, 99` 削除 (FIXED_PUA_MAP.len() vs fixture で十分) - test_token_map_parity.rs — `assert_eq!(.., 99)` を pua.json 直読みに - token_map.rs — unit test を `>= 50` サニティに弱める (強い不変条件は CI gate) - Go: pua_test.go — `const want = 99` を `len(allFixedPUA)` に - JS: test-pua-map.js — `99` を `FIXTURE.pua_map_count` 参照に - C#: PhonemeConverterTests / SwedishPhonemizerTests — サニティ (>= 50) に弱める - SwedishPhonemizerTests のメソッド名 `_Is99` → `_IsPopulated` にリネーム ローカル検証: Python 31/31, JS 140/140, Go all pass. * fix(ci): ruff の I001 違反修正 (PR #392) import block の後ろに空行を追加 (PEP 8 / ruff I001)。 * fix(ci): rustfmt 違反修正 (PR #392) * review: Copilot レビュー指摘 6 件を反映 (PR #392) - regenerate_test_fixture.py: - re.subn で match count を確認、FixtureFormatError で fail-fast - pua_map regex を [^{}]* で固める (将来 nested {} 混入時に silent ではなく失敗) - test-pua-map.js: - count assert を describe 評価時から it() 内に移動 (test として認識される) - test_token_map_parity.rs: - 重複していた repo-root 検出を repo_root() ヘルパーに抽出 - token_map.rs: - >= 50 サニティを include_str! で pua.json を埋め込んだ厳密比較に強化 - partial truncation を検出可能に - SwedishPhonemizerTests.cs: - >= 50 サニティを pua.json 直読みでの厳密比較に強化 - FindRepoFile ヘルパーで AppDomain BaseDirectory から walk-up * fix(ci): ruff format + cargo fmt 違反修正 (PR #392)
ayutaz
added a commit
that referenced
this pull request
May 7, 2026
PR #398 を 4 並列レビューエージェントで監査した結果の不整合・盲点を一括修正。 コピペで動かなかったコード片、未配信の version 参照、検出機構の抜け、契約強度 不足を中心に対処。 Critical (5): - C1: G2PError のドキュメントが実装と不一致 (`unsupportedLanguage` 等の 存在しないケース) を修正。guide / spec / troubleshooting テーブルを実装の 4 ケース (initializationFailed / phonemizeReturnedNull / invalidUTF8 / decodeFailed) に揃え、各ケースの意味を明記。 - C2: Package.swift `g2pVersion` を `1.13.0` から `1.14.0` に bump し、 guide / CLAUDE.md / decisions.md と整合。`g2pChecksum` が placeholder の意味と consumer がタグ前にビルドしようとした時の挙動をコメントで明示。 - C3: spec の cargo build 例 / CI yaml 例から `bundled-dicts,ffi` features が抜けていたのを補完 (decisions.md に書いてあるが実例では消えていた)。 - C4: PUA consistency gate に Swift PUAMap を追加。 scripts/check_pua_consistency.py に parse_swift() 実装、 pua-consistency.yml の paths に Sources/PiperPlusG2P/PUAMap.swift を追加、 job 名を "All 7 runtime PUA tables" に更新。実行で 99 entries / version=2 が他 6 ランタイムと一致を確認。Issue #389 の再発防止構造。 - C5: swift-g2p-ci.yml の Package.swift heredoc 上書きを Package.ci.swift (tracked file) 化。リリース版との drift が git diff で見える形に。 iOS(.v15) を platforms に追加して iOS-only API の compile 検証を CI でも 走らせる。 High (8): - H1: crate README の C FFI 関数名 `destroy` / `version` が実装に存在しない → 実体 (create / phonemize / free_string / free / available_languages) + 各関数の所有権・戻り値を表で明示。 - H2: THIRD_PARTY_LICENSES の `new_from_path()` / `new_from_paths()` が 実装に存在しない → 実体 (`new_with_dict` / `new(p1, p2)`) に修正。 - H3: docs/spec/swift-g2p.md:5 の Issue URL に全角カタカナ「ピ」混入で 404 → ASCII piper に修正。 - H4: swift-g2p-contract.toml に [compat] / [compat.breaking_change_policy] セクションを新設。pua_compat_version への参照、symbol 削除 / シグネチャ 変更 / JSON envelope 追加・削除 / PUA bump / default 言語変更で何が 壊れるかを明文化。 - H5: tests/PiperPlusG2PTests/PUAMapTests.swift を新規作成。 fixedMap.count == 99、compatVersion == 2、PUA 範囲、コードポイント衝突、 トークン衝突、forward/reverse round-trip、unknown lookup の 8 アサーション。 シンボリックな count も差分で検出される。 - H6: swift-g2p-ci.yml に Thread Sanitizer ジョブを追加 (continue-on-error でまずは observability 優先)。`@unchecked Sendable` の主張に裏付けが 生まれるよう ConcurrencyTests を `--sanitize=thread` で再実行。 - H7: ConcurrencyTests.testRepeatedInitDeinit にアサーションを追加。 各 iteration の availableLanguages 検証 + 10 反復ごとに phonemize して pipeline の健全性確認 (元の loop は assertion ゼロでノーオペ化していた)。 - H8 (M1 兼用): testAvailableLanguagesRoundTripsRequestedSet を追加。 Rust が新言語を返したとき Swift の Language enum が落とす silent drop を検出。 Medium (5): - M3: バイナリサイズ表記を guide で 1 表に統合。xcframework.zip ダウンロード / app 増分 / 未 strip staticlib の 3 観点を区別して記載。各言語のサイズ 寄与も実測値 (cmudict 3.7MB / pinyin 2.6MB / NAIST-JDIC 20MB) に揃えた。 - M4: NAIST-JDIC ライセンス全文 (BSD-3-Clause) と osdn.net 公式 URL を THIRD_PARTY_LICENSES.md に追加。guide でも表記揺れを解消し、 attribution 義務の有無を整理。 - M5: english.rs / chinese.rs の OnceLock+expect が catch_unwind 後に poison 状態で残る挙動をコメントで明示。iOS 経路 (new_bundled) は到達しないこと を併記して FFI consumer の安心材料に。 - M6: testEmptyTextDoesNotCrash の `try?` で例外握りつぶしを止め、 Rust 側仕様 (空入力 → 成功 or phonemizeReturnedNull のいずれか) を Swift にも明示反映。 - L: integration guide の言語表に「学習済みモデルは 6 言語」「ko/sv は G2P 出力できても TTS モデルがない」旨の注釈を追加。 検証: - python scripts/check_pua_consistency.py --verbose --check-version → 7 ランタイム全て 99 entries / version=2 一致 - cargo test -p piper-plus-g2p --features all-languages,naist-jdic, bundled-dicts,ffi --lib → 403 passed / 0 failed - yaml.safe_load で 2 ワークフロー syntax ok
ayutaz
added a commit
that referenced
this pull request
May 7, 2026
PR #398 を 4 並列レビューエージェントで監査した結果の不整合・盲点を一括修正。 コピペで動かなかったコード片、未配信の version 参照、検出機構の抜け、契約強度 不足を中心に対処。 Critical (5): - C1: G2PError のドキュメントが実装と不一致 (`unsupportedLanguage` 等の 存在しないケース) を修正。guide / spec / troubleshooting テーブルを実装の 4 ケース (initializationFailed / phonemizeReturnedNull / invalidUTF8 / decodeFailed) に揃え、各ケースの意味を明記。 - C2: Package.swift `g2pVersion` を `1.13.0` から `1.14.0` に bump し、 guide / CLAUDE.md / decisions.md と整合。`g2pChecksum` が placeholder の意味と consumer がタグ前にビルドしようとした時の挙動をコメントで明示。 - C3: spec の cargo build 例 / CI yaml 例から `bundled-dicts,ffi` features が抜けていたのを補完 (decisions.md に書いてあるが実例では消えていた)。 - C4: PUA consistency gate に Swift PUAMap を追加。 scripts/check_pua_consistency.py に parse_swift() 実装、 pua-consistency.yml の paths に Sources/PiperPlusG2P/PUAMap.swift を追加、 job 名を "All 7 runtime PUA tables" に更新。実行で 99 entries / version=2 が他 6 ランタイムと一致を確認。Issue #389 の再発防止構造。 - C5: swift-g2p-ci.yml の Package.swift heredoc 上書きを Package.ci.swift (tracked file) 化。リリース版との drift が git diff で見える形に。 iOS(.v15) を platforms に追加して iOS-only API の compile 検証を CI でも 走らせる。 High (8): - H1: crate README の C FFI 関数名 `destroy` / `version` が実装に存在しない → 実体 (create / phonemize / free_string / free / available_languages) + 各関数の所有権・戻り値を表で明示。 - H2: THIRD_PARTY_LICENSES の `new_from_path()` / `new_from_paths()` が 実装に存在しない → 実体 (`new_with_dict` / `new(p1, p2)`) に修正。 - H3: docs/spec/swift-g2p.md:5 の Issue URL に全角カタカナ「ピ」混入で 404 → ASCII piper に修正。 - H4: swift-g2p-contract.toml に [compat] / [compat.breaking_change_policy] セクションを新設。pua_compat_version への参照、symbol 削除 / シグネチャ 変更 / JSON envelope 追加・削除 / PUA bump / default 言語変更で何が 壊れるかを明文化。 - H5: tests/PiperPlusG2PTests/PUAMapTests.swift を新規作成。 fixedMap.count == 99、compatVersion == 2、PUA 範囲、コードポイント衝突、 トークン衝突、forward/reverse round-trip、unknown lookup の 8 アサーション。 シンボリックな count も差分で検出される。 - H6: swift-g2p-ci.yml に Thread Sanitizer ジョブを追加 (continue-on-error でまずは observability 優先)。`@unchecked Sendable` の主張に裏付けが 生まれるよう ConcurrencyTests を `--sanitize=thread` で再実行。 - H7: ConcurrencyTests.testRepeatedInitDeinit にアサーションを追加。 各 iteration の availableLanguages 検証 + 10 反復ごとに phonemize して pipeline の健全性確認 (元の loop は assertion ゼロでノーオペ化していた)。 - H8 (M1 兼用): testAvailableLanguagesRoundTripsRequestedSet を追加。 Rust が新言語を返したとき Swift の Language enum が落とす silent drop を検出。 Medium (5): - M3: バイナリサイズ表記を guide で 1 表に統合。xcframework.zip ダウンロード / app 増分 / 未 strip staticlib の 3 観点を区別して記載。各言語のサイズ 寄与も実測値 (cmudict 3.7MB / pinyin 2.6MB / NAIST-JDIC 20MB) に揃えた。 - M4: NAIST-JDIC ライセンス全文 (BSD-3-Clause) と osdn.net 公式 URL を THIRD_PARTY_LICENSES.md に追加。guide でも表記揺れを解消し、 attribution 義務の有無を整理。 - M5: english.rs / chinese.rs の OnceLock+expect が catch_unwind 後に poison 状態で残る挙動をコメントで明示。iOS 経路 (new_bundled) は到達しないこと を併記して FFI consumer の安心材料に。 - M6: testEmptyTextDoesNotCrash の `try?` で例外握りつぶしを止め、 Rust 側仕様 (空入力 → 成功 or phonemizeReturnedNull のいずれか) を Swift にも明示反映。 - L: integration guide の言語表に「学習済みモデルは 6 言語」「ko/sv は G2P 出力できても TTS モデルがない」旨の注釈を追加。 検証: - python scripts/check_pua_consistency.py --verbose --check-version → 7 ランタイム全て 99 entries / version=2 一致 - cargo test -p piper-plus-g2p --features all-languages,naist-jdic, bundled-dicts,ffi --lib → 403 passed / 0 failed - yaml.safe_load で 2 ワークフロー syntax ok
ayutaz
added a commit
that referenced
this pull request
May 7, 2026
PR #398 を 4 並列レビューエージェントで監査した結果の不整合・盲点を一括修正。 コピペで動かなかったコード片、未配信の version 参照、検出機構の抜け、契約強度 不足を中心に対処。 Critical (5): - C1: G2PError のドキュメントが実装と不一致 (`unsupportedLanguage` 等の 存在しないケース) を修正。guide / spec / troubleshooting テーブルを実装の 4 ケース (initializationFailed / phonemizeReturnedNull / invalidUTF8 / decodeFailed) に揃え、各ケースの意味を明記。 - C2: Package.swift `g2pVersion` を `1.13.0` から `1.14.0` に bump し、 guide / CLAUDE.md / decisions.md と整合。`g2pChecksum` が placeholder の意味と consumer がタグ前にビルドしようとした時の挙動をコメントで明示。 - C3: spec の cargo build 例 / CI yaml 例から `bundled-dicts,ffi` features が抜けていたのを補完 (decisions.md に書いてあるが実例では消えていた)。 - C4: PUA consistency gate に Swift PUAMap を追加。 scripts/check_pua_consistency.py に parse_swift() 実装、 pua-consistency.yml の paths に Sources/PiperPlusG2P/PUAMap.swift を追加、 job 名を "All 7 runtime PUA tables" に更新。実行で 99 entries / version=2 が他 6 ランタイムと一致を確認。Issue #389 の再発防止構造。 - C5: swift-g2p-ci.yml の Package.swift heredoc 上書きを Package.ci.swift (tracked file) 化。リリース版との drift が git diff で見える形に。 iOS(.v15) を platforms に追加して iOS-only API の compile 検証を CI でも 走らせる。 High (8): - H1: crate README の C FFI 関数名 `destroy` / `version` が実装に存在しない → 実体 (create / phonemize / free_string / free / available_languages) + 各関数の所有権・戻り値を表で明示。 - H2: THIRD_PARTY_LICENSES の `new_from_path()` / `new_from_paths()` が 実装に存在しない → 実体 (`new_with_dict` / `new(p1, p2)`) に修正。 - H3: docs/spec/swift-g2p.md:5 の Issue URL に全角カタカナ「ピ」混入で 404 → ASCII piper に修正。 - H4: swift-g2p-contract.toml に [compat] / [compat.breaking_change_policy] セクションを新設。pua_compat_version への参照、symbol 削除 / シグネチャ 変更 / JSON envelope 追加・削除 / PUA bump / default 言語変更で何が 壊れるかを明文化。 - H5: tests/PiperPlusG2PTests/PUAMapTests.swift を新規作成。 fixedMap.count == 99、compatVersion == 2、PUA 範囲、コードポイント衝突、 トークン衝突、forward/reverse round-trip、unknown lookup の 8 アサーション。 シンボリックな count も差分で検出される。 - H6: swift-g2p-ci.yml に Thread Sanitizer ジョブを追加 (continue-on-error でまずは observability 優先)。`@unchecked Sendable` の主張に裏付けが 生まれるよう ConcurrencyTests を `--sanitize=thread` で再実行。 - H7: ConcurrencyTests.testRepeatedInitDeinit にアサーションを追加。 各 iteration の availableLanguages 検証 + 10 反復ごとに phonemize して pipeline の健全性確認 (元の loop は assertion ゼロでノーオペ化していた)。 - H8 (M1 兼用): testAvailableLanguagesRoundTripsRequestedSet を追加。 Rust が新言語を返したとき Swift の Language enum が落とす silent drop を検出。 Medium (5): - M3: バイナリサイズ表記を guide で 1 表に統合。xcframework.zip ダウンロード / app 増分 / 未 strip staticlib の 3 観点を区別して記載。各言語のサイズ 寄与も実測値 (cmudict 3.7MB / pinyin 2.6MB / NAIST-JDIC 20MB) に揃えた。 - M4: NAIST-JDIC ライセンス全文 (BSD-3-Clause) と osdn.net 公式 URL を THIRD_PARTY_LICENSES.md に追加。guide でも表記揺れを解消し、 attribution 義務の有無を整理。 - M5: english.rs / chinese.rs の OnceLock+expect が catch_unwind 後に poison 状態で残る挙動をコメントで明示。iOS 経路 (new_bundled) は到達しないこと を併記して FFI consumer の安心材料に。 - M6: testEmptyTextDoesNotCrash の `try?` で例外握りつぶしを止め、 Rust 側仕様 (空入力 → 成功 or phonemizeReturnedNull のいずれか) を Swift にも明示反映。 - L: integration guide の言語表に「学習済みモデルは 6 言語」「ko/sv は G2P 出力できても TTS モデルがない」旨の注釈を追加。 検証: - python scripts/check_pua_consistency.py --verbose --check-version → 7 ランタイム全て 99 entries / version=2 一致 - cargo test -p piper-plus-g2p --features all-languages,naist-jdic, bundled-dicts,ffi --lib → 403 passed / 0 failed - yaml.safe_load で 2 ワークフロー syntax ok
ayutaz
added a commit
that referenced
this pull request
May 8, 2026
* docs(swift-g2p): Swift G2P 対応の設計仕様 + 利用ガイド + 事前決定事項 (#387) Issue #387 (Swift 向け G2P 対応) の実装着手前ドキュメントを整備。 方針 1 (既存 Rust piper-plus-g2p の C FFI を Swift から呼ぶ) を採用。 - docs/spec/swift-g2p.md 配布仕様。独立 xcframework (`libpiper_plus_g2p-ios.xcframework.zip`) として合成エンジンと分離配布する。Cargo + lipo で iOS slice を ビルド、cbindgen で C ヘッダ自動生成、`Package.swift` に `PiperPlusG2P` product を追加。8 言語フル (ja/en/zh/ko/es/fr/pt/sv) 対応。 - docs/guides/swift-g2p-integration.md iOS 開発者向け SPM 統合ガイド。`Phonemizer` Swift API、 単一/多言語/SwiftUI のコード例、トラブルシュート、ライセンス。 - docs/spec/swift-g2p-decisions.md 実装着手前のチェックリスト全 15 項目。重大な盲点として EN (cmudict 3.7MB) / ZH (pinyin 2.7MB) の辞書が現状 disk 依存で iOS App Sandbox では動かない件を指摘。`bundled-dicts` feature 新設による include_str! / include_bytes! 埋込を推奨。 * feat(g2p): Swift G2P prototype + iOS ビルド検証 (#387) Issue #387 (Swift 向け G2P 対応) の実装着手前検証。 Rust crate 側の必要変更を最小限のプロトタイプで実証し、 iOS 全 slice (device + simulator universal) でビルド成功を確認。 検証結果: - aarch64-apple-ios : 26s, libpiper_plus_g2p.a 84MB - aarch64-apple-ios-sim : 18s - x86_64-apple-ios : 18s - lipo (sim universal) : 168MB - bundled-dicts feature 有効 : 91MB (+6.4MB ≈ JSON 実サイズ) - 既存 unit test 373 件全 pass : 後方互換確認 - runtime smoke test : EN "Hello world" / ZH "你好世界" 両方とも IPA 出力 OK - cbindgen header 自動生成 : piper_plus_g2p.h 70 行 (5 関数 + opaque struct) 変更内容: - Cargo.toml + [lib] crate-type = ["staticlib", "cdylib", "rlib"] + bundled-dicts feature 新設 (default 非含有) - src/english.rs + EnglishPhonemizer::new_bundled() (#[cfg(feature = "bundled-dicts")]) cmudict_data.json を include_str! で埋込 - src/chinese.rs + ChinesePhonemizer::new_bundled() (#[cfg(feature = "bundled-dicts")]) pinyin_{single,phrases}.json を include_bytes! で埋込 - cbindgen.toml 新設 (C ヘッダ自動生成設定) - data/ 新設 (cmudict + pinyin JSON、src/cpp/ から複製、計 6.4MB) TODO (follow-up): - data/ の重複解消: build.rs か Cargo の package.include で src/cpp/ の JSON を参照する設計に切替 (現状は単純コピー) - ffi.rs: register_one("zh") で new_bundled() を呼ぶ分岐追加 - ffi.rs: register_one("en") も new_bundled() に切替 (CMUDICT_PATH より優先) - cbindgen.toml: PUA_COMPAT_VERSION 定数を [export.exclude] で除外 - Sources/PiperPlusG2P/, Package.swift, release-shared-lib.yml は別 PR * feat(swift-g2p): PiperPlusG2P SPM product 実装 (#387) Issue #387 (Swift 向け G2P 対応) の実装本体。 Rust piper-plus-g2p crate の C FFI を Swift から呼ぶ Plan A 採用。 合成エンジン (PiperPlus, ORT 必須) と独立した PiperPlusG2P product を 追加し、ORT 非依存で 8 言語の G2P を Swift から利用可能にする。 Step 1 — ffi.rs 拡張: - register_one("zh") を bundled-dicts feature 有効時に ChinesePhonemizer::new_bundled() 経由に変更 (従来は登録不可) - register_one("en") も bundled-dicts 有効時に new_bundled() 優先 - register_one("ja") は naist-jdic feature 有効時に new_bundled() 経由 - ffi tests に Chinese/English/Japanese bundled テスト追加 (396 件全 pass) - cbindgen.toml に [export.exclude] PUA_COMPAT_VERSION 追加 Step 2 — Sources/PiperPlusG2P/: - Language.swift : enum Language (8 言語、CaseIterable + Sendable) - G2PError.swift : enum G2PError (initFailed/null/utf8/decode + 説明) - PhonemizeResult.swift: struct PhonemizeResult (Codable, Sendable) - Phonemizer.swift : final class Phonemizer init(languages:) → piper_plus_g2p_create phonemize(_:language:) → JSON → PhonemizeResult availableLanguages → piper_plus_g2p_available_languages deinit → piper_plus_g2p_free Step 3 — Package.swift: - 新 product .library(name: "PiperPlusG2P", ...) - 新 binaryTarget PiperPlusG2PBinary (libpiper_plus_g2p-ios-v${VERSION}.xcframework.zip) - g2pVersion / g2pChecksum を追加 (合成側 checksum と同じ手動更新運用) - testTarget PiperPlusG2PTests を path: "tests/PiperPlusG2PTests" で追加 (既存 tests/ ディレクトリと整合、Linux 大文字小文字区別対応) Step 4 — release-shared-lib.yml: - build-g2p-ios job: ios-arm64 / ios-arm64_x86_64-simulator の matrix cargo build (3 target) → lipo (sim universal) → cbindgen → modulemap rustup targets, cargo cache, cbindgen cache 設定 FFI シンボル 5 個の export を nm で検証 - assemble-g2p-xcframework job: xcodebuild -create-xcframework で libpiper_plus_g2p-ios.xcframework.zip 生成、PrivacyInfo.xcprivacy 同梱 - release job: needs に assemble-g2p-xcframework 追加、 Package.swift の g2pChecksum 検証、xcframework zip との sha256 突合 artifact rename + Releases upload Step 5 — README / CHANGELOG: - CLAUDE.md ランタイム表に "iOS Swift G2P (SPM)" 行追加 - CHANGELOG.md [Unreleased] に PiperPlusG2P エントリ追加 (関連: docs/spec/swift-g2p.md, docs/guides/swift-g2p-integration.md) 検証実施結果: - cargo test (all-languages,naist-jdic,bundled-dicts,ffi) — 396 件 pass - cargo build aarch64-apple-ios (release) — 成功 libpiper_plus_g2p.a に piper_plus_g2p_* 5 シンボル全て export 確認 - YAML syntax check (release-shared-lib.yml) — pass 未対応 (follow-up): - 初回リリース時は g2pChecksum を手動更新 (placeholder 0000... のまま) - Sources/PiperPlusG2P/ の SourceKit 診断エラーは binaryTarget 未 resolve のため (xcframework 配信後に解消) - examples/swift/G2PExample/ サンプルアプリは別 issue 切出し - data/ JSON と src/cpp/ JSON の重複解消 (build.rs か symlink) * test(swift-g2p): cross-runtime golden phoneme tests を Swift 側にも追加 (#387) 既存の tests/fixtures/g2p/phoneme_test_cases.json (Python/Rust/JS で 共有されている cross-platform G2P 整合性フィクスチャ) を Swift wrapper からも参照し、同じ structural assertion を適用。 - Tests/PiperPlusG2PTests/GoldenPhonemeTests.swift 新設 - #filePath で source-relative にフィクスチャを解決 (コピー / 重複なし、 source of truth は tests/fixtures/g2p/ のまま) - 81 ケース (ja:7, en:3, zh:10, ko:4, es:15, fr:15, pt:15, sv:12) を 言語別 testN メソッドで実行 - 適用する assertion: - expected_token_count_min : トークン数下限 - expected_contains : 必須トークン - expected_not_contains : 禁止トークン - expected_tokens : 完全一致 (rule-based 言語向け) - expected_contains_any_tone: PUA codepoint 含有 (中国語声調) - expected_has_question_marker: 疑問詞マーカー (日本語 ?-suffix) これにより Python/Rust/JS と Swift で同じ入力に対して同じ structural output が得られることを CI でガードできる (xcframework 配信後)。 XCTest 診断の SourceKit 警告は binaryTarget 未 resolve のため (ローカル swift build では XCTest が見えない、CI で resolve)。 * refactor(g2p): G2P 辞書 JSON の source of truth を data/ に統一 (#387) prototype commit (00f4b95) で src/cpp/{cmudict,pinyin}*.json を src/rust/piper-plus-g2p/data/ にコピーしてしまった重複を解消。 新しい source of truth は piper-plus-g2p crate 内 data/ に統一。 C++ コード (piper.cpp, chinese_phonemize.hpp) はファイル名のみで 参照しており、ランタイムで findDictionaryFile() がパスを解決する設計 だったため、ソースツリー上の場所変更は CMake / CI の cp パスの 更新のみで完了。 更新ファイル: - src/cpp/cmudict_data.json : 削除 (data/ に移動済) - src/cpp/pinyin_single.json : 削除 (同上) - src/cpp/pinyin_phrases.json : 削除 (同上) - cmake/PiperPlusShared.cmake : install paths を data/ 参照に - cmake/Install.cmake : 同上 - .github/workflows/build-piper.yml : Linux / macOS / Windows の cp 更新 - .github/workflows/docker-test.yml : test/models/ + fallback の cp 更新 - .github/workflows/test-multilingual-tts.yml : test/models/ の cp 更新 これにより: - piper-plus-g2p crate の include_str! / include_bytes! は変更不要 (data/ のまま、相対パスは crate 内で完結) - C++ install ターゲットも変更前と同じパス (share/piper/dicts/) に 正しく辞書を配置 - リポジトリ全体での重複 (~6.4MB の git tracked dup) を解消 検証: - cargo test (all-languages,naist-jdic,bundled-dicts,ffi): 396 件 pass - cargo build aarch64-apple-ios: 成功、piper_plus_g2p_* 5 シンボル export 確認 - grep src/cpp/{cmudict,pinyin}*.json — 残存参照なし (build-* は gitignore) * chore(swift-g2p): 監査で検出した対応漏れに一括対応 (#387) エージェントチームによる徹底監査の結果、以下の対応漏れを修正: - LICENSE: THIRD_PARTY_LICENSES.md に CMU Pronouncing Dictionary (BSD-style) と pypinyin (MIT) の attribution セクションを追加 (bundled-dicts で埋込む辞書の義務) - README: README.md / README_EN.md / docs/guides/ios-integration.md に Swift G2P (SPM) への参照を追加。crate README を v0.4 に統一、 ffi / bundled-dicts feature を Feature Flags 表に明記 - FFI: default_languages() 関数を新設し、有効 features と bundled-dicts に応じて piper_plus_g2p_create(NULL) のデフォルト 言語を動的構築 (zh は chinese+bundled-dicts 同時有効時のみ) - Test: tests/PiperPlusG2PTests/ConcurrencyTests.swift を追加。 Phonemizer.@unchecked Sendable を 60 並列 phonemize / 20 並列 init / 100 反復 deinit で実証 - Spec: docs/spec/swift-g2p-contract.toml 新設 (FFI/ABI/JSON の機械 可読契約)。decisions.md に Resolution Summary を追加し全 D-* 項目 の対応状況を記録 - .gitignore: xcframework / SwiftPM build artifacts (*.xcframework/, .build/, .swiftpm/, *.xcodeproj/, DerivedData/) を除外 - CHANGELOG: 上記の追加項目を Unreleased エントリに反映 検証: cargo test -p piper-plus-g2p \ --features all-languages,naist-jdic,bundled-dicts,ffi で 396 lib + 22 integration tests すべて pass。 * ci(swift-g2p): PR で Swift G2P を自動検証する CI 整備 (#387) 監査で検出した「テスト・CI が死蔵」状態を解消: (A) release-shared-lib.yml に pull_request trigger 追加。build-g2p-ios と assemble-g2p-xcframework のみ PR で smoke run、他 jobs は `if: event_name != 'pull_request'` で tag push 限定を維持。文法エラー / path ミスを PR 段階で検出可能に。 (B) .github/workflows/swift-g2p-ci.yml 新設。macOS runner で cargo build (aarch64+x86_64 apple-darwin) → cbindgen ヘッダ生成 + 5 FFI シンボル検証 → xcodebuild -create-xcframework で local macOS xcframework 組立 → Package.swift を CI 専用 path-based manifest に上書き → swift test --filter PiperPlusG2PTests で PhonemizerTests / GoldenPhonemeTests / ConcurrencyTests を実行。xcframework 未 publish 状態でも PR で Swift wrapper を実機検証できる。 (D) ffi.rs に default_languages() / register_one() のテストを 7 件追加。 lib tests 396 → 403: - default_languages_full_feature_set: 8 言語確認 - default_languages_includes_rule_based: 規則ベース必須言語確認 - default_languages_excludes_zh_without_bundled_dicts (cfg gate) - register_one_unsupported_language: UnsupportedLanguage 確認 - register_one_zh_without_bundled_dicts: Phonemize エラー (cfg gate) - register_one_en/ko/ja_succeeds: 各 feature gate で正常系 - create_null_yields_default_set: NULL → default set の整合性 (E) PhonemizerTests.swift の assertion を Golden fixture と整合する 強い形に書換: - isEmpty のみだった 8 言語テストに具体トークン照合 (en "h", ja "k/o/n/i/a", es "o/l/a", fr "b", pt "o") - 最小トークン数の XCTAssertGreaterThanOrEqual - 中国語は PUA tone marker (E000-F8FF) を assertContainsPUA で検出 副作用: cargo fmt が english.rs::new_bundled() を自動整形。clippy 抑制 のため default_languages に #[allow(clippy::vec_init_then_push)] を追加 (cfg gate された push なので vec! 化できない)。 検証: cargo fmt -p piper-plus-g2p -- --check ✓ cargo clippy -p piper-plus-g2p --all-features -D warnings ✓ cargo test -p piper-plus-g2p --features all-languages,naist-jdic,bundled-dicts,ffi → 403 lib + 22 ffi tests pass python3 yaml.safe_load() 両 workflow ✓ * fix(ci): PR チェックの偽陽性 / 不足 COPY を解消 (#387) PR #398 で 3 件の CI が失敗していた根本原因: 1. Swift G2P CI (macOS) — nm の最終 pipeline が pipefail で exit 1 `nm -gU staticlib.a | grep | sort -u` の最終 pipeline が、staticlib に 外部 symbol を持たない archive member を含むときに macOS nm が非ゼロを 返し、`set -euo pipefail` で step 全体が失敗していた。出力には FFI symbol 5 つすべて表示されており、検証自体は成功していた。 `nm -gU ... || true` で出力をキャプチャしてから for-loop で symbol を 個別検証する形に変更。 2. Build piper-plus-g2p iOS ios-arm64 / ios-arm64_x86_64-simulator — release-shared-lib.yml の verify step も同じ形(最終 pipeline に `|| true` が無い)で失敗。同じ for-loop 検証へ統一。 3. test-cpp-inference (Docker) — cpp-inference/Dockerfile の builder stage が `src/rust/piper-plus-g2p/data/` を COPY していないため、 cmake/Install.cmake:25 が cmudict_data.json / pinyin_*.json を install する際に「No such file or directory」で失敗。data ディレクトリの COPY を追加。 * fix(ci): nm symbol 検証で grep -q の SIGPIPE を回避 (#387) 前回の修正 (b50af91) でも CI は依然失敗: awk: write error on /dev/stdout input record number 1301, file source line number 1 echo: write error: Broken pipe ::error::staticlib missing FFI symbol: piper_plus_g2p_create ただし step の末尾には FFI symbol 5 つすべて出力されており、staticlib には symbol が正しく存在していた。 根本原因: nm -gU の出力 (~10K 行) を `echo "$nm_out" | awk | grep -q` の pipeline に流していた。grep -q はマッチ発見時点で stdout を閉じて 即 exit する。awk/echo はその後さらに書き込もうとして SIGPIPE で 非ゼロ exit。`set -euo pipefail` 下では pipeline 全体が失敗扱いとなり、 ||の右辺の `exit 1` が発火。 対策: pipe を使わず bash の `case "$nm_out" in *" T _${sym}"*)` で 直接文字列マッチさせる。これなら nm 出力サイズに依存せず、`grep -q` の早期 exit による SIGPIPE が発生しない。失敗時の診断出力 (printf | grep) は pipe を使うが、fallback 用なので `|| true` で守る。 ローカルで 10,001 行のダミー nm 出力で 5 symbol すべて検出 / exit 0 を確認。 * fix(swift-g2p): OpaquePointer の冗長 wrap を削除 (#387) Swift G2P CI のコンパイルが以下のエラーで失敗: Phonemizer.swift:28:13: error: no exact matches in call to initializer OpaquePointer(piper_plus_g2p_create(ptr)) `- error: no exact matches in call to initializer candidate expects value of type 'UnsafeMutableRawPointer?' for parameter #1 (got 'OpaquePointer?') 根本原因: cbindgen が opaque forward-declared struct (`typedef struct PiperG2pHandle PiperG2pHandle;`) を出力し、Swift の clang importer はそれを直接 `OpaquePointer?` にブリッジする。よって `piper_plus_g2p_create(ptr)` の戻り値は既に `OpaquePointer?` で、 `OpaquePointer(...)` で再 wrap すると型不一致になる (OpaquePointer の init は UnsafeMutableRawPointer? しか受けない)。 修正: `OpaquePointer(...)` の wrap を削除し戻り値をそのまま `raw` 変数に代入。同じ理由で `piper_plus_g2p_free(.init(handle))` 等の `.init(handle)` も不要 (OpaquePointer は OpaquePointer? に暗黙変換可) なので `piper_plus_g2p_free(handle)` 等に簡素化。 修正対象: - Phonemizer.swift:28 (init) - Phonemizer.swift:36 (deinit) - Phonemizer.swift:48 (phonemize) - Phonemizer.swift:67 (availableLanguages) * fix(swift-g2p): PUA mapping を追加し golden test を Rust と整合 (#387) Swift G2P CI の swift test ステップで GoldenPhonemeTests が 5 言語で 10 failures。例: testSpanishGoldenCases: [es] perro grande — expected token 'rr' to be present, got: ["p", "ˈ", "e", "", "o", " ", "ɡ", "ɾ", ...] 根本原因: Rust 実装の `piper_plus_g2p_phonemize` は多文字音素トークン (rr / cl / dʒ / eː など 99 種) を **PUA codepoint 1 文字** にエンコード して JSON で返す。一方 fixture の `expected_contains` は人間可読な named token (`"rr"` 文字列) で記述されている。Rust 側のクロスランタイム テスト (`test_g2p_golden.rs::assert_case`) は `token_to_pua("rr")` で PUA char に変換してから contains 判定を行うが、Swift 側の同等テストは この変換を行っていなかったため fail。 修正: - `Sources/PiperPlusG2P/PUAMap.swift` を新規追加。Rust の `token_map.rs::FIXED_PUA_MAP` (99 entry / PUA v2) を Swift 側に 忠実に複製し、`tokenToPua` / `puaToToken` を public API として公開。 - `tests/PiperPlusG2PTests/GoldenPhonemeTests.swift` で `expected_contains` / `expected_not_contains` の検証時に `PUAMap.tokenToPua(needle)` で PUA 化してから contains 判定。 `expected_tokens` (exact match) は fixture 内に multi-char token が 含まれないため変換不要 (a/l/o/ˈ のみ)、改修対象外。 * fix(swift-g2p): エージェントレビュー指摘 19 件を解消 (#387) PR #398 を 4 並列レビューエージェントで監査した結果の不整合・盲点を一括修正。 コピペで動かなかったコード片、未配信の version 参照、検出機構の抜け、契約強度 不足を中心に対処。 Critical (5): - C1: G2PError のドキュメントが実装と不一致 (`unsupportedLanguage` 等の 存在しないケース) を修正。guide / spec / troubleshooting テーブルを実装の 4 ケース (initializationFailed / phonemizeReturnedNull / invalidUTF8 / decodeFailed) に揃え、各ケースの意味を明記。 - C2: Package.swift `g2pVersion` を `1.13.0` から `1.14.0` に bump し、 guide / CLAUDE.md / decisions.md と整合。`g2pChecksum` が placeholder の意味と consumer がタグ前にビルドしようとした時の挙動をコメントで明示。 - C3: spec の cargo build 例 / CI yaml 例から `bundled-dicts,ffi` features が抜けていたのを補完 (decisions.md に書いてあるが実例では消えていた)。 - C4: PUA consistency gate に Swift PUAMap を追加。 scripts/check_pua_consistency.py に parse_swift() 実装、 pua-consistency.yml の paths に Sources/PiperPlusG2P/PUAMap.swift を追加、 job 名を "All 7 runtime PUA tables" に更新。実行で 99 entries / version=2 が他 6 ランタイムと一致を確認。Issue #389 の再発防止構造。 - C5: swift-g2p-ci.yml の Package.swift heredoc 上書きを Package.ci.swift (tracked file) 化。リリース版との drift が git diff で見える形に。 iOS(.v15) を platforms に追加して iOS-only API の compile 検証を CI でも 走らせる。 High (8): - H1: crate README の C FFI 関数名 `destroy` / `version` が実装に存在しない → 実体 (create / phonemize / free_string / free / available_languages) + 各関数の所有権・戻り値を表で明示。 - H2: THIRD_PARTY_LICENSES の `new_from_path()` / `new_from_paths()` が 実装に存在しない → 実体 (`new_with_dict` / `new(p1, p2)`) に修正。 - H3: docs/spec/swift-g2p.md:5 の Issue URL に全角カタカナ「ピ」混入で 404 → ASCII piper に修正。 - H4: swift-g2p-contract.toml に [compat] / [compat.breaking_change_policy] セクションを新設。pua_compat_version への参照、symbol 削除 / シグネチャ 変更 / JSON envelope 追加・削除 / PUA bump / default 言語変更で何が 壊れるかを明文化。 - H5: tests/PiperPlusG2PTests/PUAMapTests.swift を新規作成。 fixedMap.count == 99、compatVersion == 2、PUA 範囲、コードポイント衝突、 トークン衝突、forward/reverse round-trip、unknown lookup の 8 アサーション。 シンボリックな count も差分で検出される。 - H6: swift-g2p-ci.yml に Thread Sanitizer ジョブを追加 (continue-on-error でまずは observability 優先)。`@unchecked Sendable` の主張に裏付けが 生まれるよう ConcurrencyTests を `--sanitize=thread` で再実行。 - H7: ConcurrencyTests.testRepeatedInitDeinit にアサーションを追加。 各 iteration の availableLanguages 検証 + 10 反復ごとに phonemize して pipeline の健全性確認 (元の loop は assertion ゼロでノーオペ化していた)。 - H8 (M1 兼用): testAvailableLanguagesRoundTripsRequestedSet を追加。 Rust が新言語を返したとき Swift の Language enum が落とす silent drop を検出。 Medium (5): - M3: バイナリサイズ表記を guide で 1 表に統合。xcframework.zip ダウンロード / app 増分 / 未 strip staticlib の 3 観点を区別して記載。各言語のサイズ 寄与も実測値 (cmudict 3.7MB / pinyin 2.6MB / NAIST-JDIC 20MB) に揃えた。 - M4: NAIST-JDIC ライセンス全文 (BSD-3-Clause) と osdn.net 公式 URL を THIRD_PARTY_LICENSES.md に追加。guide でも表記揺れを解消し、 attribution 義務の有無を整理。 - M5: english.rs / chinese.rs の OnceLock+expect が catch_unwind 後に poison 状態で残る挙動をコメントで明示。iOS 経路 (new_bundled) は到達しないこと を併記して FFI consumer の安心材料に。 - M6: testEmptyTextDoesNotCrash の `try?` で例外握りつぶしを止め、 Rust 側仕様 (空入力 → 成功 or phonemizeReturnedNull のいずれか) を Swift にも明示反映。 - L: integration guide の言語表に「学習済みモデルは 6 言語」「ko/sv は G2P 出力できても TTS モデルがない」旨の注釈を追加。 検証: - python scripts/check_pua_consistency.py --verbose --check-version → 7 ランタイム全て 99 entries / version=2 一致 - cargo test -p piper-plus-g2p --features all-languages,naist-jdic, bundled-dicts,ffi --lib → 403 passed / 0 failed - yaml.safe_load で 2 ワークフロー syntax ok * feat(swift-g2p): macOS slice + diagnostic G2PError + HelloG2P サンプル (#387) ユーザー視点レビュー指摘 (Concern B / C / E) を本ブランチで解消。 - macOS slice 追加 (Concern E): build-g2p-ios → build-g2p-apple に rename し macos-arm64_x86_64 matrix エントリ追加。assemble-g2p-xcframework が 3 slice を扱うよう 拡張。Package.swift に .macOS(.v13) を追加し、binaryTarget URL を libpiper_plus_g2p-ios- → libpiper_plus_g2p-apple- へ rename (macOS slice 同梱を反映)。 - G2PError 診断情報 (Concern C): initializationFailed(requestedLanguages:) / phonemizeReturnedNull(language:) に associated value を追加。 multi-language caller が call site を辿らずに失敗箇所を特定可能に。 既存テスト 1 件更新 + 新規テスト 2 件追加。 - examples/swift-g2p/HelloG2P/ (Concern B): macOS CLI demo (Package.swift + main.swift + README)。swift run で 8 言語の動作確認が可能。tag publish 前は path: モードへの切替手順を README に明記。 ドキュメント整合: integration guide / spec / contract / decisions log / CLAUDE.md を新 artifact 名・slice 構成・新エラー形式に追従。 未対応 Concern (本ブランチ範囲外): A (tag publish)、D (JSON decode 最適化)、 F (App Clip lite slice)、G (review approval)。詳細は docs/spec/swift-g2p-decisions.md の Post-merge follow-ups 節を参照。 * fix(swift-g2p): fixture v2 対応 + Rust 英語 OOV 既知ケース skip (#387) dev リベースで取り込んだ #392 (fixture schema v1→v2) と #400 (Kotlin G2P が 新規英語ケース追加) によって Swift CI 2 件失敗を解消。 # 修正内容 - tests/fixtures/g2p/phoneme_test_cases.json: regenerate_test_fixture.py で pua_map セクションを再生成 (drift 検出 CI が通る形式に統一、ensure_ascii=True で IPA 文字を \\u エスケープ表記に)。test_cases 自体は不変。 - GoldenPhonemeTests.swift: testFixtureLoads の expected version を 1 → 2 に 更新。runGoldenCases で Rust crate の英語実装が letter-spelling fallback 未実装である既知ギャップ (acronym/all-caps/mixed-case/repeated/Latin-1 accent/quote-only) を skip リスト化。Rust 英語実装の改善は別 PR で追跡。 * docs(swift-g2p): 実装完了後に陳腐化した設計時 doc を整理 (#387) エージェントチームの監査で「役目終了」と判定された箇所を削除/削減。 純減 334 行 (89 insertions, 423 deletions)。 # 削除 - docs/spec/swift-g2p-decisions.md (387 行) - 自身が冒頭で Resolved 宣言済み、末尾「全 ID 解決後は役割を swift-g2p.md / swift-g2p-integration.md に引き継ぐ」と明記 - 全 D-1〜D-15 と Post-merge follow-ups (2026-05-07) で完了済み - 参照: CHANGELOG.md のリンク行削除、swift-g2p-ci.yml のコメント差し替え # 削減 - docs/spec/swift-g2p.md (564 → 207 行) - Status: Proposed (Draft) → Implemented (v1.14.0+) - §3.5 Swift API: コード塊掲載 → Sources/PiperPlusG2P/ への参照テーブル化 - §4 実装スコープ: Cargo.toml/cbindgen.toml/CI YAML/Package.swift 全文 → 「実体は実コード/CI/manifest が真」のサマリ表 - §4.7 リリース手順: Package.swift 冒頭コメントの正本へのリンク化 - §4.9 テスト一覧: tests/PiperPlusG2PTests/ への参照のみ - 残存: §1 ユースケース、§2 採用方針、§3 配布構成、§5 採用しなかった案、 §6 拡張可能性、§7 関連リンク - examples/swift-g2p/README.md (101 → 64 行) - "Quick Start" を 3 行に圧縮、ガイドへのリンク化 - "Integrating into your own project" を削除 (ガイドへ統合) - "Local Development" / "Why a separate example directory?" は unique 価値ありで保持 # 保持 - docs/spec/swift-g2p-contract.toml (FFI/ABI 契約として現役) - docs/guides/swift-g2p-integration.md (consumer 向けガイド本体) - examples/swift-g2p/HelloG2P/ (実行可能サンプル) - src/rust/piper-plus-g2p/README.md (crate-level doc, Swift 言及は最小限) * docs(spec): 孤立した pua-codegen-design.md を削除 エージェント調査で参照ゼロ + 自己宣言「Design proposal — not yet implemented」と判定。Strategy C (現行手動同期 + CI gate) が `scripts/check_pua_consistency.py` + `/check-pua` skill + pre-commit hook で実運用成立済みのため、Strategy A/B 提案文書は陳腐化。 他 doc/コード/CI からの参照ゼロを grep で確認済 (削除によるリンク 切れなし)。 # 残った削減推奨 (本 PR 範囲外、別 PR 推奨) - zh-en-loanword-runtime-rollout.md (1958 行、Issue #384 完了) - kotlin-g2p-design.md (540 行、Issue #388 PR #400 完了) - kotlin-g2p-requirements.md (650 行、同上) これらは本ブランチ (Swift G2P #387) と無関係の領域で、git blame と PR レビューの clean さを優先して別 PR で対応する。 * docs(spec): 実装完了済み 3 spec の陳腐化セクションを削除 エージェントチーム調査で「実装完了で役目終了」と判定された箇所を削除。 本ブランチ範囲外領域だがユーザー判断で同 PR に同梱。 純減 325 行 (30 insertions, 355 deletions)。 # zh-en-loanword-runtime-rollout.md (1958 → 1739) Issue #384 / PR #399 で全 7 ランタイム同期実装完了済み。 - Status: Draft → Implemented - §4.1 ランタイム別タスク表 (R/G/C/W/P) 削除 (git log と PR 履歴に集約) - §5 1 ブランチ戦略のリスクと対策 削除 - §6 ロールアウト計画 (Day 1-14 + 受け入れ基準) 削除 - §8.11 リリース戦略 (実装後不要) 削除 - §8.15 CI ジョブ整合性 (実装後不要) 削除 - §9 改訂履歴 (Draft v1-v9) 削除 残置: §1 背景、§2 各ランタイム現状、§3 共通の実装パターン、§4 横断的 な設計課題、§5 関連ドキュメント、§8 深堀り (規範・運用 SOP・将来拡張)。 # kotlin-g2p-design.md (540 → 502) Issue #388 / PR #400 で実装完了済み。 - Status: Implemented を冒頭追加 - L8 「実装着手前に更新」警告 削除 (前提矛盾の自己警告は陳腐化済) - §6 実装ロードマップ (Day-by-Day Phase 1-7) 削除 - §9 受け入れ基準 削除 - §12 改訂履歴 削除 - §7-§11 のセクション番号を §6-§9 に詰め直し 残置: §1 ゴール、§2 OSS コンセプト分析、§3 既存資産、§4 アプローチ 比較、§5 推奨アプローチ、§6 自動化戦略、§7 リスクと対策、§8 関連 ドキュメント、§9 競合事例。 # kotlin-g2p-requirements.md (650 → 582) - Status: Implemented を冒頭追加 - §13 リスクと緩和策 削除 (実装後に発火/不発が確定) - §14 受け入れ基準 削除 (PR #400 で全 AC 実現済み) - §15 オープンクエスチョン 削除 (Q-1〜Q-10 全解決済み) - §16 用語集再掲 ("§2 を参照" のみ) 削除 - §17 関連ドキュメント を §13 に番号繰り上げ - §18 改訂履歴 削除 残置: §0〜§12 (要件定義としての規範性が継続)、§13 関連ドキュメント。
15 tasks
ayutaz
added a commit
that referenced
this pull request
Jun 13, 2026
- CLAUDE.md「ランタイム別パッケージ」表の版数を bump 後の値に更新 (Python 1.12.0→1.13.0 / C# 0.3.0→0.4.0 / Rust 0.4.0→0.5.0 / npm 0.6.0→0.7.0)。 version-consistency gate は CLAUDE.md を読まないため CI 未検出だった - CHANGELOG [1.13.0] Fixed に PR #389 を独立記載。v1.12.0 配布 config.json の 未 PUA 化 multi-codepoint 音素 (ɔɪ/œ̃/ɐ̃) による Windows C++ 推論失敗の実データ 修正 (pua.json v1→v2 + 全6ランタイム同期 + strict fail-fast)。従来は #557 の regression CI ガード文脈でしか言及されておらず、別 PR の実修正が独立記載漏れだった
ayutaz
added a commit
that referenced
this pull request
Jun 14, 2026
* chore(release): v1.13.0 — version bump + CHANGELOG promotion - bump 5 manifests (Python/Rust/C#/npm) + release-versions.toml prefixes - Python/root VERSION + piper_train/VERSION + src/python pyproject: 1.12.0 -> 1.13.0 - Rust workspace (+ internal cli/core/python/wasm deps): 0.4.0 -> 0.5.0 - C# Core/Cli: 0.3.0 -> 0.4.0 - npm synthesis (openjtalk-web): 0.6.0 -> 0.7.0 - regenerate uv.lock / Cargo.lock / package-lock.json - promote CHANGELOG [Unreleased] -> [1.13.0] - 2026-06-13 - add missing Fixed entries (#557 Windows tsukuyomi C++, #506/#507 EOS trim, #504 Go text splitter, #505 Windows release-QA) - add missing Security entries (CVE bumps #530/#531/#437/#450, CodeQL #435/#434, pyo3 advisories #558/#559) * docs(release): v1.13.0 監査で判明した CHANGELOG 漏れ等を追記 - CHANGELOG ### Added: G2P 文単位並列化 + 新 env PIPER_G2P_PARALLELISM (Issue #383, PR #403) - CHANGELOG ### Fixed: MB-iSTFT モデルの speaker_embedding 未対応で Docker/Rust/C++ が 500/load 失敗していた問題 (Issue #426, PR #443) - CLAUDE.md: env 列挙に PIPER_G2P_PARALLELISM を追記 - README.npm.md: importmap CDN pin を piper-plus@0.6.0 -> @0.7.0 に更新 (publish 版と同期) * fix(ci): unblock v1.13.0 tag — g2p checksum gate + pre-tag Swift runbook 監査 (ultracode) で判明したリリース実行 blocker を release ブランチで解消: - release-shared-lib.yml: g2p xcframework の checksum 検証 (placeholder + asset 一致) を「g2pVersion == tag 版」のときのみ強制する条件を追加。g2p は自身の g2pVersion tag で debut するため、synthesis v1.13.0 タグで g2pVersion=1.14.0 の placeholder が release job を hard-fail させていた (Issue #387)。synthesis の checksum は従来どおり常時強制。 - prepare-release skill: Swift checksum 更新を「tag 後」→「tag 前」の正しい順序に 修正 (Phase 5/7)。compute-checksum 対象を実 artifact 名 (libpiper_plus-ios. xcframework.zip 等) に訂正。workflow_dispatch artifact から取得する手順に変更。 - CONTRIBUTING.md: v<X.Y.Z> tag が release-shared-lib.yml + docker-build.yml を 同時発火する点、Swift checksum pre-tag 手順、GitHub Release 二重作成の注意を追記。 * docs(release): v1.13.0 監査で判明した CHANGELOG/CLAUDE.md の漏れを修正 - CLAUDE.md「ランタイム別パッケージ」表の版数を bump 後の値に更新 (Python 1.12.0→1.13.0 / C# 0.3.0→0.4.0 / Rust 0.4.0→0.5.0 / npm 0.6.0→0.7.0)。 version-consistency gate は CLAUDE.md を読まないため CI 未検出だった - CHANGELOG [1.13.0] Fixed に PR #389 を独立記載。v1.12.0 配布 config.json の 未 PUA 化 multi-codepoint 音素 (ɔɪ/œ̃/ɐ̃) による Windows C++ 推論失敗の実データ 修正 (pua.json v1→v2 + 全6ランタイム同期 + strict fail-fast)。従来は #557 の regression CI ガード文脈でしか言及されておらず、別 PR の実修正が独立記載漏れだった * docs(changelog): 開発時の重大度マーカー (🔴 Critical 等) を除去 [1.13.0] の Kotlin/Android G2P エントリに混入していた開発時の重大度/優先度 マーカー (🔴 Critical / 🔴 / 🟡 / 🟡 → 🟢) を全除去。リリース後に changelog を 読む人にとって緊急度の色分けは無意味なノイズのため、事実記述のみに統一。 カテゴリ見出しの装飾絵文字 (### 🚀 Major Features 等) と要件 ID (NFR-SEC-2 等) は意味が明確なため残置。 * docs(readme): 全8言語のヘッダーを統一・改行整理し FR/ES/PT/DE の正書法を復元 - 言語ナビとバッジ群の間・パッケージラベルとバッジの間に空行を挿入。README は CommonMark で単一改行がスペース化されナビとバッジが密着していたため、論理グループ を空行で分離 (左寄せ・HTML 不使用) - 全8言語ヘッダーを日本語版水準に統一: 状態バッジ整列 (重複 PyPI 除去)、パッケージ レジストリバッジ群 (PyPI/NuGet/crates.io/npm/Maven Central)、Try in Browser バッジ、 MIT フォーク訴求文 (各言語へ翻訳) を追加 - FR/ES/PT/DE の本文 diacritics を全面復元 (Systeme→Système、Franzoesisch→Französisch 等)。heading slug と TOC アンカーの整合は維持、コードブロック内は ASCII のまま (コピペ互換性のため) - 6言語は per-language の edit→adversarial verify で検証 (バッジ URL byte 一致、callout 翻訳、TOC アンカー整合、内容非改変)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v1.12.0 で配布された tsukuyomi config.json に未PUA化のmulti-codepoint音素 (
ɔɪ/œ̃/ɐ̃) が混入し、Windows の C++ 推論で"is not a single codepoint" (ids=149,)エラーが発生していた。直接修正に加え、同種バグの再発を CI で防止する仕組みを導入。根本原因
multi-codepoint 音素を PUA に正規化する責務が分散しており、登録漏れを fail-fast に検出する仕組みがなかった。
pua.jsonにɔɪ/œ̃/ɐ̃が未登録map_token()が未登録 multi-codepoint をwarnings.warn()のみで素通り詳細:
docs/spec/pua-contract.tomlとdocs/spec/pua-test-matrix.md変更内容
データ修正 (PUA v2)
pua.json96→99 entries、version: 1→2ɔɪ(U+E062, 英語 OY)、œ̃(U+E063, 仏鼻母音)、ɐ̃(U+E064, 葡鼻母音)token_mapper.pyに欠落していた Swedish 9 entries も補完Fail-fast 化
map_token(strict=True): 未登録 multi-codepoint でUnmappedMultiCodepointTokenErrorraise (default は back-compat のためFalse、build時のみTrue)_build_*_id_map()末尾で全キー単一コードポイント不変条件を_assert_single_codepoint_keysで assertupdate_phoneme_id_map(strict=True)を default 化、--validate-onlyCLI 追加契約明文化
docs/spec/pua-contract.toml: 4 つの不変条件と CI 必須項目CI ゲート (今回のバグを将来検出する仕組み)
scripts/check_pua_consistency.py: 6 ランタイムを entry-by-entry で pua.json と比較.github/workflows/pua-consistency.yml: 新規 workflow (6 ジョブ並列).github/workflows/g2p-cross-platform-ci.yml: 既存の count-only validation を完全比較に置換設計提案
docs/spec/pua-codegen-design.md: codegen 移行の3戦略を検討、Strategy C (CI 整合性チェック継続) を推奨配布config 修復スクリプト
scripts/regenerate_tsukuyomi_config.py: HF からダウンロード→strict-mode で PUA 化→tmp/ に出力テスト項目 (Test Matrix)
Test plan
python scripts/check_pua_consistency.py --check-version→ All 6 runtimes consistentuv run pytest src/python/g2p/tests/→ 652 passed, 197 skipped (新規 27 含む)uv run pytest src/python/tests/test_update_model_config.py→ 8 passedgo test ./phonemize/...→ 全 TestFixedPUA + TestRegisterToken_DynamicPUA PASSregenerate_tsukuyomi_config.pyを実行して HF にconfig push、 Windows C++ 推論で動作確認Breaking changes
update_phoneme_id_map()の default がstrict=True(以前は warning のみ素通し)strict=Falseを明示--no-strictフラグで warning モードPUA_COMPAT_VERSION1 → 2 (古い v1 モデルは config 再生成必要、ただし配布物の整合性回復のため事実上必須)