# 通知設計書 8-show_child_exited（子プロセス終了通知）

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおける子プロセス終了時の通知機能の設計について記述する。

### 本通知の処理概要

show_child_exited通知は、ターミナルで実行されていた子プロセス（シェルやコマンド）が終了した際に、その結果をGUIバナーまたはターミナル内テキストで表示する機能である。特に異常終了（短時間での終了や非ゼロ終了コード）を検出した場合に、ユーザーにエラーを通知する。

**業務上の目的・背景**：シェルやコマンドが予期せず終了した場合、ユーザーはその原因を把握する必要がある。特にシェル起動時のエラーや設定ミスを早期に発見できるようにすることで、トラブルシューティングを支援する。

**通知の送信タイミング**：子プロセスが終了した直後に通知が表示される。異常終了（`abnormal-command-exit-runtime`ミリ秒以下での終了、かつLinuxでは非ゼロ終了コード）の場合は即座に通知、正常終了の場合も終了メッセージが表示される。

**通知の受信者**：Ghosttyターミナルを使用しているローカルユーザー。GTK版ではAdwaitaバナー、非対応環境ではターミナル内テキストとして表示される。

**通知内容の概要**：「Command succeeded」または「Command failed」のメッセージ。終了コードと実行時間の情報を含む。

**期待されるアクション**：ユーザーはバナーの閉じるボタンをクリックするか、任意のキーを押してサーフェスを閉じる。

## 通知種別

アプリ内通知（GTK Adwaitaバナー / ターミナル内テキスト）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（プロセス終了直後に即時表示） |
| 優先度 | 高（異常終了時）/ 通常（正常終了時） |
| リトライ | 無し |

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

通知は、子プロセスが終了したサーフェスに対して表示される。

## 通知テンプレート

### バナー通知の場合（GTK + Adwaita 1.3以上）

| 項目 | 内容 |
|-----|------|
| 正常終了メッセージ | "Command succeeded" |
| 異常終了メッセージ | "Command failed" |
| CSSクラス | 正常時: "normal" / 異常時: "abnormal" |
| 閉じるボタン | あり |

### ターミナル内テキストの場合

| 項目 | 内容 |
|-----|------|
| メッセージ | プロセス終了情報（終了コード等） |
| 位置 | ターミナル画面内 |

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| exit_code | u32 | 必須 | プロセスの終了コード |
| runtime_ms | u64 | 必須 | プロセスの実行時間（ミリ秒） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロセス終了 | 子プロセス終了 | 常に | 子プロセス終了時に発火 |

### 異常終了判定条件

| 条件 | 説明 |
|-----|------|
| 実行時間 | `abnormal-command-exit-runtime`ミリ秒以下（デフォルト250ms） |
| 終了コード | Linux: 非ゼロ / macOS: 任意 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Adwaita 1.3未満 | バナー表示はAdwaita 1.3以上が必要（テキストにフォールバック） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[子プロセス終了] --> B[child_exitedメッセージ送信]
    B --> C[Surface.handleMessage]
    C --> D[childExited関数]
    D --> E[child_exitedフラグ設定]
    E --> F{runtime_ms <= 閾値?}
    F -->|はい| G{Linux && exit_code == 0?}
    F -->|いいえ| H[正常終了処理]
    G -->|はい| H
    G -->|いいえ| I[異常終了検出]
    I --> J[performAction show_child_exited]
    H --> J
    J --> K{GTK + Adwaita 1.3?}
    K -->|はい| L[バナー表示]
    K -->|いいえ| M[ターミナル内テキスト表示]
    L --> N[終了]
    M --> N
```

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

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| GUI表示失敗 | GTKウィンドウが存在しない | ターミナル内テキストにフォールバック |
| performActionエラー | アクション実行失敗 | 警告ログ出力して処理継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（プロセス終了ごとに1回） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（24時間いつでも表示可能）

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

- 終了コードと実行時間のみを表示し、コマンド内容や出力は含まれないため、機密情報漏洩のリスクは低い
- プロセス終了のタイミングは第三者に知られる可能性がある

## 備考

- GTK + Adwaita 1.3以上でバナー表示、それ以外ではターミナル内テキスト
- バナー表示後、任意のキー入力でサーフェスが閉じられる
- `abnormal-command-exit-runtime`設定で異常終了判定の閾値を変更可能（デフォルト250ms）
- macOSではloginコマンド経由でシェルを起動するため、終了コードが正確に取得できない場合がある

---

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

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

### 推奨読解順序

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

子プロセス終了情報のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | surface.zig | `src/apprt/surface.zig` | ChildExited構造体（行117-131）を確認 |
| 1-2 | surface.zig | `src/apprt/surface.zig` | child_exitedメッセージ（行51-52）を確認 |

**読解のコツ**: ChildExitedはexit_code（u32）とruntime_ms（u64）の2フィールドを持つextern struct。GTK環境ではGObjectとしても動作する。

#### Step 2: 設定を理解する

異常終了判定の設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Config.zig | `src/config/Config.zig` | abnormal-command-exit-runtime設定（行1358-1365）を確認 |

**読解のコツ**: デフォルト値は250ms。この閾値以下の実行時間で終了した場合に異常終了と判定される。

#### Step 3: Surface処理を理解する

Surfaceでの子プロセス終了処理を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Surface.zig | `src/Surface.zig` | child_exitedフィールド（行142）を確認 |
| 3-2 | Surface.zig | `src/Surface.zig` | handleMessage child_exited（行1058）を確認 |
| 3-3 | Surface.zig | `src/Surface.zig` | childExited関数（行1198-1253）を確認 |

**読解のコツ**: childExited関数では、runtime_msとabnormal_command_exit_runtime_msを比較して異常終了を判定。異常終了時はshow_child_exitedアクションを実行し、GUIが表示できない場合はターミナル内にテキストを表示。

#### Step 4: アクション定義を理解する

show_child_exitedアクションの定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | action.zig | `src/apprt/action.zig` | show_child_exitedアクション定義（行309-310）を確認 |

#### Step 5: GTK実装を理解する

GTKでのバナー表示処理を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | application.zig | `src/apprt/gtk/class/application.zig` | showChildExited関数（行2438-2446）を確認 |
| 5-2 | surface.zig | `src/apprt/gtk/class/surface.zig` | childExited関数（行1450-1465）を確認 |
| 5-3 | surface_child_exited.zig | `src/apprt/gtk/class/surface_child_exited.zig` | SurfaceChildExitedBanner実装を確認 |

**読解のコツ**: SurfaceChildExitedはAdwaitaのバージョンにより2つの実装がある。Adwaita 1.3以上ではSurfaceChildExitedBanner、それ未満ではSurfaceChildExitedNoop。Noop版はコンパイル時にnoop定数で検出可能。

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

```
子プロセス終了
    |
    v
IOスレッドからsurface.push(.child_exited)
    |
    v
Surface.handleMessage() (src/Surface.zig:1058)
    |
    v
Surface.childExited() (src/Surface.zig:1198-1253)
    |   - child_exitedフラグ設定
    |   - runtime_ms <= abnormal_command_exit_runtime_ms 判定
    |
    v
rt_app.performAction(.show_child_exited) (src/Surface.zig:1215-1222, 1242-1249)
    |
    v
Application.showChildExited() (src/apprt/gtk/class/application.zig:2438-2446)
    |
    v
Surface.childExited() (src/apprt/gtk/class/surface.zig:1450-1465)
    |   - child_exitedプロパティ設定
    |   - child_exited_overlay.setData()
    |
    v
SurfaceChildExitedBanner.setData() (src/apprt/gtk/class/surface_child_exited.zig:76-91)
    |
    v
SurfaceChildExitedBanner.propData() (src/apprt/gtk/class/surface_child_exited.zig:96-117)
    |   - exit_code判定
    |   - バナータイトル設定（"Command succeeded" / "Command failed"）
    |   - CSSクラス設定（"normal" / "abnormal"）
    |
    v
adw.Banner表示
```

### データフロー図

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

子プロセス終了     ───▶  ChildExited構造体生成      ───▶  exit_code + runtime_ms
                              |
                              v
                         handleMessage
                              |
                              v
                         childExited()
                              |
                              v
                         異常終了判定
                              |
        ┌─────────────────────┴─────────────────────┐
        |                                           |
        v                                           v
    正常終了                                    異常終了
        |                                           |
        v                                           v
    終了メッセージ                              エラーバナー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| surface.zig | `src/apprt/surface.zig` | ソース | ChildExited構造体、メッセージ定義 |
| Config.zig | `src/config/Config.zig` | ソース | abnormal-command-exit-runtime設定 |
| Surface.zig | `src/Surface.zig` | ソース | childExited処理、異常終了判定 |
| action.zig | `src/apprt/action.zig` | ソース | show_child_exitedアクション定義 |
| application.zig | `src/apprt/gtk/class/application.zig` | ソース | GTKアプリケーション、showChildExited |
| surface.zig | `src/apprt/gtk/class/surface.zig` | ソース | GTKサーフェス、childExited |
| surface_child_exited.zig | `src/apprt/gtk/class/surface_child_exited.zig` | ソース | SurfaceChildExitedBanner実装 |
| surface-child-exited.blp | `src/apprt/gtk/ui/1.3/surface-child-exited.blp` | UI定義 | バナーUIテンプレート |
