# 機能設計書 30-生成キャンセル

## 概要

本ドキュメントは、Giselleシステムにおける「生成キャンセル」機能の設計仕様を定義する。この機能は、実行中または待機中の生成（Generation）を明示的にキャンセルするための機能である。

### 本機能の処理概要

**業務上の目的・背景**：AIワークフローの実行中に、ユーザーが生成処理を中断したい場合がある。この機能は、不要になった生成処理を即座に停止し、システムリソースを解放するために提供される。

**機能の利用シーン**：
- ユーザーがワークフロー実行を手動で中断する場合
- タスク全体のキャンセル時に関連する生成を停止する場合
- 誤った設定での実行を早期に停止する場合
- 長時間実行中の生成を途中で停止する場合

**主要な処理内容**：
1. 指定されたgenerationIdで生成オブジェクトを取得する
2. 生成オブジェクトが存在しない場合はエラーをスローする
3. 生成オブジェクトのstatusを"cancelled"に変更する
4. cancelledAtタイムスタンプを設定する
5. 更新された生成オブジェクトをストレージに保存する
6. ノードインデックスとタスクインデックスを更新する

**関連システム・外部連携**：
- Storage: 生成オブジェクトの永続化
- Index: ノード別・タスク別生成インデックス

**権限による制御**：
- 認証済みユーザーによるワークスペースへのアクセス権限

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | ワークフローエディタ画面 | 主画面 | 生成実行のキャンセル |
| 18 | プレイグラウンド画面 | 主画面 | テスト実行のキャンセル |

## 機能種別

生成制御 / ステータス管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| context | GiselleContext | Yes | Giselleコンテキスト | 有効なコンテキストオブジェクト |
| generationId | GenerationId | Yes | キャンセル対象の生成ID | 存在する生成IDであること |

### 入力データソース

- Storage: `generations/{generationId}/generation.json`

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| CancelledGeneration | CancelledGeneration | キャンセル済み生成オブジェクト |

### CancelledGeneration型の構造

| フィールド | 型 | 説明 |
|-----------|-----|------|
| id | GenerationId | 生成の一意識別子 |
| context | GenerationContext | 生成コンテキスト |
| status | "cancelled" | キャンセル済みステータス |
| createdAt | number | 作成タイムスタンプ |
| cancelledAt | number | キャンセルタイムスタンプ |
| messages | Message[] | メッセージ配列（オプション） |
| queuedAt | number | キュー追加タイムスタンプ（オプション） |
| startedAt | number | 開始タイムスタンプ（オプション） |

### 出力先

- Storage: `generations/{generationId}/generation.json`（更新）
- Storage: `generations/byNode/{nodeId}.json`（インデックス更新）
- Storage: `tasks/{taskId}/generation-indexes.json`（タスクインデックス更新、タスク関連時）

## 処理フロー

### 処理シーケンス

```
1. 生成取得
   └─ getGenerationでストレージから生成オブジェクトを取得

2. 存在確認
   └─ 生成が存在しない場合はエラーをスロー

3. キャンセル状態作成
   ├─ status: "cancelled" を設定
   └─ cancelledAt: Date.now() を設定

4. ストレージ保存
   └─ internalSetGenerationで生成オブジェクトを保存

5. インデックス更新
   ├─ ノード別インデックスを更新
   └─ タスク別インデックスを更新（タスク関連時）

6. 結果返却
   └─ CancelledGenerationを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[getGeneration]
    B --> C{生成存在?}
    C -->|No| D[Error: Generation not found]
    C -->|Yes| E[CancelledGeneration構築]
    E --> F[status = cancelled]
    F --> G[cancelledAt = Date.now]
    G --> H[internalSetGeneration]
    H --> I[ストレージ保存]
    I --> J[ノードインデックス更新]
    J --> K{タスク関連?}
    K -->|Yes| L[タスクインデックス更新]
    K -->|No| M[CancelledGeneration返却]
    L --> M
    M --> N[終了]
    D --> O[例外終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | 存在確認必須 | 生成オブジェクトが存在しない場合はエラー | 常時 |
| BR-30-02 | ステータス更新 | statusを"cancelled"に変更 | キャンセル時 |
| BR-30-03 | タイムスタンプ記録 | cancelledAtに現在時刻を記録 | キャンセル時 |
| BR-30-04 | 元データ保持 | キャンセル前の全フィールドを保持 | キャンセル時 |
| BR-30-05 | インデックス同期 | ノードとタスクのインデックスを更新 | 保存時 |

### 計算ロジック

**タイムスタンプ**：
- cancelledAt = Date.now()（ミリ秒単位のUNIXタイムスタンプ）

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 生成取得 | Storage (generations/{id}/generation.json) | SELECT | 生成オブジェクト取得 |
| 生成更新 | Storage (generations/{id}/generation.json) | UPDATE | キャンセル状態保存 |
| ノードインデックス更新 | Storage (generations/byNode/{nodeId}.json) | UPDATE | インデックス更新 |
| タスクインデックス更新 | Storage (tasks/{taskId}/generation-indexes.json) | UPDATE | インデックス更新 |

### テーブル別操作詳細

#### Storage: generations/{generationId}/generation.json

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | generation | generationId一致 | Zodスキーマでバリデーション |
| UPDATE | status | "cancelled" | キャンセル時 |
| UPDATE | cancelledAt | Date.now() | キャンセル時 |

#### Storage: generations/byNode/{nodeId}.json

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | indexes[] | NodeGenerationIndex | upsert処理 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------
| GENERATION_NOT_FOUND | NotFoundError | 指定IDの生成が存在しない | 正しい生成IDを指定 |
| STORAGE_ERROR | StorageError | ストレージアクセス失敗 | ストレージ接続確認 |

### リトライ仕様

- ストレージエラー: 自動リトライなし
- 呼び出し側でリトライ判断

## トランザクション仕様

- 生成オブジェクトとインデックスは順次更新
- インデックス更新はキューイングにより非同期処理可能（タスクインデックス）
- 部分的な失敗時はログ出力のみ

## パフォーマンス要件

- キャンセル処理: 50ms以内
- ストレージ更新: 100ms以内
- インデックス更新: 100ms以内

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

- ワークスペースへのアクセス権限チェックは呼び出し側で実施
- 生成IDはUUID形式で推測困難
- キャンセル操作のログ記録

## 備考

- キャンセルは即座にステータスを更新するが、実際の処理停止はAbortSignal経由で伝播
- キャンセル後も生成オブジェクトは削除されない（監査証跡として保持）
- cancelledAt以外のタイムスタンプ（queuedAt、startedAtなど）は元の値を保持

---

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

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

### 推奨読解順序

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

CancelledGeneration型の構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/protocol/src/generation/index.ts` | CancelledGeneration型定義（133-143行目） |
| 1-2 | index.ts | `packages/protocol/src/generation/index.ts` | Generation判別共用体（157-164行目） |

**読解のコツ**: CancelledGenerationはGenerationBaseを拡張し、status="cancelled"とcancelledAtを必須フィールドとして持つ。

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

cancelGeneration関数の実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cancel-generation.ts | `packages/giselle/src/generations/cancel-generation.ts` | 関数全体（6-27行目） |

**主要処理フロー**:
1. **6-9行目**: 関数シグネチャ（context、generationIdを受け取る）
2. **10-13行目**: getGenerationで現在の生成を取得
3. **14-16行目**: 存在確認とエラースロー
4. **17-21行目**: CancelledGenerationオブジェクトの構築
5. **22-25行目**: internalSetGenerationでストレージ保存
6. **26行目**: CancelledGenerationを返却

#### Step 3: ストレージ操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | utils.ts | `packages/giselle/src/generations/utils.ts` | getGeneration関数（396-409行目） |
| 3-2 | set-generation.ts | `packages/giselle/src/generations/internal/set-generation.ts` | internalSetGeneration関数（22-58行目） |

#### Step 4: インデックス更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | set-generation.ts | `packages/giselle/src/generations/internal/set-generation.ts` | toNodeGenerationIndex関数（60-74行目） |

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

```
cancelGeneration (cancel-generation.ts:6-27)
    │
    ├─ getGeneration (utils.ts:396-409)
    │      │
    │      └─ storage.getJson
    │             └─ Generation.parse
    │
    └─ internalSetGeneration (set-generation.ts:22-58)
           │
           ├─ storage.setJson (27-31行目)
           │      └─ generations/{id}/generation.json
           │
           ├─ toNodeGenerationIndex (36行目)
           │      └─ cancelledAtを含むインデックス生成
           │
           ├─ getNodeGenerationIndexes (40-43行目)
           │
           ├─ upsertIndex (44行目)
           │      └─ 既存インデックスの更新または追加
           │
           ├─ writeNodeIndexes (45-49行目)
           │      └─ generations/byNode/{nodeId}.json
           │
           └─ updateTaskGenerationIndexes (55-57行目)
                  └─ tasks/{taskId}/generation-indexes.json
```

### データフロー図

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

generationId ─────────────▶ getGeneration
                                │
                                ▼
                           Generation ───────────▶ 存在確認
                                │
                                ├─▶ Error（存在しない場合）
                                │
                                ▼
                           CancelledGeneration構築
                                │
                                ├─ status: "cancelled"
                                │
                                └─ cancelledAt: Date.now()
                                    │
                                    ▼
                           internalSetGeneration
                                    │
                                    ├─▶ generation.json ──────▶ Storage
                                    │
                                    ├─▶ NodeGenerationIndex
                                    │        │
                                    │        └─▶ byNode/{nodeId}.json
                                    │
                                    └─▶ TaskGenerationIndex
                                             │
                                             └─▶ tasks/{taskId}/
                                                    generation-indexes.json
                                    │
                                    ▼
                           CancelledGeneration ────────────────▶ 返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cancel-generation.ts | `packages/giselle/src/generations/cancel-generation.ts` | ソース | メイン実装 |
| utils.ts | `packages/giselle/src/generations/utils.ts` | ソース | getGeneration、generationPath |
| set-generation.ts | `packages/giselle/src/generations/internal/set-generation.ts` | ソース | internalSetGeneration、インデックス更新 |
| index.ts | `packages/protocol/src/generation/index.ts` | ソース | CancelledGeneration型、Generation型 |
| giselle.ts | `packages/giselle/src/giselle.ts` | ソース | Giselleクラス（cancelGenerationメソッド公開） |
