# 画面設計書 20-タスク詳細画面

## 概要

本ドキュメントは、Giselle Studio の「タスク詳細画面」に関する画面設計書である。タスクの実行状況、各ステップの進行状況、最終出力結果を表示し、リアルタイムでタスクの進行を監視できる画面を定義する。

### 本画面の処理概要

**業務上の目的・背景**：実行されたタスクの詳細な実行状況を確認し、各ステップの処理結果や最終出力を閲覧する。タスクが実行中の場合はポーリングにより自動更新され、リアルタイムで進行状況を把握できる。

**画面へのアクセス方法**：タスク一覧画面からタスクIDリンクをクリック、プレイグラウンド画面でタスク実行後のリダイレクト。

**主要な操作・処理内容**：
1. タスクのステータス表示（In Progress/Completed/Failed/Cancelled）
2. 各ステップの進行状況表示（アコーディオン形式）
3. 最終出力結果の表示（複数出力時はタブ切り替え）
4. ワークスペースエディタへの遷移
5. 新規タスクの実行（同じアプリで再実行）

**画面遷移**：
- 遷移元：タスク一覧画面（/tasks）、プレイグラウンド画面（/playground）
- 遷移先：ワークスペースエディタ画面（/workspaces/[workspaceId]）、新規タスク詳細画面（/tasks/[newTaskId]）

**権限による表示制御**：認証済みユーザーのみアクセス可能。タスクが属するチームのメンバーのみ閲覧可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | タスク取得 | 主機能 | タスクの詳細情報取得 |
| 22 | タスク作成・開始 | 補助機能 | 新規タスクの作成と実行 |
| 24 | タスクストリーム | API連携 | タスク実行状況のリアルタイム更新 |

## 画面種別

詳細画面

## URL/ルーティング

`/tasks/[taskId]`

**パスパラメータ**:
- `taskId` (必須): タスクの一意識別子（TaskId形式）

## 入出力項目

| 項目名 | 項目ID | 入出力 | 必須 | データ型 | 制約・バリデーション |
|--------|--------|--------|------|----------|---------------------|
| タスクID | taskId | 入力（パスパラメータ） | 必須 | TaskId | TaskId.safeParse検証 |

## 表示項目

| 項目名 | 説明 | データソース |
|--------|------|-------------|
| ステータスバッジ | タスクの実行状態 | data.status |
| タイトル | ワークスペース名 | workspace.name |
| 説明 | アプリの説明文 | app.description |
| ワークスペース編集リンク | エディタへのリンク | data.workspaceId |
| タスク入力 | 実行時の入力パラメータ | data.input |
| ステップセクション | 進行状況とステップ一覧 | data.stepsSection |
| ステップ進捗バー | 完了/全体ステップ数 | completedStepsCount/totalStepsCount |
| ステップアイテム | 各ステップの詳細（ノード名、サブラベル、ステータス） | step.items |
| 最終出力 | 生成結果の表示 | data.finalStep.outputs |
| 再実行フォーム | 新規タスク実行のための入力UI | TaskStageInput |

## イベント仕様

### 1-自動更新（ポーリング）

**トリガー**: タスクが非終端状態（created/inProgress）の場合

**処理フロー**:
1. 初回は1500ms後にrefreshActionを呼び出し
2. refreshActionでgetTaskData()を実行
3. 成功時、UITask状態を更新
4. タスクが終端状態（completed/failed/cancelled）になるまで1500msごとに繰り返し
5. エラー時は3000msごとにリトライ継続

### 2-ステップアコーディオン展開

**トリガー**: ステップセクションヘッダーをクリック

**処理フロー**:
1. Radix Accordionの状態を切り替え
2. ステップ一覧の表示/非表示を切り替え

### 3-ワークスペース編集

**トリガー**: 「Edit in Workspace」バッジをクリック

**処理フロー**:
1. /workspaces/{workspaceId} を新規タブで開く

### 4-新規タスク実行

**トリガー**: TaskStageInputコンポーネントで送信

**処理フロー**:
1. handleSubmit関数が呼ばれる
2. showOverlayでオーバーレイを表示
3. createAndStartTaskAction Server Actionを呼び出し
4. giselle.createTaskでタスク作成
5. giselle.startTaskでタスク開始
6. 成功時、/tasks/{newTaskId}へ遷移
7. エラー時、オーバーレイを非表示

### 5-出力タブ切り替え

**トリガー**: 出力が複数ある場合、タブをクリック

**処理フロー**:
1. Radix Tabsで選択タブを切り替え
2. 対応するGenerationViewを表示

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 詳細取得 | tasks | SELECT | タスク情報取得 |
| 詳細取得 | apps | SELECT | アプリ情報取得 |
| 詳細取得 | workspaces | SELECT | ワークスペース情報取得 |
| 新規タスク実行 | tasks | INSERT | 新規タスクの作成 |
| 新規タスク実行 | generations | INSERT | タスク内の生成ステップ作成 |

### テーブル別更新項目詳細

#### tasks（SELECT）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, teamDbId | WHERE id = taskId | タスク取得 |

#### giselle.getTask（SDK経由）

| 操作 | 取得情報 | 備考 |
|-----|---------|------|
| 取得 | sequences, steps, status, nodeIdsConnectedToEnd | タスクの実行状態と構造 |

#### giselle.getGeneration（SDK経由）

| 操作 | 取得情報 | 備考 |
|-----|---------|------|
| 取得 | id, status, context, error, output | 各ステップの生成結果 |

## メッセージ仕様

| 種別 | メッセージID | メッセージ内容 | 表示条件 |
|------|-------------|---------------|----------|
| ステータス | in_progress | In Progress | status === "inProgress" |
| ステータス | completed | Completed | status === "completed" or "created" |
| ステータス | failed | Failed | status === "failed" |
| ステータス | cancelled | Cancelled | その他 |
| 進捗 | running_step | Running Step {n} | 実行中ステップがある場合 |
| 進捗 | preparing_steps | Preparing {n} Step(s) | 準備中ステップがある場合 |
| 進捗 | completed_steps | Completed {n} Step(s) | 実行完了の場合 |
| 結果 | results | Results | 常時表示 |
| 結果 | results_count | Results ({n}) | 複数出力の場合 |
| 結果 | running | Running... | 出力が未完了の場合 |
| 入力 | no_input | No task input | input === null |
| 入力 | github_webhook | GitHub Webhook | GitHubイベント入力の場合 |
| リンク | edit_workspace | Edit in Workspace | 常時表示 |
| トースト | auth_error | You do not have permission to view this task. | 権限エラー時 |

## 例外処理

| 例外状況 | 処理内容 | ユーザーへの通知 |
|---------|---------|----------------|
| 不正なTaskId | notFound()でエラーページ表示 | 404ページ表示 |
| タスク未存在 | notFound()でエラーページ表示 | 404ページ表示 |
| チーム非メンバー | notFound()でエラーページ表示 | 404ページ表示 |
| nodeIdsConnectedToEnd未設定 | Error throw | 500エラーページ表示 |
| 権限エラー（ポーリング時） | トースト表示、/tasksへリダイレクト | エラートースト表示 |
| refreshAction失敗 | ポーリング継続（3000ms間隔） | なし（サイレント） |
| Generation取得失敗 | undefinedとして処理、ステップアイテム非表示 | なし |

## 備考

- ポーリング間隔：1500ms（正常時）、3000ms（エラー時）
- 終端状態でポーリング停止：completed, failed, cancelled
- UITask型：status, title, description, workspaceId, input, stepsSection, finalStep
- UIStep型：id, index, title, status, items
- UIStepItem型：status別にcompleted/failed/その他の3パターン
- GenerationViewコンポーネントで生成結果を表示（Markdownレンダリング対応）
- TaskOverlayStoreで新規タスク実行時のオーバーレイ状態を管理
- 初期選択アプリは元タスクのアプリID（getTaskAppId）

---

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

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

### 推奨読解順序

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

UITask型とステップ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | task-data.ts | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-data.ts` | UITask型（64-84行目）、UIStep型（45-62行目）、UIStepItem型（31-43行目） |

**読解のコツ**: UITaskにはstatus, title, description, workspaceId, input, stepsSection, finalStepが含まれる。stepsSectionはtitle, totalStepsCount, completedStepsCount, stepsを持つ。

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

Server Componentの構造とアクセス権検証を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | page.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/page.tsx` | TaskId検証（34-40行目）、アクセス権検証（42-53行目）、データ取得（55-59行目） |

**主要処理フロー**:
1. **34-40行目**: TaskId.safeParseでパラメータ検証
2. **42-45行目**: getCurrentUser(), getTaskTeamDbId()を並列実行
3. **50-53行目**: isMemberOfTeam()でアクセス権検証
4. **55-59行目**: getTaskData(), loadStageAppsData(), getTaskAppId()を並列実行
5. **61-77行目**: refreshAction Server Actionの定義

#### Step 3: クライアントコンポーネントを理解する

ポーリング処理とUI構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | task-client.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-client.tsx` | ポーリング処理（39-151行目）、UIレンダリング（153-167行目） |

**主要処理フロー**:
- **30行目**: useState(initial)でUITaskを管理
- **39-151行目**: useEffectでポーリング処理
- **43-125行目**: tick()関数で定期的にrefreshAction呼び出し
- **112-118行目**: 終端状態でポーリング停止
- **155-161行目**: TaskHeader, StepsSection, FinalStepOutputをレンダリング

#### Step 4: データ取得処理を理解する

タスク詳細取得とUI用変換を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | task-data.ts | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-data.ts` | getTaskData関数（167-348行目） |

**主要処理フロー**:
1. **168行目**: giselle.getTask()でタスク取得
2. **179-192行目**: 各ステップのGenerationを並列取得
3. **231-299行目**: UIStep[]への変換（ステータス別処理）
4. **302-322行目**: 最終出力の抽出（nodeIdsConnectedToEnd）
5. **324-328行目**: workspace, app, inputの並列取得

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

```
page.tsx (Server Component)
    │
    ├─ TaskId.safeParse() ─── パラメータ検証
    │
    ├─ getCurrentUser() ─── 現在ユーザー取得
    │
    ├─ getTaskTeamDbId() ─── タスクのチームID取得
    │      └─ db.select(tasks)
    │
    ├─ isMemberOfTeam() ─── アクセス権検証
    │
    ├─ getTaskData() ─── task-data.ts
    │      │
    │      ├─ giselle.getTask()
    │      ├─ giselle.getGeneration() (並列)
    │      ├─ giselle.getWorkspace()
    │      └─ getAppByTaskId() → giselle.getApp()
    │
    ├─ loadStageAppsData() ─── stage-apps.ts
    │
    ├─ getTaskAppId() ─── task-data.ts
    │
    └─ TaskClient (Client Component) ─── task-client.tsx
           │
           ├─ useEffect (polling)
           │      └─ refreshAction() ─── Server Action
           │             └─ getTaskData()
           │
           ├─ TaskHeader ─── task-header.tsx
           │
           ├─ StepsSection ─── steps-section.tsx
           │      └─ StepItem
           │
           ├─ FinalStepOutput ─── final-step-output.tsx
           │      ├─ GenerationView
           │      └─ OutputActions
           │
           └─ TaskStageInput ─── task-stage-input.client.tsx
                  └─ createAndStartTaskAction()
```

### データフロー図

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

taskId (URL) ───────────▶ TaskId.safeParse() ──────────▶ 有効なTaskId
                               │
ユーザー情報 ───────────▶ getCurrentUser() ─────────────▶ currentUser
                               │
                         getTaskTeamDbId()
                               │
                         isMemberOfTeam() ─────────────▶ アクセス許可
                               │
                               ▼
                         getTaskData()
                               │
                               ├─ giselle.getTask()
                               ├─ giselle.getGeneration() ─▶ generationsByStepId
                               ├─ giselle.getWorkspace()
                               └─ getAppByTaskId()
                                      │
                                      ▼
                               UITask ─────────────────────▶ TaskClient
                                      │
                                      ├─ TaskHeader ─────────▶ ステータス/タイトル/説明/入力表示
                                      │
                                      ├─ StepsSection ───────▶ ステップ進捗表示
                                      │
                                      └─ FinalStepOutput ────▶ 生成結果表示

[ポーリング]
TaskClient ──────────────▶ refreshAction() ─────────────▶ 最新UITask
     │                           │
     └──────1500ms間隔──────────┘ (非終端状態の間)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| page.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/page.tsx` | ソース | ページエントリーポイント（Server Component） |
| task-client.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-client.tsx` | ソース | クライアントコンポーネント（ポーリング管理） |
| task-data.ts | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-data.ts` | ソース | データ型定義とデータ取得関数 |
| steps-section.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/steps-section.tsx` | ソース | ステップ一覧表示（アコーディオン） |
| final-step-output.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/final-step-output.tsx` | ソース | 最終出力表示（タブ/シングル） |
| task-stage-input.client.tsx | `apps/studio.giselles.ai/app/(main)/tasks/[taskId]/ui/task-stage-input.client.tsx` | ソース | 新規タスク実行フォーム |
| task-header.tsx | `apps/studio.giselles.ai/components/task/task-header.tsx` | ソース | タスクヘッダー（ステータス、タイトル、入力表示） |
| task-layout.tsx | `apps/studio.giselles.ai/components/task/task-layout.tsx` | ソース | レイアウトコンポーネント |
| stage-apps.ts | `apps/studio.giselles.ai/app/(main)/lib/stage-apps.ts` | ソース | アプリデータ取得 |
| create-and-start-task.ts | `apps/studio.giselles.ai/app/(main)/lib/create-and-start-task.ts` | ソース | タスク作成・開始Server Action |
| generation-view | `internal-packages/workflow-designer-ui/src/ui/generation-view` | ソース | 生成結果表示コンポーネント |
| status-badge | `internal-packages/ui/src/status-badge.tsx` | ソース | ステータスバッジコンポーネント |
