作成日: 2026-06-26
対象: Ourin の PLUGIN/2.0M bridge / SSP PLUGIN 互換 / PLUGIN/1.0 挙動互換
目的: UKADOC PLUGIN/2.0 の request/response、PLUGIN Event、plugin descript、旧 PLUGIN/1.0 由来の挙動を、macOS native plugin bridge として実用互換レベルまで実装する。
参照仕様
- UKADOC PLUGIN/2.0: https://ssp.shillest.net/ukadoc/manual/spec_plugin.html
- UKADOC PLUGIN Event: https://ssp.shillest.net/ukadoc/manual/list_plugin_event.html
- UKADOC Plugin descript.txt: https://ssp.shillest.net/ukadoc/manual/descript_plugin.html
- PLUGIN/1.0 参考: http://usada.sakura.vg/contents/plugin.html
- Ourin 仕様:
docs/SPEC_PLUGIN_2.0M_ja-jp.md - Ourin イベント仕様:
docs/PLUGIN_EVENT_2.0M_SPEC_ja-jp.md
完了定義
Plugin bridge 完全実装とは、以下を満たす状態とする。
- プラグインへの送信が PLUGIN/2.0 の wire semantics に準拠する。
- プラグインからの
Event/Reference*/Target/Script/ScriptOption/EventOption応答が、ゴーストへ仕様通り橋渡しされる。 - UKADOC PLUGIN Event リストの全イベントについて、少なくとも「正しい ID、GET/NOTIFY、Reference 順」で送信経路がある。
descript.txtのcharset/secondchangeinterval/otherghosttalkが実動作へ接続される。property.get/property.setがpluginlist(...).ext系の拡張プロパティとして plugin へ委譲される。- Shift_JIS/CP932 plugin と UTF-8 plugin の双方で、request/response の文字化けがない。
- PLUGIN/1.0 は macOS 上で Windows DLL を直接ロードしないが、イベント名・メニュー呼び出し・旧 descript 由来 metadata の挙動互換を提供する。
現状サマリ
更新 2026-06-27: 本計画の runtime bridge 項目は実装済み。Charset の byte encode/decode、Target 配送、Event/Script fallback、ScriptOption、plugin 別 secondchangeinterval、otherghosttalk before/after、catalog/install/ghost/menu event wiring、pluginlist(...).ext property 委譲、PLUGIN/1.0 metadata 互換、DevTools console、選択肢/アンカーの plugin-origin one-shot hook を接続した。回帰は PluginEncodingTests、PluginTargetRoutingTests、PluginPropertyBridgeTests と既存 matrix、および full xcodebuild -project Ourin.xcodeproj -scheme Ourin test で検証する。
実装済み:
.plugin/.bundle検出とrequest/loadu/unload呼び出し。PluginFrameによる GET/NOTIFY フレーム構築。PluginEventDispatcherによる plugin 単位の直列 dispatch。OurinPluginEventBridgeによるraiseplugin/notifyplugin応答 bridge。EventOption: notify、ScriptOptionparse、__SYSTEM_ALL_GHOST__の基本受理。OnChoiceSelect(Ex)/OnAnchorSelect(Ex)の plugin-origin one-shot 横流し。- negotiated
Charsetに基づく実 byte encode/decode。 Targetの特定 ghost / all ghost / baseware 配送。notranslate/nobreakの SakuraScript 実行制御。property.get/property.setの plugin 委譲。OnOtherGhostTalkの実発話 pipeline 接続とbefore/after。- catalog 系通知の起動時・install 時 wiring。
- PLUGIN/1.0 由来 plugin metadata / menu/message 挙動互換。
Phase 0: 実装状況の棚卸しと仕様同期
目的: 古い docs と実コードのずれをなくし、後続実装の基準を固定する。
対象:
docs/SPEC_PLUGIN_2.0M_ja-jp.mddocs/PLUGIN_EVENT_2.0M_SPEC_ja-jp.mddocs/PLUGIN_COMPAT_FIX_PROPOSAL.mdOurin/PluginEvent/*Ourin/PluginHost/*
作業:
- UKADOC のイベント一覧を Ourin 実装表へ転記し直す。
- 各イベントを
implemented/helper-only/not wired/not implementedに分類する。 - 既存 docs の「実装済み」表現を、helper-only と runtime-wired に分ける。
受け入れ条件:
- docs の実装状況と
rgで確認できるコードが矛盾しない。 - 各未実装項目がこの計画書の Phase に対応する。
Phase 1: Wire charset を byte レベルで実装
目的: PLUGIN/2.0 の Charset を実際の request/response encode/decode に反映する。
対象:
Ourin/PluginHost/Plugin.swiftOurin/PluginHost/PluginProtocol.swiftOurin/PluginEvent/PluginEncodingNormalizer.swiftOurin/PluginEvent/PluginEventDispatcher.swiftOurinTests/OurinPluginEventBridgeTests.swift- 新規:
OurinTests/PluginEncodingTests.swift
作業:
Plugin.sendをString.Encoding指定可能にする。PluginRequestbuild 後の wire を negotiated charset で byte 化する。- plugin response を response
Charsetまたは request charset で decode し、内部 UTF-8Stringへ正規化する。 version応答のCharsetを plugin ごとに保持し、次回 request へ適用する。- Shift_JIS/CP932 の round-trip fixture plugin または mock request 関数をテストに追加する。
受け入れ条件:
Charset: Shift_JISplugin に日本語Reference*を送っても文字化けしない。- Shift_JIS response の
Script/Event/Reference*を UTF-8 内部文字列として扱える。 - UTF-8 plugin の既存テストが退行しない。
Phase 2: Target routing の完全化
目的: Target が指定された plugin response を、仕様通り対象 ghost へ返す。
対象:
Ourin/PluginEvent/OurinPluginEventBridge.swiftOurin/SHIORIEvents/EventBridge.swiftOurin/Ghost/GhostManager.swiftOurin/Ghost/GhostManager+System.swiftOurin/SSTP/BridgeToSHIORI.swift
作業:
PluginTransportAction.targetを文字列のまま判定するだけでなく、配送先 resolver に渡す。- target resolver を追加する。
-
nil/ empty: 呼び出し元 ghost を優先し、なければ active ghost。 -__SYSTEM_ALL_GHOST__: 起動中全 ghost。 - ghost name / ghost id / ghost path / owned SSTP id: 該当 ghost。 -baseware/ourin: baseware 側処理。 raiseplugin呼び出し元 ghost を action context に保持する。PluginEventDispatcherの system event 由来 response は active ghost または target 指定に従う。- target 不一致時は fallback script を実行しない。
受け入れ条件:
- 2 体起動時、
Target指定 response が該当 ghost のみに届く。 __SYSTEM_ALL_GHOST__は全 ghost に届く。raisepluginresponse は target 未指定なら呼び出し元 ghost に戻る。
Phase 3: Event/Script response semantics の厳密化
目的: Event が反応しない場合だけ default Script を実行する仕様を安定化する。
対象:
Ourin/PluginEvent/OurinPluginEventBridge.swiftOurin/SHIORIEvents/EventBridge.swiftOurin/SHIORIEvents/EventID.swiftOurin/Ghost/GhostManager.swiftOurin/SakuraScriptEngine.swift
作業:
- plugin response 由来
EventはEventOption: notifyなしなら GET として SHIORI に送る。 - GET response script が空なら plugin response
Scriptを fallback として実行する。 EventOption: notifyの場合は response script を見ず、plugin responseScriptfallback も実行しない。ScriptOption: nobreakを「現在のトーク後に enqueue」として実装する。ScriptOption: notranslateを SakuraScript 実行 context に伝播する。OnOtherGhostTalkの reasons にplugin-script/plugin-event/notranslateを付与できるようにする。
受け入れ条件:
Event + Scriptresponse で ghost が GET 応答した場合、default Script は再生されない。- ghost が空応答の場合だけ default Script が再生される。
nobreakは再生中トークを中断しない。notranslateは translate pipeline を通らない。
Phase 4: UKADOC PLUGIN Event 全送信経路
目的: イベント一覧の全 ID を runtime event source と接続する。
対象:
Ourin/PluginEvent/PluginEventDispatcher.swiftOurin/OurinApp.swiftOurin/Ghost/GhostManager+System.swiftOurin/Ghost/GhostManager+Balloon.swiftOurin/NarInstall/*Ourin/HeadlineHost/*Ourin/Property/*
イベント別作業:
version- 現状維持。ただし charset byte 実装後に再検証する。installedplugin- 起動時、plugin reload 時、install/uninstall 時に送る。installedghostname- 起動時、ghost install/uninstall 時に送る。 -Reference0: ghost names。 -Reference1: sakura names。 -Reference2: kero names。installedballoonname- 起動時、balloon install/uninstall 時に送る。ghostpathlist- 起動時、ghost path 設定変更時に送る。balloonpathlist- 起動時、balloon path 設定変更時に送る。headlinepathlist- 起動時、headline path 設定変更時に送る。pluginpathlist- 起動時、plugin reload/install/uninstall 時に送る。OnSecondChange-secondchangeintervalを plugin ごとに尊重する。現在の最小 interval 一括 timer ではなく、plugin 別 timer または tick filter にする。OnOtherGhostTalk- ghost 発話直前/直後に接続する。
otherghosttalkのfalse/0/true/1/after/beforeを尊重する。
OnGhostBoot- ghost boot 完了時に送る。window 未構築なら
0。
- ghost boot 完了時に送る。window 未構築なら
OnGhostExit- ghost exit 時に NOTIFY で送る。
OnGhostInfoUpdate- ghost config/shell/info update 時に NOTIFY で送る。
OnMenuExec- plugin menu 実行時に GET で送る。
OnInstallComplete- NAR / ghost / shell / balloon / plugin / headline install 完了時に送る。
OnChoiceSelect(Ex)/OnAnchorSelect(Ex)/ arbitrary choice event- plugin response 由来 script/event から出た選択肢だけ、一回限り plugin へ横流しする marker を付ける。
raiseplugin/notifyplugin任意名- 現状を維持し、Target context と charset を接続する。
property.get- Phase 5 で実装する。
property.set- Phase 5 で実装する。
受け入れ条件:
- 各イベントの Reference 順が UKADOC と一致する。
[NOTIFY]イベントは response script を無視する。OnSecondChangeは plugin ごとの interval を守る。OnOtherGhostTalkはbefore/afterを区別する。
Phase 5: plugin 拡張 property.get / property.set
目的: pluginlist(...).ext 由来の拡張 property を plugin request へ委譲する。
対象:
Ourin/Property/PluginPropertyProvider.swiftOurin/Property/PropertyManager.swiftOurin/PluginHost/PluginRegistry.swiftOurin/PluginEvent/PluginEventDispatcher.swiftOurinTests/PropertySystemTests.swift
設計:
pluginlist(name).ext.fooまたはpluginlist.index(n).ext.fooを拡張 property として扱う。- get:
- target plugin へ
GET PLUGIN/2.0M ID: property.getReference0: fooValueまたはScriptではなく、仕様通り property value として response value を採用する。- set:
- target plugin へ
GETまたはNOTIFYではなく、仕様に合わせGET相当の request-response とする。 ID: property.setReference0: fooReference1: value- status
204または200を成功扱い。
受け入れ条件:
pluginlist.index(0).ext.somekeyが plugin のproperty.getresponse を返す。PropertyManager.set("pluginlist.index(0).ext.somekey", value: "x")がproperty.setを呼ぶ。- 404/500 response は property get nil / set false になる。
Phase 6: PLUGIN/1.0 挙動互換
目的: Windows DLL バイナリ互換ではなく、旧 plugin 配布物・旧 metadata・旧 menu 挙動を Ourin 上で扱いやすくする。
対象:
Ourin/PluginHost/PluginRegistry.swiftOurin/PluginHost/PluginProtocol.swiftOurin/PluginEvent/PluginEventDispatcher.swiftOurin/OwnerDrawMenu/*OurinTests/LegacyPluginRegistryTests.swift
作業:
- legacy metadata-only plugin directory を bridge 対象として扱う条件を整理する。
descript.txtの旧 key alias を正規化する。- native
.pluginがない Windows DLL plugin は「ロード不可だが metadata visible」とし、menu 実行時には明示エラーまたは無効表示にする。 - PLUGIN/1.0 由来の menu / author / readme / charset key を
PluginMetaに保持する。 - macOS native へ移植済み package が同一 ID で存在する場合、native を優先し legacy は重複登録しない。
受け入れ条件:
- legacy plugin directory は Ourin UI 上で識別できる。
- native replacement がある場合は二重ロードされない。
- 旧 descript の charset/name/id/filename が metadata として読める。
Phase 7: Menu / UI / DevTools bridge
目的: plugin bridge の状態と発火を UI から検証できるようにする。
対象:
Ourin/ContentView.swiftOurin/DevTools/*Ourin/OwnerDrawMenu/*Ourin/PluginHost/PluginRegistry.swift
作業:
- Plugin DevTools に negotiated charset、package path、executable path、legacy/native 状態を表示する。
- 任意 plugin に
GET/NOTIFYrequest を送れる test console を追加または強化する。 OnMenuExecの実 menu からの起動を dispatcher に接続する。message.*.txtを plugin menu 表示文言へ優先適用する。
受け入れ条件:
- DevTools から
version/ arbitrary event / property.get を手動検証できる。 - plugin menu 文言が
message.japanese.txt/message.english.txtで切り替わる。
Phase 8: Test plugin fixtures
目的: 実 plugin 互換を unit test と integration test で固定する。
対象:
OurinTests/Fixtures/plugin/*- 新規 fixture plugin source
OurinTests/PluginBridgeIntegrationTests.swift
fixture:
- UTF-8 echo plugin。
- Shift_JIS echo plugin。
Event + Script fallbackplugin。Targetplugin。property.get/setplugin。- legacy metadata-only plugin。
受け入れ条件:
- CI または local
xcodebuild testで fixture plugin をロードできる。 - byte charset、Target、fallback、property を実 plugin response で検証する。
Phase 9: Regression test matrix
対象テスト
OurinPluginEventBridgeTestsLegacyPluginRegistryTestsPropertySystemTestsSakuraScriptEngineTestsSSTPDispatcherTests- 新規
PluginEncodingTests - 新規
PluginTargetRoutingTests - 新規
PluginPropertyBridgeTests - 新規
PluginEventDispatchMatrixTests
検証コマンド:
xcodebuild -project Ourin.xcodeproj -scheme Ourin test
追加で、対象変更中は以下を個別実行する。
xcodebuild -project Ourin.xcodeproj -scheme Ourin -only-testing:OurinTests/OurinPluginEventBridgeTests test
xcodebuild -project Ourin.xcodeproj -scheme Ourin -only-testing:OurinTests/PropertySystemTests test
xcodebuild -project Ourin.xcodeproj -scheme Ourin -only-testing:OurinTests/LegacyPluginRegistryTests test
実装順序
推奨順:
- Phase 0: docs/status 棚卸し。
- Phase 1: charset byte 実装。
- Phase 2: Target routing。
- Phase 3: Event/Script semantics。
- Phase 4: event wiring。
- Phase 5: property.get/set。
- Phase 6: PLUGIN/1.0 挙動互換。
- Phase 7: UI/DevTools。
- Phase 8/9: fixtures と test matrix 拡充。
理由:
- charset と Target は bridge 全体の土台で、後続イベントすべてに影響する。
- Event/Script semantics を先に固めると、OnMenuExec・raiseplugin・choice hook の挙動が安定する。
- property は独立度が高いが、target plugin resolution が必要なため Phase 2 後がよい。
リスクと対策
| リスク | 影響 | 対策 |
|---|---|---|
| Shift_JIS plugin response の decode 失敗 | 日本語 plugin が壊れる | byte fixture と CP932 fallback を追加 |
| Target routing で別 ghost に script が流れる | ユーザー体験・互換性に直撃 | multi-ghost routing tests を追加 |
nobreak 実装で再生 queue が破綻 |
トーク再生 regressions | SakuraScript playback tests を追加 |
OnOtherGhostTalk が再帰的に発火 |
plugin-event loop | reason flag と再入抑止 token を導入 |
| property.set が副作用を持つ | 予期せぬ plugin 状態変更 | 明示 target plugin のみに送る |
| legacy plugin を誤ロード | クラッシュ/無効 bundle | native bundle 有無でロード可否を分離 |
最小マイルストーン
M1: bridge core 完成
- charset byte 実装。
- Target routing。
- Event/Script fallback 厳密化。
OurinPluginEventBridgeTests/PluginEncodingTests/PluginTargetRoutingTests成功。
M2: event matrix 完成
- UKADOC PLUGIN Event 全 ID を runtime source に接続。
OnSecondChangeplugin 別 interval。OnOtherGhostTalkbefore/after。PluginEventDispatchMatrixTests成功。
M3: property と legacy 互換
property.get/setplugin 委譲。- PLUGIN/1.0 metadata-only 互換。
- native replacement 優先と重複抑止。
M4: UI と運用検証
- DevTools で request/response を確認可能。
- plugin menu 文言に
message.*.txtを反映。 - full
xcodebuild ... test成功。
完了後に更新する docs
docs/SPEC_PLUGIN_2.0M_ja-jp.mddocs/SPEC_PLUGIN_2.0M_en-us.mddocs/PLUGIN_EVENT_2.0M_SPEC_ja-jp.mddocs/PLUGIN_EVENT_2.0M_SPEC_en-us.mddocs/PropertySystem_ja-jp.mddocs/PropertySystem_en-us.mddocs/SUPPORTED_SAKURA_SCRIPT.md
チェックリスト
- [x] Charset negotiated byte encode/decode
- [x] Target resolver
- [x] caller ghost context for
raiseplugin - [x]
__SYSTEM_ALL_GHOST__multi-ghost delivery - [x]
EventGET fallback semantics - [x]
EventOption: notify - [x]
ScriptOption: nobreak - [x]
ScriptOption: notranslate - [x] plugin-specific
secondchangeinterval - [x]
otherghosttalkbefore/after - [x]
installedghostname - [x]
installedballoonname - [x]
ghostpathlist - [x]
balloonpathlist - [x]
headlinepathlist - [x]
OnGhostBoot - [x]
OnGhostExit - [x]
OnGhostInfoUpdate - [x]
OnMenuExec - [x]
OnInstallComplete - [x] choice/anchor one-shot plugin-origin hook
- [x]
property.get - [x]
property.set - [x] PLUGIN/1.0 metadata-only compatibility
- [x] DevTools request console
- [x] fixture/plugin bridge regression suite
監査記録(2026-07-08)
実装コードとの突合監査の結果、上記チェックリストの一部が実装より先に✅化されていた(過大申告)ことが判明した。以下は当時の実態と、その後の対応。
- Phase 8 fixture 6種・
PluginBridgeIntegrationTests: 監査時点でファイル自体が存在しなかった(- [x] fixture/plugin bridge regression suiteは当時誤り)。→ 2026-07-08 に実装完了:OurinTests/Fixtures/plugin/(C実装fixture 5種+legacy metadata-only、テスト時にclangでビルド)+OurinTests/PluginBridgeIntegrationTests.swift。 - Phase 9
PluginEventDispatchMatrixTests: 監査時点で不存在。→ 2026-07-08 に実装完了(仕様全17項目、21テストケース)。同時に発見された仕様ギャップ4件(OnInstallComplete複数値、notifyplugin NOTIFY強制、balloonpathlist Ref数、reasons語彙)も解消済み。 - Phase 7 plugin menu統合: 「DevTools request console」自体は実在したが、plugin menu項目のホストメニュー列挙と
message.*.txt文言切替は未実装だった。→ 2026-07-08 に実装完了(PluginRegistryのmenuモデル+右クリック/OwnerDrawメニューへのサブメニュー追加+対象plugin限定OnMenuExec)。 - 仕様の明確化:
OnMenuExecのReference構成は仕様(Ref0=CGWindowID列、Ref1..4=ghost情報)を維持し、選択されたplugin menu項目のIDは Ref5 に格納する(Ourin拡張)。
教訓: チェックリストの✅は、対象ファイルの実在とテスト実行結果を確認してから付けること。
出典と編集について
Ourin / Furin Lab・contributors。文書はCC BY-SA 4.0(第三者の個別表記を除く)。Web版は見出し・リンクの整備とローカルパスの一般化を行っています。更新基準日: 2026-10-30。
元資料: docs/PLUGIN_BRIDGE_COMPLETION_PLAN_ja-jp.md
ソース SHA-256: a359de6c623c6287a0161fc7fd212a3ce4386f23b66752c403a7e8f607c90d79