# 帳票設計書 7-アプリケーション状態JSON出力

## 概要

本ドキュメントは、Windows Terminal のアプリケーション状態情報（最近使用したコマンド、生成済みプロファイル、ウィンドウレイアウト等）をJSONファイルに永続化する処理の設計を記述する。

### 本帳票の処理概要

ApplicationState オブジェクトが保持するアプリケーションの実行状態情報を、JSON形式にシリアライズしてstate.json（共有状態）およびelevated-state.json（昇格時のローカル状態）に永続化する。ユーザー編集を意図しない内部状態データの保存を行う。

**業務上の目的・背景**：アプリケーションの実行状態を永続化することで、再起動後も最近使用したコマンドの履歴、生成済みプロファイルの管理情報、ウィンドウレイアウトの復元情報などを維持する。settings.jsonがユーザー編集可能な設定であるのに対し、state.jsonはアプリケーション内部で自動管理される状態データを保持する。

**帳票の利用シーン**：アプリケーションの状態が変更された際（コマンド実行、ウィンドウ位置変更、プロファイル生成等）に、スロットリング（1秒間のデバウンス）を経てバックグラウンドで自動保存される。アプリケーション終了時には強制フラッシュされる。

**主要な出力内容**：
1. Shared状態（state.json）：settingsHash, generatedProfiles, recentCommands, dismissedMessages, sshFolderGenerated
2. Local状態（state.json または elevated-state.json）：persistedWindowLayouts, allowedCommandlines, dismissedBadges

**帳票の出力タイミング**：状態変更後のスロットリングタイマー満了時（1秒デバウンス）、およびアプリケーション終了時のFlush()呼び出し時。

**帳票の利用者**：Windows Terminal アプリケーション自身（内部管理用。ユーザーが直接編集することは想定しない）。

## 帳票種別

状態シリアライズ（JSONファイル出力）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | なし（自動） | - | 状態変更時に自動保存（スロットリング付き） |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | state.json（共有状態）/ elevated-state.json（昇格時ローカル状態） |
| 出力方法 | ファイル書き込み（アトミック操作 / 昇格時は直接書き込み） |
| 文字コード | UTF-8 |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

JSONオブジェクトとして、各状態フィールドがキー・バリューペアで格納される。Shared と Local の2種類のソースに分かれる。

```
┌─────────────────────────────────────┐
│ state.json:                         │
│ {                                   │
│   "settingsHash": "...",            │
│   "generatedProfiles": [...],       │
│   "recentCommands": [...],          │
│   "dismissedMessages": [...],       │
│   "sshFolderGenerated": false,      │
│   "persistedWindowLayouts": [...],  │  ← 非昇格時のみ
│   "allowedCommandlines": [...],     │  ← 非昇格時のみ
│   "dismissedBadges": [...]          │  ← 非昇格時のみ
│ }                                   │
├─────────────────────────────────────┤
│ elevated-state.json (昇格時のみ):    │
│ {                                   │
│   "persistedWindowLayouts": [...],  │
│   "allowedCommandlines": [...],     │
│   "dismissedBadges": [...]          │
│ }                                   │
└─────────────────────────────────────┘
```

### ヘッダー部

該当なし

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|------|
| 1 | settingsHash | 設定ファイルのハッシュ値 | Shared | JSON文字列 | 可変 |
| 2 | generatedProfiles | 自動生成されたプロファイルのGUID一覧 | Shared | JSON配列（GUID文字列） | 可変 |
| 3 | recentCommands | 最近使用したコマンド一覧 | Shared | JSON配列（文字列） | 可変 |
| 4 | dismissedMessages | 非表示にしたメッセージ一覧 | Shared | JSON配列 | 可変 |
| 5 | sshFolderGenerated | SSHフォルダ生成済みフラグ | Shared | JSONブール値 | 可変 |
| 6 | persistedWindowLayouts | 永続化されたウィンドウレイアウト | Local | JSON配列 | 可変 |
| 7 | allowedCommandlines | 許可されたコマンドライン一覧 | Local | JSON配列（文字列） | 可変 |
| 8 | dismissedBadges | 非表示にしたバッジ一覧 | Local | JSON配列（文字列） | 可変 |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| FileSource::Shared | Shared プロパティを出力対象に含める | 状況依存 |
| FileSource::Local | Local プロパティを出力対象に含める | 状況依存 |
| 昇格状態 | IsRunningElevated() による分岐 | 自動判定 |

### ソート順

該当なし（各フィールドはマクロ定義順に出力）

### 改ページ条件

該当なし

## データベース参照仕様

### 参照テーブル一覧

該当なし（メモリ上の ApplicationState オブジェクトからデータを取得する）

### テーブル別参照項目詳細

#### ApplicationState（メモリ上状態オブジェクト）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| state->SettingsHash | settingsHash | Shared | std::optional\<winrt::hstring\> |
| state->GeneratedProfiles | generatedProfiles | Shared | std::optional\<unordered_set\<guid\>\> |
| state->PersistedWindowLayouts | persistedWindowLayouts | Local | std::optional\<IVector\<WindowLayout\>\> |
| state->RecentCommands | recentCommands | Shared | std::optional\<IVector\<hstring\>\> |
| state->DismissedMessages | dismissedMessages | Shared | std::optional\<IVector\<InfoBarMessage\>\> |
| state->AllowedCommandlines | allowedCommandlines | Local | std::optional\<IVector\<hstring\>\> |
| state->DismissedBadges | dismissedBadges | Local | std::optional\<unordered_set\<hstring\>\> |
| state->SSHFolderGenerated | sshFolderGenerated | Shared | std::optional\<bool\>（デフォルト: false） |

## 計算仕様

### 計算項目一覧

該当なし（値はそのままJSONにシリアライズされる）

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[状態変更 setter呼び出し] --> B[_throttler 起動 1秒デバウンス]
    B --> C[_write 呼び出し]
    C --> D{昇格実行中?}
    D -->|Yes| E[state.json読み込み]
    E --> F[Shared プロパティを既存JSONに上書き]
    F --> G[state.json に書き込み]
    G --> H[Local プロパティをelevated-state.jsonに書き込み]
    D -->|No| I[全プロパティをstate.jsonに書き込み]
    H --> J[終了]
    I --> J
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| JSON書き込みエラー | ファイルシステムエラー | なし | CATCH_LOG() でログ記録 |
| JSON読み込みエラー | state.json が破損 | なし | CATCH_LOG() で無視、空状態として継続 |
| elevated-state.json 権限エラー | 昇格時のセキュリティ検証失敗 | なし | ファイル削除して再作成 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 各フィールド数十〜数百件程度 |
| 目標出力時間 | 即座（非同期、スロットリング付き） |
| 同時出力数上限 | 1（スロットリングにより制御） |

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

- 昇格/非昇格の状態を分離管理し、昇格時のLocal状態はelevated-state.jsonに保存される
- elevated-state.jsonの読み込み時にはパーミッション検証が行われ、不正なパーミッションのファイルは削除される
- 昇格時のelevated-state.json書き込みはアトミック操作を使用しない（一時ファイル→リネームだと非昇格プロセスが書き込み可能になるため）
- state.jsonは非昇格時にはアトミック操作で書き込まれる

## 備考

- MTSM_APPLICATION_STATE_FIELDS マクロにより、全フィールドのgetter/setter/FromJson/ToJsonが自動生成される
- 各フィールドは std::optional\<T\> で管理され、未設定のフィールドはJSONから省略可能
- スロットリングは til::throttled_func を使用し、1秒間のデバウンス（trailing=true）で実装される
- デストラクタでFlush()が呼ばれ、最後の書き込みが完了してから終了する
- SharedInstance() により、アプリケーション全体で単一のインスタンスが共有される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ApplicationState.h | `src/cascadia/TerminalSettingsModel/ApplicationState.h` | MTSM_APPLICATION_STATE_FIELDS マクロ（37-45行目）で全フィールドとそのソース（Shared/Local）を理解する |
| 1-2 | ApplicationState.h | `src/cascadia/TerminalSettingsModel/ApplicationState.h` | FileSource enum（27-31行目）のShared/Localの意味、state_t構造体（86-91行目）の各フィールド |

**読解のコツ**: MTSM_APPLICATION_STATE_FIELDS マクロは X(source, type, name, key, ...) の形式で、sourceがFileSource::SharedかLocalか、typeがデータ型、nameがフィールド名、keyがJSONキーを表す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ApplicationState.cpp | `src/cascadia/TerminalSettingsModel/ApplicationState.cpp` | 344-361行目のマクロ展開によるgetter/setter生成。setterでは _throttler() を呼び出してスロットリング書き込みをトリガー |

**主要処理フロー**:
1. **setter呼び出し**: state->name.emplace(value) で値を設定
2. **_throttler()**: 1秒のデバウンスタイマーを開始
3. **タイマー満了**: _write() が呼ばれる

#### Step 3: _write の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ApplicationState.cpp | `src/cascadia/TerminalSettingsModel/ApplicationState.cpp` | _write メソッド（198-247行目）。昇格/非昇格での分岐処理 |

**主要処理フロー**:
- **217行目**: 昇格チェック（IsRunningElevated()）
- **226-236行目**: 昇格時 - state.json読み込み → Shared上書き → 書き込み
- **239行目**: 昇格時 - elevated-state.json に Local プロパティを書き込み
- **244行目**: 非昇格時 - state.json に全プロパティ（Shared+Local）を書き込み

#### Step 4: ToJson の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ApplicationState.cpp | `src/cascadia/TerminalSettingsModel/ApplicationState.cpp` | ToJson メソッド（281-285行目）と _toJsonWithBlob（287-303行目）。MTSM_APPLICATION_STATE_FIELDS マクロで各フィールドをJSONに設定 |

**主要処理フロー**:
- **283行目**: 空のJSONオブジェクト作成
- **284行目**: _toJsonWithBlob で既存JSONに状態を上書き
- **295-297行目**: マクロ展開で各フィールドを parseSource に基づいてフィルタリングしながら設定

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

```
状態変更（setter）
    |
    +-- _throttler() (1秒デバウンス)
            |
            +-- ApplicationState::_write() (ApplicationState.cpp:198)
                    |
                    +-- [昇格時]
                    |       +-- _readSharedContents() (state.json読み込み)
                    |       +-- _toJsonWithBlob(root, FileSource::Shared) (Shared上書き)
                    |       +-- _writeSharedContents() (state.json書き込み)
                    |       +-- ToJson(FileSource::Local) (Local状態取得)
                    |       +-- _writeLocalContents() (elevated-state.json書き込み)
                    |
                    +-- [非昇格時]
                            +-- ToJson(FileSource::Local | FileSource::Shared) (全状態取得)
                            +-- _writeLocalContents() (state.json書き込み)

ApplicationState::~ApplicationState()
    |
    +-- Flush()
            +-- _throttler.flush() (強制実行)
```

### データフロー図

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

ApplicationState (メモリ)       ---> _write()              ---> state.json
  |                                    |                          (Shared + Local)
  +-- state_t (ロック保護)            +-- ToJson()
  |    +-- SettingsHash              +-- JsonUtils::SetValueForKey
  |    +-- GeneratedProfiles         +-- _toJsonWithBlob()
  |    +-- PersistedWindowLayouts                              [昇格時追加出力]
  |    +-- RecentCommands                                      elevated-state.json
  |    +-- DismissedMessages                                    (Local のみ)
  |    +-- AllowedCommandlines
  |    +-- DismissedBadges
  |    +-- SSHFolderGenerated
  +-- _throttler (デバウンス制御)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ApplicationState.h | `src/cascadia/TerminalSettingsModel/ApplicationState.h` | ヘッダー | ApplicationState クラス定義、MTSM_APPLICATION_STATE_FIELDS マクロ |
| ApplicationState.cpp | `src/cascadia/TerminalSettingsModel/ApplicationState.cpp` | ソース | _write, ToJson, _read, getter/setter の実装 |
| ApplicationState.idl | `src/cascadia/TerminalSettingsModel/ApplicationState.idl` | IDL | WinRT インターフェース定義 |
| JsonUtils.h | `src/cascadia/TerminalSettingsModel/JsonUtils.h` | ヘッダー | GetValueForKey, SetValueForKey テンプレート関数 |
| FileUtils.h | `src/cascadia/TerminalSettingsModel/FileUtils.h` | ヘッダー | GetBaseSettingsPath 関数 |
