# バッチ設計書 2-GitHubリポジトリインジェスト

## 概要

本ドキュメントは、GitHubリポジトリのコンテンツ（blob、Pull Request、Issue）をベクトルストアにインジェストする定期実行バッチ処理の設計を記載する。

### 本バッチの処理概要

本バッチは、チームに連携されたGitHubリポジトリのコンテンツ（ソースコードファイル、Pull Request、Issue）をベクトル埋め込みとして抽出・保存し、RAG機能で検索可能にするための処理を担う。リポジトリの最新状態を定期的に同期し、ナレッジベースを最新に保つ。

**業務上の目的・背景**：GitHubリポジトリに蓄積されたソースコード、Pull Requestのディスカッション、Issueの議論は、開発チームにとって貴重なナレッジベースとなる。本バッチは、これらのコンテンツをベクトル埋め込みとして抽出することで、AIワークフローからGitHubのコンテキストを活用した回答生成を可能にする。また、24時間ごとの再同期により、リポジトリの変更を継続的に反映する。

**バッチの実行タイミング**：10分ごとにcronジョブとして自動実行される（`*/10 * * * *`）。Vercel Cronを使用してスケジュール実行される。

**主要な処理内容**：
1. インジェスト対象リポジトリの抽出（idle、failed（リトライ時間経過後）、running（15分以上経過）、completed（24時間以上経過）のいずれか）
2. 各リポジトリ・コンテンツタイプごとのステータスをrunningに更新
3. コンテンツタイプに応じた処理実行
   - blob: デフォルトブランチのHEADコミットからファイルを取得・埋め込み生成
   - pull_request: Pull Requestのタイトル・本文・コメント・差分を取得・埋め込み生成
   - issue: Issueのタイトル・本文・コメントを取得・埋め込み生成
4. 処理結果に応じたステータス・メタデータ更新

**前後の処理との関連**：GitHubリポジトリ連携設定画面でリポジトリが登録され、コンテンツタイプが有効化されていることが前提。本バッチの完了後、RAGクエリ処理でGitHubコンテンツのベクトル検索が可能になる。

**影響範囲**：
- `github_repository_content_status`テーブル（ステータス・メタデータ更新）
- `github_repository_embeddings`テーブル（blob埋め込み登録）
- `github_repository_pull_request_embeddings`テーブル（PR埋め込み登録）
- `github_repository_issue_embeddings`テーブル（Issue埋め込み登録）
- GitHub API（コンテンツ取得）
- 外部埋め込みAPI（OpenAI等）

## バッチ種別

データ連携 / ベクトル埋め込み生成

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 10分ごと |
| 実行時刻 | 毎時0, 10, 20, 30, 40, 50分 |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | Vercel Cron（`*/10 * * * *`） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| CRON_SECRET環境変数 | 認証用シークレットが設定されていること |
| GitHub App連携 | GitHubアプリがインストールされ、リポジトリへのアクセス権があること |
| 対象リポジトリ存在 | インジェスト対象のリポジトリ・コンテンツタイプが1件以上存在すること |

### 実行可否判定

認証ヘッダーに含まれるBearerトークンが`CRON_SECRET`と一致する場合のみ実行する。SHA-256ハッシュによるタイミングセーフ比較を行い、タイミング攻撃を防止する。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| Authorization | string | Yes | - | Bearerトークン形式の認証ヘッダー |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| github_repository_index | DB | リポジトリインデックス情報 |
| github_repository_content_status | DB | コンテンツタイプ別ステータス |
| GitHub API | REST/GraphQL | リポジトリコンテンツ取得 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| github_repository_content_status | DB | ステータス・メタデータ更新 |
| github_repository_embeddings | DB | blobファイルの埋め込みベクトル |
| github_repository_pull_request_embeddings | DB | Pull Requestの埋め込みベクトル |
| github_repository_issue_embeddings | DB | Issueの埋め込みベクトル |

### 出力ファイル仕様

ファイル出力はない（データベースへの登録のみ）。

| 項目 | 内容 |
|-----|------|
| ファイル名 | 該当なし |
| 出力先 | 該当なし |
| 文字コード | 該当なし |
| 区切り文字 | 該当なし |

## 処理フロー

### 処理シーケンス

```
1. 認証チェック
   └─ Authorizationヘッダーのトークンを検証

2. インジェスト対象取得
   └─ 以下の条件に該当するリポジトリ・コンテンツタイプを抽出
      - idle状態
      - failed状態かつretryAfterが現在時刻より前
      - running状態かつupdatedAtが15分以上前
      - completed状態かつupdatedAtが24時間以上前
   └─ リポジトリ単位でグルーピング

3. リポジトリごとの処理（並列実行）
   └─ 3.1 チーム情報の取得
   └─ 3.2 コンテンツタイプごとの処理（順次実行）
        └─ 3.2.1 ステータスをrunningに更新
        └─ 3.2.2 コンテンツプロセッサの実行
             - blob: デフォルトブランチHEADからファイル取得・埋め込み生成
             - pull_request: PR一覧取得・埋め込み生成
             - issue: Issue一覧取得・埋め込み生成
        └─ 3.2.3 成功時: ステータスをcompletedに更新、メタデータ保存
        └─ 3.2.4 失敗時: ステータスをfailedに更新、retryAfter設定

4. レスポンス返却
   └─ "ok"を返却
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D[インジェスト対象取得]
    D --> E{対象あり?}
    E -->|なし| F["ok"返却]
    E -->|あり| G[リポジトリ並列処理開始]
    G --> H[チーム情報取得]
    H --> I[コンテンツタイプループ]
    I --> J{有効?}
    J -->|いいえ| K[スキップ]
    J -->|はい| L[running更新]
    L --> M{コンテンツタイプ}
    M -->|blob| N[blobインジェスト]
    M -->|pull_request| O[PRインジェスト]
    M -->|issue| P[Issueインジェスト]
    N --> Q{成功?}
    O --> Q
    P --> Q
    Q -->|はい| R[completed更新]
    Q -->|いいえ| S[failed更新]
    R --> T{次のコンテンツ?}
    S --> T
    K --> T
    T -->|あり| I
    T -->|なし| U{次のリポジトリ?}
    U -->|あり| G
    U -->|なし| F
    F --> V[バッチ終了]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 対象取得 | github_repository_index | SELECT | リポジトリ情報取得 |
| 対象取得 | github_repository_content_status | SELECT | コンテンツステータス取得 |
| ステータス更新 | github_repository_content_status | UPDATE | running/completed/failed更新 |
| blob埋め込み登録 | github_repository_embeddings | INSERT/UPDATE | ファイル埋め込み登録（UPSERT） |
| PR埋め込み登録 | github_repository_pull_request_embeddings | INSERT/UPDATE | PR埋め込み登録（UPSERT） |
| Issue埋め込み登録 | github_repository_issue_embeddings | INSERT/UPDATE | Issue埋め込み登録（UPSERT） |
| 最終PR番号取得 | github_repository_pull_request_embeddings | SELECT | メタデータ用 |
| 最終Issue番号取得 | github_repository_issue_embeddings | SELECT | メタデータ用 |

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

#### github_repository_content_status

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | repositoryIndexDbId, embeddingProfileId, contentType, status, enabled, updatedAt, retryAfter | enabled = true AND (status = 'idle' OR (status = 'failed' AND retryAfter < now) OR (status = 'running' AND updatedAt < 15分前) OR (status = 'completed' AND updatedAt < 24時間前)) | 対象抽出 |
| UPDATE | status | 'running' | 処理開始時 |
| UPDATE | status | 'completed' | 成功時 |
| UPDATE | metadata | JSON（lastIngestedCommitSha等） | 成功時 |
| UPDATE | lastSyncedAt | 現在日時 | 成功時 |
| UPDATE | errorCode, retryAfter | null | 成功時にクリア |
| UPDATE | status | 'failed' | 失敗時 |
| UPDATE | errorCode | エラーコード文字列 | 失敗時 |
| UPDATE | retryAfter | リトライ日時 | 失敗時（エラー種別に応じて設定） |

#### github_repository_embeddings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | repositoryIndexDbId | リポジトリのdbId | UPSERT |
| INSERT/UPDATE | embeddingProfileId | プロファイルID | UPSERT |
| INSERT/UPDATE | embeddingDimensions | 1536 / 3072 | UPSERT |
| INSERT/UPDATE | fileSha | ファイルのSHA | UPSERT |
| INSERT/UPDATE | path | ファイルパス | UPSERT |
| INSERT/UPDATE | embedding | ベクトル配列 | UPSERT |
| INSERT/UPDATE | chunkContent | チャンクテキスト | UPSERT |
| INSERT/UPDATE | chunkIndex | チャンク番号 | UPSERT |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DOCUMENT_NOT_FOUND | 業務エラー | 対象ドキュメントが存在しない | リトライなしでfailed |
| DOCUMENT_TOO_LARGE | 業務エラー | ファイルサイズ超過 | リトライなしでfailed |
| DOCUMENT_RATE_LIMITED | 一時エラー | GitHub APIレート制限 | retryAfterをセットしてfailed |
| DOCUMENT_FETCH_ERROR | 一時エラー | GitHub API通信エラー | 即時リトライ可能としてfailed |
| UNKNOWN | システムエラー | 予期しないエラー | 即時リトライ可能としてfailed |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無制限（retryAfter経過後） |
| リトライ間隔 | エラー種別に応じて可変（即時〜GitHub指定のretry-after） |
| リトライ対象エラー | DOCUMENT_RATE_LIMITED, DOCUMENT_FETCH_ERROR, UNKNOWN |

### 障害時対応

- `DOCUMENT_NOT_FOUND`、`DOCUMENT_TOO_LARGE`はリトライ対象外（永続的エラー）
- `DOCUMENT_RATE_LIMITED`はGitHub APIのretry-afterヘッダー値を使用
- その他のエラーは即時リトライ可能（次回cron実行時に処理）
- Sentryにエラー情報を送信し、監視・アラートで検知

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | コンテンツタイプ単位 |
| コミットタイミング | 各コンテンツタイプの処理完了時 |
| ロールバック条件 | 明示的なトランザクションロールバックなし（埋め込みはUPSERT） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 複数リポジトリを並列処理 |
| 目標処理時間 | 800秒以内（maxDuration設定） |
| メモリ使用量上限 | Vercel Serverless関数の制限内（1GB程度） |

## 排他制御

- ステータスベースの排他制御（running状態のコンテンツは処理対象外）
- 15分以上経過したrunning状態は「スティール」により別インスタンスが処理可能
- リポジトリレベルでは並列処理を許可（複数リポジトリを同時処理）

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | バッチ開始時 | 暗黙的（Vercel Cronログ） |
| 進捗ログ | 処理中 | 暗黙的 |
| 終了ログ | バッチ終了時 | 暗黙的（HTTPレスポンス "ok"） |
| エラーログ | エラー発生時 | 「Failed to ingest {contentType} for GitHub Repository: teamDbId={teamDbId}, repository={owner}/{repo}, embeddingProfileId={embeddingProfileId}」 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 800秒（タイムアウト） | Vercel Functions監視 |
| エラー件数 | 異常な連続失敗 | Sentry |

## 備考

- リポジトリは並列処理される（`Promise.all`）が、各リポジトリ内のコンテンツタイプは順次処理される
- completed状態のコンテンツは24時間後に再同期され、リポジトリの変更が反映される
- コンテンツタイプ（blob, pull_request, issue）ごとに別々のプロセッサが実装されている
- メタデータにはlastIngestedCommitSha（blob）、lastIngestedPrNumber（PR）、lastIngestedIssueNumber（Issue）が保存される
- 部分的な埋め込み失敗はAggregateErrorとしてSentryに報告されるが、成功した埋め込みは保持される
