# タスク設計書: M-02 Registry バインド実装

最終更新: 2026-02-13
- ステータス: 完了(done)
- 作成者: Codex
- レビュー: shogohasegawa
- 対象コンポーネント: backend / docs
- 関連: `docs/product/goals.md`, `docs/product/milestones.md`, `docs/product/progress.md`, `docs/task-designs/20260213224942_m01-yaml-pydantic-schema.md`
- チケット/リンク: 該当なし
- 関連ゴールID: G-02
- 関連マイルストーンID: M-02

## 0. TL;DR
- M-02 の目的に合わせて、`handler` 文字列から Python callable を解決する Registry 契約と実装を追加する。
- Hexagonal 方針に従い、契約は `ports/outbound`、実装は `adapters/outbound` に分離する。
- 未登録ノード解決時は明示的な例外を返し、M-02 の DoD（未登録時エラー明示）を満たす。
- 単体テストで、登録・再登録防止・解決成功・未登録エラーを検証する。

## 1. 背景 / 課題
- M-01 で YAML/Pydantic スキーマは整備済みだが、`NodeSpec.handler` を実処理へ紐づける機構がまだ無い。
- このままでは G-02 の「YAML 定義と Python 実処理の疎結合接続」を満たせない。
- M-03（StateGraph ビルダー）に進む前に、`handler` 文字列を callable に解決する契約を固定する必要がある。

## 2. ゴール / 非ゴール
### 2.1 ゴール
- Registry の抽象契約（登録・解決）を ports として定義する。
- インメモリ Registry 実装を追加し、文字列キーから callable を解決できる。
- 未登録キー解決時に専用例外を返す。
- 単体テストで成功系/失敗系を担保する。

### 2.2 非ゴール
- LangGraph `StateGraph` の組み立ては行わない（M-03で対応）。
- YAML ファイル I/O や CLI 実行導線は追加しない。
- ノード callable の厳密な引数シグネチャ制約はこのタスクでは固定しない。

## 3. スコープ / 影響範囲
- 変更対象: `src/graphyml/ports/outbound/**`, `src/graphyml/adapters/outbound/**`, `tests/unit/**`, `docs/product/progress.md`。
- 影響範囲: 今後のユースケース層（M-03）で利用するノード解決インターフェース。
- 互換性: 初回導入のため後方互換問題はない。
- 依存関係: 標準ライブラリのみ（新規依存追加なし）。

## 4. 要件
### 4.1 機能要件
- `NodeHandler` 型（`Callable[..., Any]`）を定義する。
- Registry 契約に以下を持たせる。
  - `register(name: str, handler: NodeHandler) -> None`
  - `resolve(name: str) -> NodeHandler`
- インメモリ実装で以下を満たす。
  - 文字列キーの登録
  - キー重複登録の拒否
  - 登録済みキーの解決
  - 未登録キー解決時に専用例外を送出
- 例外クラスを分離する。
  - `NodeRegistryError`（基底）
  - `NodeHandlerAlreadyRegisteredError`
  - `NodeHandlerNotFoundError`

### 4.2 非機能要件 / 制約
- docstring は Google style（日本語説明）で記述する。
- 型ヒントを必須にし、`mypy` を通す。
- `ruff`/`pytest` を通し、既存品質ゲートを壊さない。
- ドメイン依存を逆転させるため、`adapters` から `ports` へ依存する構造を維持する。

## 5. 仕様 / 設計
### 5.1 全体方針
- 契約を `ports` に置き、実装を `adapters` に置く Hexagonal 分離を徹底する。
- M-03 で拡張しやすいように、`resolve` は callable を返す最小責務に留める。
- 失敗は専用例外で表現し、呼び出し側がハンドリングしやすい形にする。

### 5.2 変更点一覧
| 対象 | 変更内容 | 影響 | 備考 |
| --- | --- | --- | --- |
| `src/graphyml/ports/outbound/node_registry.py` | Registry 抽象契約と型/例外を追加 | M-02 契約の正本 | 新規 |
| `src/graphyml/adapters/outbound/in_memory_node_registry.py` | インメモリ Registry 実装を追加 | 文字列→callable 解決機構 | 新規 |
| `src/graphyml/ports/outbound/__init__.py` | 公開 API をエクスポート | 利用導線の統一 | 更新 |
| `src/graphyml/adapters/outbound/__init__.py` | 実装クラスをエクスポート | 利用導線の統一 | 更新 |
| `tests/unit/adapters/test_in_memory_node_registry.py` | 登録/解決/重複/未登録のテスト追加 | 品質担保 | 新規 |
| `docs/product/progress.md` | G02-I01/G02-I02/G02-I03 の状態更新 | 進捗同期 | 更新（必要時） |

### 5.3 詳細
#### API
- 外部 API 追加はなし（ライブラリ内部 API のみ）。

#### UI
- 該当なし。

#### データモデル / 永続化
- 永続化は行わない。メモリ上辞書で管理する。

#### 設定 / 環境変数
- 追加なし。

### 5.4 代替案と不採用理由
- 代替案A: `domain/services` に直接 Registry 実装を置く。
  - 不採用理由: Hexagonal の契約/実装分離が崩れ、差し替え困難になる。
- 代替案B: `resolve` で `None` を返す。
  - 不採用理由: 呼び出し側のエラー見落としが起きやすく、DoD の「未登録時エラー明示」を満たしにくい。

## 6. 移行 / ロールアウト
- フェーズ1: ports に抽象契約と例外を追加。
- フェーズ2: adapters にインメモリ実装を追加。
- フェーズ3: 単体テスト追加と品質ゲート実行。
- ロールバック条件: `ruff`/`mypy`/`pytest` のいずれか失敗。
- ロールバック手順: 追加ファイル差分を戻し、設計を再調整する。

## 7. テスト計画
- 単体:
  - 正常系: 登録した callable を `resolve` で取得できる。
  - 異常系: 重複登録で `NodeHandlerAlreadyRegisteredError`。
  - 異常系: 未登録解決で `NodeHandlerNotFoundError`。
- 結合: 該当なし（M-03 で利用テストを実施）。
- 手動: `uv run ruff check .`, `uv run mypy .`, `uv run pytest -q`。
- LLM/外部依存: 該当なし。
- 合格条件: 新規テストが通り、既存テストを壊さない。

## 8. 受け入れ基準
- ports に Registry 契約と例外が実装されている。
- adapters にインメモリ実装があり、登録・解決ができる。
- 重複登録と未登録解決で専用例外が送出される。
- `tests/unit/adapters/test_in_memory_node_registry.py` が通る。
- `uv run ruff check .` / `uv run mypy .` / `uv run pytest -q` が成功する。

## 9. リスク / 対策
- callable 署名を厳密化しすぎるリスク。
  - 対策: M-02 は `Callable[..., Any]` で最小契約にとどめ、M-03 で必要に応じて拡張する。
- 登録重複の扱いが曖昧になるリスク。
  - 対策: 明示例外を仕様として固定し、テストで担保する。
- 例外メッセージが利用者に分かりにくいリスク。
  - 対策: キー名を含むエラーメッセージを実装し、文字列マッチでテストする。

## 10. オープン事項 / 要確認
- 該当なし。

## 11. 実装タスクリスト
- [x] ports に `NodeHandler` 型、Registry 契約、例外を追加する。
- [x] adapters にインメモリ Registry 実装を追加する。
- [x] 単体テスト（登録/解決/重複/未登録）を追加する。
- [x] `docs/product/progress.md` の G-02 進捗を更新する。
- [x] 品質ゲート（ruff/mypy/pytest）を通す。

## 12. ドキュメント更新
- [ ] `README.md`（必要に応じて）
- [ ] `AGENTS.md`（必要に応じて）
- [x] `docs/product/progress.md`（G-02進捗更新）

## 13. 承認ログ
- 承認者: shogohasegawa
- 承認日時: 2026-02-13 23:04
- 承認コメント: 「OK」にて実装承認

## 実装開始条件
- [x] ステータスが `承認済み(approved)` である
- [x] 10. オープン事項が空である
- [x] 受け入れ基準とテスト計画に合意済み
