# 通知設計書 36-アクティブプロジェクトコールバックエラー

## 概要

本ドキュメントは、Julia のアクティブプロジェクト変更時に実行されるコールバック関数が失敗した場合に出力されるエラー通知の設計を記述する。

### 本通知の処理概要

本通知は、`set_active_project` 関数によりアクティブプロジェクトが変更された際に `active_project_callbacks` 配列内のコールバック関数がエラーをスローした場合に、エラーレベルのログを出力する処理である。

**業務上の目的・背景**：Julia のプロジェクト環境管理では、アクティブプロジェクト（`Project.toml`）が変更されると、登録されたコールバック関数が呼び出される。これは Revise.jl などのツールがプロジェクト変更を追跡するための仕組みである。コールバックが失敗してもプロジェクトの切り替え自体は完了させるべきであり、本通知はエラーを記録しつつ処理を続行させる。

**通知の送信タイミング**：`set_active_project` 関数内で `active_project_callbacks` 配列のいずれかのコールバックが例外をスローした時点で送信される。具体的には `base/initdefs.jl` の383行目で発火する。

**通知の受信者**：Julia プロセスの標準エラー出力 (stderr) を監視する開発者・運用者。ログレベルは `Error`。

**通知内容の概要**：`"active project callback {コールバック関数} failed"` というメッセージが出力される。`maxlog=1` が指定されており、同一コールバックのエラーは最大1回のみ出力される。

**期待されるアクション**：受信者はコールバックを登録したツール（例：Revise.jl）の問題を調査する。プロジェクト切り替え自体は正常に完了しているため、コールバックに依存する機能（例：コード変更追跡）のみが影響を受ける。

## 通知種別

ログ（Error） -- Julia の標準ログシステム（`@error` マクロ）による stderr へのエラーレベル出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（`@error` マクロによる即座のログ出力） |
| 優先度 | 中 |
| リトライ | 無し |

### 送信先決定ロジック

Julia の標準ログシステムにより、現在アクティブなログハンドラに送信される。`maxlog=1` の指定により、同一コールバック関数に対するエラーは1回のみ出力される。

## 通知テンプレート

### メール通知の場合

該当なし（本通知はログ出力であり、メール送信は行わない）

### 本文テンプレート

```
active project callback {f} failed
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| f | 失敗したコールバック関数の表現 | `active_project_callbacks` 配列の要素 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロジェクト変更 | `set_active_project` の呼び出し時 | `active_project_callbacks` 内のコールバックが例外をスロー | `set_active_project` 関数内の `try-catch` で検出 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| コールバックが正常終了 | すべてのコールバックが例外をスローしなければ本通知は発生しない |
| `maxlog=1` | 同一コールバックのエラーは最大1回のみ出力される |
| `active_project_callbacks` が空 | 登録されたコールバックがなければ実行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["set_active_project(projfile) 呼び出し"] --> B["ACTIVE_PROJECT[] = projfile"]
    B --> C["active_project_callbacks をイテレート"]
    C --> D["Base.invokelatest(f) を実行"]
    D --> E{"コールバック成功か"}
    E -->|成功| F["次のコールバックへ"]
    E -->|失敗 catch| G["@error active project callback f failed"]
    G --> F
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| コールバックエラー | `active_project_callbacks` 内の関数がエラーをスロー | @error 通知を発行し、次のコールバックの実行を続行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無し |
| リトライ間隔 | -- |
| リトライ対象エラー | -- |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | `maxlog=1` により同一コールバック関数あたり最大1回 |
| 1日あたり上限 | 同上 |

### 配信時間帯

制限なし

## セキュリティ考慮事項

本通知はコールバック関数の表現（関数名等）を含むが、通常は機密情報を含まない。

## 備考

- `set_active_project` は `--project` コマンドライン引数の処理時、`Pkg.activate()` の呼び出し時などに実行される。
- `active_project_callbacks` はモジュールレベルの定数配列（`const active_project_callbacks = []`、initdefs.jl 201行目）として定義されている。
- `maxlog=1` の指定により、頻繁なプロジェクト切り替え時にログが溢れることを防止している。

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | initdefs.jl | `base/initdefs.jl` | `ACTIVE_PROJECT` Ref（197行目）と `active_project_callbacks` 配列（201行目）の構造を理解する |

**読解のコツ**: `ACTIVE_PROJECT` は `Ref{Union{String,Nothing}}` であり、現在のアクティブプロジェクトのパスを保持する。`active_project_callbacks` は引数なしの関数（thunk）を格納する配列。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | initdefs.jl | `base/initdefs.jl` | `set_active_project` 関数（377-386行目）が本通知のエントリーポイント |

**主要処理フロー**:
1. **378行目**: `ACTIVE_PROJECT[] = projfile` でアクティブプロジェクトを更新
2. **379行目**: `for f in active_project_callbacks` でコールバックをイテレート
3. **381行目**: `Base.invokelatest(f)` でコールバックを実行
4. **383行目**: `@error "active project callback $f failed" maxlog=1` でエラーを通知

#### Step 3: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | initdefs.jl | `base/initdefs.jl` | `init_active_project` 関数（263-272行目）が起動時の初期化 |

**主要処理フロー**:
- **264行目**: `JLOptions().project` や `ENV["JULIA_PROJECT"]` からプロジェクトパスを取得
- **267-271行目**: `set_active_project()` を呼び出してアクティブプロジェクトを設定

### プログラム呼び出し階層図

```
init_active_project() [base/initdefs.jl:263]
    |
    +-- set_active_project(projfile) [行377]
            |
            +-- ACTIVE_PROJECT[] = projfile [行378]
            +-- for f in active_project_callbacks [行379]
                    |
                    +-- Base.invokelatest(f) [行381]
                    |       (ここでエラー発生時)
                    +-- @error (No.36 本通知) [行383]
```

### データフロー図

```
[入力]                    [処理]                          [出力]

projfile (String) -----> set_active_project()
                            |
                            +-- ACTIVE_PROJECT 更新
                            +-- コールバック実行
                            |       |
                            |       +-- エラー発生 --> catch
                            |                           |
                            |                           +--> @error ログ --> stderr
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| initdefs.jl | `base/initdefs.jl` | ソース | 初期化定義、`set_active_project` 関数と `active_project_callbacks` の定義 |
| logging.jl | `base/logging.jl` | ソース | `@error` マクロの定義 |
