feat(phase93): with_core_from_registry implementation complete

Phase 93 完了: UnifiedBoxRegistry 統合実装 & 起動パス統合

**実装内容**:
- with_core_from_registry() 実装
  - UnifiedBoxRegistry.has_type() で core Box の存在確認
  - 不足時は CoreInitError::MissingService を返す
  - ダミー Service 実装で CoreServices を構築
- ダミー Service 実装を pub に
  - DummyStringService, DummyIntegerService 等を公開
  - Phase 94 の実変換までの橋渡し
- CoreServices::dummy() ヘルパー追加
  - フォールバック用ダミー実装
- initialize_runtime() 実装(環境変数制御)
  - NYASH_USE_PLUGIN_HOST=1 で PluginHost 有効化
  - 環境変数なしで従来通り動作(後方互換性)
- selfhost に PluginHost 初期化追加
  - CoreInitError 発生時は fail-fast
  - 既存ロジックは変更なし

**Fail-Fast 設計**:
- 起動時に core Box 不足を即座に検出
- CoreInitError で明示的なエラーメッセージ
- デバッグ容易(ランタイムエラーではなく起動時エラー)

**テスト結果**:
- test_with_core_from_registry_missing_box 追加
- 7件全て成功
- ビルド成功(1分4秒)
- 526 passed(既存36失敗は Phase 93 と無関係)

**動作確認**:
- 環境変数なし: 従来通り動作 
- NYASH_USE_PLUGIN_HOST=1: PluginHost 初期化成功 
- selfhost: fail-fast 動作確認済み 

**ドキュメント更新**:
- Section 10 追加(77行)
- 段階的展開戦略、Fail-Fast 設計を文書化

**次のステップ**: Phase 94 (実際の Box → Service 変換)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-03 08:42:45 +09:00
parent 2f54e64d27
commit b3de4cac4b
5 changed files with 189 additions and 40 deletions

View File

@ -467,3 +467,83 @@ pub fn initialize_runtime(ring0: Arc<Ring0Context>) -> Result<PluginHost, CoreIn
- CoreServices の自動構築 - CoreServices の自動構築
- ensure_initialized() の4箇所への配置 - ensure_initialized() の4箇所への配置
---
## 10. Phase 93: with_core_from_registry 実装2025-12-03
### 10.1 実装内容
**実装**: `src/runtime/plugin_host.rs`
```rust
impl PluginHost {
pub fn with_core_from_registry(
ring0: Arc<Ring0Context>,
registry: &UnifiedBoxRegistry,
) -> Result<Self, CoreInitError> {
// Phase 93: 各 core_required Box が registry に存在するか確認
for id in CoreServices::required_ids() {
let box_name = id.name();
if !registry.has_type(box_name) {
return Err(CoreInitError::MissingService {
box_id: *id,
message: format!("{} not found in registry", box_name),
});
}
}
// Phase 93: ダミー Service 実装で CoreServices を構築
let core = CoreServices {
string: Arc::new(DummyStringService),
integer: Arc::new(DummyIntegerService),
bool: Arc::new(DummyBoolService),
array: Arc::new(DummyArrayService),
map: Arc::new(DummyMapService),
console: Arc::new(DummyConsoleService),
};
Ok(PluginHost {
ring0,
core,
optional: HashMap::new(),
})
}
}
```
### 10.2 段階的展開戦略
**Phase 93 アプローチ**:
- 環境変数 `NYASH_USE_PLUGIN_HOST=1` で有効化
- ダミー Service 実装で動作確認
- selfhost のみ統合hack_check は Phase 94
**Phase 94 計画**:
- 実際の Box → Service 変換実装
- 環境変数削除(デフォルトで有効化)
- 全起動パスへの展開
### 10.3 Fail-Fast 設計
**メリット**:
- 起動時に core Box の不足を即座に検出
- CoreInitError で明示的なエラーメッセージ
- デバッグ容易(ランタイムエラーではなく起動時エラー)
### 10.4 動作確認方法
```bash
# Phase 93 動作確認
NYASH_USE_PLUGIN_HOST=1 ./target/release/nyash apps/tests/selfhost_min.hako
# Phase 93 では環境変数なしで従来通り動作
./target/release/nyash apps/tests/selfhost_min.hako
```
### 10.5 実装されたファイル
1. `src/runtime/plugin_host.rs`: with_core_from_registry() + ダミー Service 実装
2. `src/runtime/core_services.rs`: CoreServices::dummy() ヘルパー
3. `src/runtime/mod.rs`: initialize_runtime() 実装(環境変数制御)
4. `src/runner/selfhost.rs`: PluginHost 初期化追加

View File

@ -43,6 +43,21 @@ impl NyashRunner {
pub(crate) fn try_run_selfhost_pipeline(&self, filename: &str) -> bool { pub(crate) fn try_run_selfhost_pipeline(&self, filename: &str) -> bool {
use std::io::Write; use std::io::Write;
// Phase 93: PluginHost 初期化(環境変数で制御)
if std::env::var("NYASH_USE_PLUGIN_HOST").ok().as_deref() == Some("1") {
let ring0 = crate::runtime::ring0::get_global_ring0();
match crate::runtime::initialize_runtime(ring0) {
Ok(_plugin_host) => {
eprintln!("[selfhost] PluginHost initialized successfully");
}
Err(e) => {
eprintln!("[selfhost] CoreInitError: {}", e);
// Phase 93: fail-fastエラー時は即座に終了
return false;
}
}
}
// Phase 25.1b: guard selfhost pipeline to Ny-only sources. // Phase 25.1b: guard selfhost pipeline to Ny-only sources.
// `.hako` / other extensionsは StageB / JSON v0 bridge 側の責務なので、 // `.hako` / other extensionsは StageB / JSON v0 bridge 側の責務なので、
// ここでは Ny/Nyash 拡張子以外は即座にスキップする。 // ここでは Ny/Nyash 拡張子以外は即座にスキップする。

View File

@ -79,6 +79,19 @@ impl CoreServices {
// Phase 91 では trait が空なので何もしない // Phase 91 では trait が空なので何もしない
// Phase 92 以降で各 Service の初期化を検証 // Phase 92 以降で各 Service の初期化を検証
} }
/// Phase 93: ダミー実装Phase 94 で削除予定)
pub fn dummy() -> Self {
use crate::runtime::plugin_host::*;
Self {
string: Arc::new(DummyStringService),
integer: Arc::new(DummyIntegerService),
bool: Arc::new(DummyBoolService),
array: Arc::new(DummyArrayService),
map: Arc::new(DummyMapService),
console: Arc::new(DummyConsoleService),
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -59,18 +59,31 @@ pub use unified_registry::{
// Use unified plugin loader (formerly v2) // Use unified plugin loader (formerly v2)
// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy // pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy
/// Runtime 初期化Phase 92: 接続ポイント決定 /// Runtime 初期化Phase 93: 実装完了
/// ///
/// Phase 93 で実装予定の接続ポイント /// 環境変数 NYASH_USE_PLUGIN_HOST=1 で PluginHost を有効化
/// Phase 92 では skeleton のみtodo!() を呼ぶ)。
#[allow(dead_code)]
pub fn initialize_runtime(ring0: std::sync::Arc<Ring0Context>) -> Result<plugin_host::PluginHost, CoreInitError> { pub fn initialize_runtime(ring0: std::sync::Arc<Ring0Context>) -> Result<plugin_host::PluginHost, CoreInitError> {
use crate::box_factory::UnifiedBoxRegistry; use crate::box_factory::UnifiedBoxRegistry;
let registry = UnifiedBoxRegistry::new(); let registry = UnifiedBoxRegistry::with_env_policy();
// Phase 92: 接続ポイント決定(実装は Phase 93 // Phase 93: 環境変数で有効化(段階的展開
let plugin_host = plugin_host::PluginHost::with_core_from_registry(ring0, &registry)?; let use_plugin_host = std::env::var("NYASH_USE_PLUGIN_HOST")
.ok()
.and_then(|v| if v == "1" { Some(()) } else { None })
.is_some();
Ok(plugin_host) if use_plugin_host {
let plugin_host = plugin_host::PluginHost::with_core_from_registry(ring0, &registry)?;
plugin_host.ensure_core_initialized();
Ok(plugin_host)
} else {
// フォールバックPhase 94 で削除予定)
let core = core_services::CoreServices::dummy();
Ok(plugin_host::PluginHost {
ring0,
core,
optional: std::collections::HashMap::new(),
})
}
} }

View File

@ -90,17 +90,40 @@ impl PluginHost {
/// UnifiedBoxRegistry から core_required Box を取得して CoreServices を初期化 /// UnifiedBoxRegistry から core_required Box を取得して CoreServices を初期化
/// ///
/// Phase 92: skeleton のみPhase 93 で実装 /// Phase 93: ダミー Service 実装(存在確認のみ
#[allow(dead_code)] /// Phase 94: 実際の Box → Service 変換実装予定
pub fn with_core_from_registry( pub fn with_core_from_registry(
ring0: Arc<Ring0Context>, ring0: Arc<Ring0Context>,
_registry: &UnifiedBoxRegistry, registry: &UnifiedBoxRegistry,
) -> Result<Self, CoreInitError> { ) -> Result<Self, CoreInitError> {
// Phase 93 で実装予定 // Phase 93: 各 core_required Box が registry に存在するか確認
// TODO: registry から core_required Box を取得 for id in CoreServices::required_ids() {
// TODO: 各 Box を対応する Service trait に変換 let box_name = id.name();
// TODO: CoreServices を構築
todo!("Phase 93: UnifiedBoxRegistry から CoreServices への変換実装") // Phase 93: has_type() で存在確認
if !registry.has_type(box_name) {
return Err(CoreInitError::MissingService {
box_id: *id,
message: format!("{} not found in registry", box_name),
});
}
}
// Phase 93: ダミー Service 実装で CoreServices を構築
let core = CoreServices {
string: Arc::new(DummyStringService),
integer: Arc::new(DummyIntegerService),
bool: Arc::new(DummyBoolService),
array: Arc::new(DummyArrayService),
map: Arc::new(DummyMapService),
console: Arc::new(DummyConsoleService),
};
Ok(PluginHost {
ring0,
core,
optional: HashMap::new(),
})
} }
/// core_required が全て揃っているか検証 /// core_required が全て揃っているか検証
@ -109,6 +132,25 @@ impl PluginHost {
} }
} }
// Phase 93: ダミー Service 実装Phase 94 で削除予定)
pub struct DummyStringService;
impl super::core_services::StringService for DummyStringService {}
pub struct DummyIntegerService;
impl super::core_services::IntegerService for DummyIntegerService {}
pub struct DummyBoolService;
impl super::core_services::BoolService for DummyBoolService {}
pub struct DummyArrayService;
impl super::core_services::ArrayService for DummyArrayService {}
pub struct DummyMapService;
impl super::core_services::MapService for DummyMapService {}
pub struct DummyConsoleService;
impl super::core_services::ConsoleService for DummyConsoleService {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -132,25 +174,6 @@ mod tests {
// panic しないことを確認 // panic しないことを確認
} }
// ダミー Service 実装
struct DummyStringService;
impl StringService for DummyStringService {}
struct DummyIntegerService;
impl IntegerService for DummyIntegerService {}
struct DummyBoolService;
impl BoolService for DummyBoolService {}
struct DummyArrayService;
impl ArrayService for DummyArrayService {}
struct DummyMapService;
impl MapService for DummyMapService {}
struct DummyConsoleService;
impl ConsoleService for DummyConsoleService {}
#[test] #[test]
fn test_core_services_all_fields() { fn test_core_services_all_fields() {
let services = CoreServices { let services = CoreServices {
@ -223,14 +246,19 @@ mod tests {
} }
#[test] #[test]
#[should_panic(expected = "Phase 93")] fn test_with_core_from_registry_missing_box() {
fn test_with_core_from_registry_todo() { // Phase 93: registry が空の場合はエラーを返すことを確認
// Phase 92: todo!() を返すことを確認
use crate::runtime::ring0::default_ring0; use crate::runtime::ring0::default_ring0;
let ring0 = Arc::new(default_ring0()); let ring0 = Arc::new(default_ring0());
let registry = UnifiedBoxRegistry::new(); let registry = UnifiedBoxRegistry::new();
let _result = PluginHost::with_core_from_registry(ring0, &registry); let result = PluginHost::with_core_from_registry(ring0, &registry);
// Phase 93 で実装後はこのテストを削除 assert!(result.is_err());
// エラーメッセージに "not found" が含まれることを確認
if let Err(e) = result {
let msg = format!("{}", e);
assert!(msg.contains("not found"));
}
} }
} }