# 通知設計書 20-conflicts detected

## 概要

本ドキュメントは、Settings Sync で競合が検出された場合に表示される `conflicts detected` 通知の設計仕様を記述する。

### 本通知の処理概要

この通知は、Settings Sync の同期処理中にローカルとリモートの間で競合が検出された場合に警告として表示される。ユーザーに競合の解決方法を選択させる。

**業務上の目的・背景**：VS Code の Settings Sync 機能では、複数のデバイス間で設定を同期する際に、両方のデバイスで同じ設定が変更された場合に競合が発生する可能性がある。この競合を自動的に解決することは難しいため、ユーザーに競合の内容を通知し、どちらの変更を採用するか選択させる必要がある。

**通知の送信タイミング**：同期処理中に競合が検出された場合、`onDidChangeConflicts` イベントハンドラ内で通知が表示される。同じ競合に対して複数回通知が表示されないよう管理される。

**通知の受信者**：Settings Sync を使用し、競合が発生した VS Code ユーザー本人。

**通知内容の概要**：「Unable to sync due to conflicts in {同期領域}. Please resolve them to continue.」という警告メッセージと、3つのアクションボタン（Replace Remote、Replace Local、Show Conflicts）が表示される。

**期待されるアクション**：ユーザーは以下のいずれかのアクションを選択する。(1) 「Replace Remote」でリモートをローカルの内容で上書き、(2) 「Replace Local」でローカルをリモートの内容で上書き、(3) 「Show Conflicts」で競合の詳細を確認してから解決。

## 通知種別

アプリ内通知（Warning レベル）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | 無し |

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

Settings Sync が有効で、競合が発生したユーザーに対してのみ通知が表示される。

## 通知テンプレート

### アプリ内通知

| 項目 | 内容 |
|-----|------|
| 深刻度 | Warning |
| スティッキー | Yes |

### 本文テンプレート

```
Unable to sync due to conflicts in {0}. Please resolve them to continue.
```

### アクションボタン

| ボタン名 | 動作 |
|---------|------|
| Replace Remote | `acceptLocal()` を呼び出し、リモートをローカルで上書き |
| Replace Local | `acceptRemote()` を呼び出し、ローカルをリモートで上書き |
| Show Conflicts | `userDataSyncWorkbenchService.showConflicts()` で競合ビューを表示 |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| {0} | 競合が発生した同期領域名（小文字） | `getSyncAreaLabel(conflict.syncResource).toLowerCase()` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| システムイベント | onDidChangeConflicts | `conflicts.length > 0` かつ Settings Sync が有効 | 競合検出時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Settings Sync が無効 | `userDataSyncEnablementService.isEnabled()` が false |
| 同じ競合に対して既に通知済み | `conflictsDisposables.has(key)` が true |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[同期処理実行] --> B{競合検出?}
    B -->|No| C[正常完了]
    B -->|Yes| D[onDidChangeConflicts 発火]
    D --> E{Settings Sync 有効?}
    E -->|No| F[早期リターン]
    E -->|Yes| G{既に通知済み?}
    G -->|Yes| H[スキップ]
    G -->|No| I[conflicts detected 通知]
    I --> J{ユーザーアクション}
    J -->|Replace Remote| K[acceptLocal 実行]
    J -->|Replace Local| L[acceptRemote 実行]
    J -->|Show Conflicts| M[競合ビュー表示]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | ローカルデータベースは使用しない | - |

### 外部サービス参照

| サービス | 用途 | 備考 |
|---------|------|------|
| User Data Sync Service | 競合の検出と解決 | `userDataSyncService.accept()` |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 解決失敗 | accept() が例外をスロー | `accept failed` エラー通知を表示 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限あり | 同じ競合に対しては1回のみ通知 |

### 配信時間帯

制限なし

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

- 同期領域名のみが通知に含まれ、設定内容は含まれない
- 競合の詳細は「Show Conflicts」で別途確認

## 備考

- 通知は sticky（自動で消えない）
- 競合が解決されると、通知は自動的に閉じられる（`conflictsDisposables` の dispose 時）
- 同期領域の例: settings, keybindings, extensions, globalState, snippets, tasks, profiles

---

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

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

### 推奨読解順序

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

競合データの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | userDataSync.ts | `src/vs/platform/userDataSync/common/userDataSync.ts` | `IUserDataSyncResourceConflicts` インターフェース |
| 1-2 | userDataSync.ts | `src/vs/workbench/services/userDataSync/common/userDataSync.ts` | `getSyncAreaLabel()` 関数 |

**読解のコツ**: `IUserDataSyncResourceConflicts` は `syncResource`（同期対象の種類）と `conflicts`（競合のリスト）を含む。

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

通知が発行される処理を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | userDataSync.ts | `src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts` | `onDidChangeConflicts` メソッド |

**主要処理フロー**:
1. **行167-172**: 前提条件チェック（Settings Sync 有効化状態）
2. **行173-180**: 既存の競合通知のクリーンアップ
3. **行182-218**: 新しい競合に対する通知発行
4. **行186-187**: `notificationService.prompt()` で通知発行
5. **行188-207**: アクションボタンの定義
6. **行213-217**: 通知のクローズ時の Disposable 登録

#### Step 3: 競合解決処理を理解する

競合解決のアクションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | userDataSync.ts | `src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts` | `acceptLocal`、`acceptRemote` メソッド |

**主要処理フロー**:
- **acceptLocal**: ローカルの内容をリモートに適用
- **acceptRemote**: リモートの内容をローカルに適用
- エラー時は `accept failed` 通知を表示

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

```
同期処理中に競合発生
    │
    ├─ UserDataSyncService が競合を検出
    │      │
    │      └─ onDidChangeConflicts イベント発火
    │
    └─ UserDataSyncContribution.onDidChangeConflicts()
           │
           ├─ 既存の競合通知のクリーンアップ
           │
           └─ 新規競合の処理
                  │
                  ├─ 重複チェック (conflictsDisposables)
                  │
                  └─ notificationService.prompt()
                         │
                         ├─ "conflicts detected"
                         │      (Warning, sticky)
                         │
                         └─ アクションボタン
                                ├─ "Replace Remote" → acceptLocal()
                                ├─ "Replace Local" → acceptRemote()
                                └─ "Show Conflicts" → showConflicts()
```

### データフロー図

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

競合データ           ───▶ onDidChangeConflicts     ───▶ 通知表示
IUserDataSyncResource      │
Conflicts[]                │
    │                      ↓
    ├─ syncResource   getSyncAreaLabel()
    │                      │
    │                      ↓
    └─ conflicts[]    conflictsArea.toLowerCase()
                           │
                           ↓
                      notificationService.prompt()
                           │
                           ↓
                      Notification (Warning)
                      "Unable to sync due to
                       conflicts in {area}..."

[ユーザーアクション]

Replace Remote  ───▶ acceptLocal()       ───▶ 競合解決
                     userDataSyncService        (ローカル優先)
                     .accept(localResource)

Replace Local   ───▶ acceptRemote()      ───▶ 競合解決
                     userDataSyncService        (リモート優先)
                     .accept(remoteResource)

Show Conflicts  ───▶ showConflicts()     ───▶ 競合ビュー表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| userDataSync.ts | `src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts` | ソース | 通知発行元、UI コントリビューション |
| userDataSync.ts | `src/vs/workbench/services/userDataSync/common/userDataSync.ts` | ソース | getSyncAreaLabel 定義 |
| userDataSyncWorkbenchService.ts | `src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts` | ソース | showConflicts 実装 |
| userDataSync.ts | `src/vs/platform/userDataSync/common/userDataSync.ts` | ソース | 競合データ構造定義 |
| notification.ts | `src/vs/platform/notification/common/notification.ts` | ソース | 通知サービスインターフェース |
