# ADR 0032: ユーザー拡張のためのクラスベース・フックシステムの導入

## ステータス

提案中 (Proposed) / 承認済 (Accepted)

## コンテキスト（背景と課題）

`beautyspot` のユーザー層が広がるにつれ、キャッシュ機能と連携して「LLMAPIの消費トークン数・節約トークン数の計算」や「関数の実行時間（レイテンシ）の計測」といったカスタムメトリクスを収集したいという要望が高まっている。

これらのメトリクス収集ロジックをライブラリのコア（`core.py`）に直接組み込むことは、汎用性を損ない、特定のドメイン（LLMなど）に依存してしまうため、ライブラリの設計思想（シンプルさと関心の分離）に反する。

したがって、コアロジックを汚染することなく、ユーザーが自身の関心事（ロギング、メトリクス送信、事前バリデーションなど）を任意のタスク実行ライフサイクルに差し込める「拡張ポイント（フック）」を提供する仕組みが必要である。

## 決定事項

タスクの実行ライフサイクルに介入するためのプラグインアーキテクチャとして、以下の設計を採用する。

1. **クラスベースのフックインターフェース (`HookBase`)**
* 関数ベースのコールバックではなく、状態（例：`pre_execute`で記録した開始時間）をインスタンス変数として保持できるクラスベースのフックを採用する。
* インターフェースには `pre_execute`, `on_cache_hit`, `on_cache_miss` の3つのタイミングを定義し、デフォルト実装は空 (`pass`) とする。これにより、ユーザーは必要なフックのみをオーバーライドできる。


2. **型安全なコンテキストオブジェクト (`types.py`)**
* フックの種類ごとに、利用可能なデータが明確に異なる（例：実行前は戻り値が存在しない）。そのため、単一のコンテキストクラスを使い回すのではなく、`HookContextBase` を継承した以下の専用クラスを提供する。
* `PreExecuteContext`
* `CacheHitContext`
* `CacheMissContext`


* これにより、IDEでの型推論と補完が正確に機能し、優れたDeveloper Experience (DX) を提供する。


3. **ゼロオーバーヘッドのスキップ処理 (`core.py`)**
* `@spot.mark` には `hooks: Optional[Sequence[HookBase]] = None` としてフックを受け入れる。
* フックが登録されていない場合（デフォルトの利用形態）、コンテキストオブジェクトの生成処理自体を完全にスキップ（Fast-path）し、既存のパフォーマンスへの影響をゼロにする。



## 結果（トレードオフ）

* **ポジティブな影響**:
* コアの肥大化を防ぎつつ、ユーザーに強力な拡張性を提供できる。
* 将来的にフックのタイミング（例: `on_save_complete` など）を追加したくなった際も、既存のユーザーコードやシグネチャを破壊することなく `HookBase` を拡張するだけで対応できる（高いAPI安定性）。
* メトリクス計測時において、実行前と実行後の情報をクラスのインスタンス変数経由で容易に共有できる。


* **ネガティブな影響**:
* 非常に単純な（状態を持たない）コールバック関数を1つだけ登録したいユーザーにとっても、クラスを定義してインスタンス化するというわずかなボイラープレート（記述量）の増加を強いることになる。ただし、このトレードオフは型安全性と拡張性のメリットが上回ると判断した。

