# 検証プロファイル (Verify Profile)

TIAS Shot（WinForms / .NET Framework 4.8 / x64 の舌診撮影ソフト）の検証プロファイル．verifier は本ファイルだけを頼りに検証する．

経緯: 2026-09-18 時点では「verifier はハードウェア結合層（カメラ・UI・スレッド）を担保できない」としてプロファイルを作らなかった．その後，ヘッドレスのセルフテストモード（`TIASshot.exe --selftest`，`SELFTEST` ビルドフラグ）で起動時の依存解決・プレビュー経路・校正〜保存の一部を実機なしで自動確認できるようになったため，本プロファイルを作成した．

## 種別・検証手段 (Target & Method)

- デスクトップアプリ（WinForms）．**画面操作・カメラ操作はしない**．
- ランナー `scripts/verify/run_selftest.py` が，クリーンビルド（`Debug|x64`）→ 既存テスト（NUnit）→ `TIASshot.exe --selftest` を実行し，終了コードと結果ファイルで判定する．
- セルフテストの 3 項目（実行ファイルに組み込み済み．verifier がシナリオを書く必要はない）:

| ID | 確認内容 | 実コードを通す範囲 | 代替している部分 |
| --- | --- | --- | --- |
| `deps` | 起動時にアセンブリ解決エラー（FileLoadException / FileNotFoundException）が出ない | `TIASshot.exe.config` のバインディングリダイレクトと bin の DLL バージョンの一致，本体参照の推移的ロード，bin 内の全管理 DLL のロード，OpenCvSharp（ネイティブ DLL・Mat 演算・`Mat.AsSpan`（System.Memory）・ArUco），`BitmapConverter` と `BitmapUtil.FastClone` の往復，`config.xml`・TCC CSV の読み込み，**`Form1` の実コンストラクタ**（`InitializeComponent`） | `Form1_Load`（カメラ・光源への接続）は実行しない |
| `preview` | プレビュー経路（カメラコールバック → UI 所有コピー（FastClone）→ 表示用 Bitmap）が動く | `CameraBase`（コンストラクタ・`BootCheck`・`ProcessCalibrationFrame`），`Form1.ShowImage`・`MainRefreshTimer_Tick`（実タイマー），`PreviewMonitor`（実コンストラクタ・`UpdateImage`・`RefreshTimer_Tick`）．入力 Bitmap 解放後も表示コピーが有効であること（所有権モデル）を確認 | カメラは偽カメラ（`SelfTest/SelfTestCamera`．Lucam のコールバック手順を再現）．フォームは非表示・`InitializeComponent` を呼ばないヘッドレス版コンストラクタ．副ディスプレイの存在は模擬 |
| `calib-save` | 校正〜保存が一通り動く | `ChartDetector`（ArUco 検出・固定判定・ホモグラフィ・マスク），`CameraBase.CalcTcc`・`ColorCorrector`（変換行列算出・CSV 保存），`CameraBase.ShotOne` → `RunShotLoop` → `SaveThread` → `SaveImages`（sRGB 変換画像の保存）．保存物の読み戻し・行列の有限性・変換後の色票が参照値に戻ることを確認 | チャートは合成画像（ArUco と色票をコードで描画）．ゲイン反映は偽カメラが画像へ乗算して模擬．保存先は一時フォルダ |

## 検証対象の環境 (Target Environments)

- ローカルの接続先: このマシンのみ（ビルド・実行ファイル・一時フォルダ）．ネットワーク上のサービスは使わない（`--full` 時の `nuget restore` による nuget.org からのパッケージ取得のみ例外）
- ローカル以外で許可する環境: なし
- 接続を禁止する環境（拒否リスト）: 実機カメラ（Lumenera / ImagingSource）・光源（シリアルポート）・本番の撮影データフォルダ（`config.xml` の `File/SaveFolder`，既定 `C:\TIAS_Data`）．セルフテストはこれらに触れない設計であり，verifier も触れない

## 実行コマンド (Run Command)

リポジトリルートで実行する．

| 用途 | コマンド |
| --- | --- |
| 通常（コード変更の確認） | `python scripts/verify/run_selftest.py` |
| 依存更新（NuGet パッケージ・バインディングリダイレクトの変更）の確認 | `python scripts/verify/run_selftest.py --full` |

- ランナーの `--no-clean`（Debug 出力を消さない）・`--skip-tests`（既存テストを省略）は開発時の短縮用であり，**判定には使わない**（verifier は上表のコマンドだけを使う）
- `--full` は `packages` を退避して `nuget restore` からやり直す（`git clone` 直後と同じ状態．ENV_04「クリーン環境での検証」）．復元に失敗した場合は退避した `packages` を自動で戻す．`packages` の退避（改名）自体に失敗した場合（Visual Studio が使用中等）は `packages` を変更せずに終了コード 2 で止まる
- 出力先: `.verify/selftest/run-<日時>/`（`.gitignore` 済み．実行ごとに別ディレクトリ）
- 出力されるもの: `summary.json`（各ステップの成否）・`build.log`・`test.log`・`restore.log`（`--full` 時）・`selftest-result.txt`（セルフテストの詳細．UTF-8 BOM 付き）
- ランナーの終了コード: `0`=全て合格，`1`=不合格あり（ビルド失敗・テスト失敗・セルフテスト失敗），`2`=前提条件を満たさない（→「検証不能」）
- セルフテスト単体の終了コード（`summary.json` の `selftest_exit_code`）: `0`=全項目合格，`1`=不合格あり，`2`=セルフテスト自体の異常，`3`=タイムアウト（5 分．ハングの疑い）

### 判定の読み方

- `selftest-result.txt` は項目ごとに `[PASS] <ID>: ...` / `[FAIL] <ID>: ...` の見出し行と，その下の詳細行（`  - ` 情報，`  WARN:` 参考，`  FAIL:` 失敗理由）で構成され，最終行が `RESULT: PASS (3/3)` または `RESULT: FAIL (...)` になる
- `FAIL:` 行には例外型・メッセージ・対象アセンブリ名（`[assembly: ...]`）・Fusion ログの要点（`[fusion: ...]`）・スタックの先頭が入る．依存の問題はここで特定できる
- 判定は**終了コードと結果ファイル**で行う．`TIASshot.exe` の標準出力は OS のコードページ（cp932）で出るため，直接実行すると文字化けすることがある（ランナーは標準出力を使わない）

## 前提条件と確認方法 (Preconditions)

| 前提条件 | 確認方法 | 満たされない場合 |
| --- | --- | --- |
| Visual Studio 2022 または Build Tools（MSBuild・vstest）が導入済み | ランナーが `vswhere` で自動検出する（見つからなければ終了コード 2） | 検証不能とし，`docs/02_ENV/ENV_02_環境構築手順.md` に従った導入を人間に案内する |
| NuGet パッケージが復元済み（`packages/`） | `ls packages` | `python scripts/verify/run_selftest.py --full` を実行してよい（`nuget.exe` が無ければ検証不能とし `setup.ps1` の実行を人間に案内する） |
| IC Imaging Control 3.5 .NET Library が導入済み（ビルドに必要．実機カメラは不要） | ビルドの成否（`build.log` に `TIS.Imaging` の参照エラーが無いこと） | 検証不能とし，ENV_02 の SDK 導入手順を人間に案内する |
| `TIASshot.exe` やテストが実行中でない（Debug 出力を削除できる） | ランナーがクリーンするのは Debug 出力（`TIASshot/` と `TIASshot.Tests/` の `bin/x64/Debug`・`obj/x64/Debug`）のみで，`bin/x64/Release` 等ほかの構成の出力には触れない．削除に失敗すると終了コード 2 で止まる | 検証不能とし，人間にアプリ・Visual Studio のデバッグ実行の終了を依頼する（**自分でプロセスを止めない**） |

## 認証方法・ロール (Auth & Roles)

- 認証なし

## テストデータの規約 (Test Data Rules)

- セルフテストが書き込むのは次の 2 か所だけ:
  - `%TEMP%\TIASshot-selftest\work-<日時>-<PID>\`（一時設定ファイル・校正データ・撮影画像）．**合格時は自動削除**，不合格時は証拠として残し，結果ファイルの末尾に `work dir kept: <パス>` と出る
  - `.verify/selftest/run-<日時>/`（ランナーのログと結果）
- 同梱の `config.xml` は変更しない（セルフテストは複製して保存先・参照パス・効果音を差し替えた一時設定を使う）．本番の保存先（`C:\TIAS_Data` 等）には書き込まない
- 不合格時に残った `work-*` フォルダの有無とパスを，報告の「後始末」に書く（削除は人間の判断に任せてよい）

## 待ち方・操作の癖 (Waiting & Selectors)

- ランナーは同期実行で，完了まで待つだけでよい（通常 10〜30 秒．`--full` はパッケージ取得で数分）
- セルフテストはウィンドウを表示しない．画面の確認手段は無く，スクリーンショットも出ない
- `Release` 構成にはセルフテストが含まれない（`SELFTEST` フラグは Debug 系のみ）．Release の exe に `--selftest` を渡すと通常起動（UI 表示・カメラ接続）になるため，実行しない

## 既知のノイズ (Known Noise)

- `deps` の `リソース系の AssemblyResolve n 件（サテライトリソースの探索．判定対象外）`: 日本語カルチャのサテライトリソース（`TIASshot.resources`）を探して見つからないのは通常動作
- `deps` の `redirect System.ValueTuple → 4.0.3.0: bin に DLL なし`: .NET Framework 4.7.1 以降は mscorlib に統合されており bin に置かれないのが正常
- カメラ SDK（`TIS.*`・`lumenera.*`）のロード失敗は `WARN:`（判定対象外）．SDK 未導入の環境では出うる
- `preview` / `calib-save` の所要時間は環境で変わる（判定条件ではない）

## 禁止操作 (Prohibited Operations)

- `--selftest` を付けない `TIASshot.exe` の起動（UI が開き，実機カメラ・光源（シリアルポート）へ接続を試みる）
- `config.xml`（リポジトリ内・bin 内とも）や本番の保存先フォルダ（`C:\TIAS_Data` 等）の編集・削除
- 自分が起動していないプロセスの停止（人間が起動した TIASshot・Visual Studio のデバッグ実行を止めない）
- `packages.bak-*` 以外のリポジトリ内ディレクトリの手動削除・改名（`packages` の退避と復元はランナーの `--full` だけが行う）
- ランナーを通さない独自のビルド・実行手順の組み立て（コマンドは本ファイルに書かれたものだけを使う）
- 失敗注入（bin 内 DLL の一時除去・`TIASshot.exe.config` の改ざん等）や `TIASshot.exe --selftest` の直接実行．セルフテストが依存の問題を検出できるかの確認が必要な場合は，人間が `docs/02_ENV/ENV_04_開発コマンド.md` の手順で行う（verifier には許可しない）

## シナリオのテンプレート (Scenario Template)

本プロジェクトでは **verifier がシナリオを書く必要はない**（確認項目は `TIASshot.exe --selftest` に組み込み済みで，引数で項目を選ぶ仕組みも無い）．verifier の作業は次のとおり．

```text
1. 変更内容に応じてコマンドを選ぶ
   - NuGet パッケージ・App.config（バインディングリダイレクト）・csproj の参照を変えた → --full
   - それ以外のコード変更 → 通常
2. python scripts/verify/run_selftest.py [--full] を実行する
3. 出力ディレクトリの summary.json と selftest-result.txt を Read で開き，
   - 各ステップ（clean / restore / build / tests / selftest）の成否
   - 各項目（deps / preview / calib-save）の [PASS]/[FAIL] と，FAIL: 行・WARN: 行の内容
   を確認する．PASS でも，今回の変更に関係する項目の詳細行（ロードされた DLL のバージョン等）を必ず見る
4. FAIL の場合は FAIL: 行（例外型・アセンブリ名・Fusion ログ）を証拠として報告する
   （build.log / test.log の該当箇所も併記する）
```

変更がセルフテストの 3 項目で観測できない領域（下記「自動で確認できない範囲」）にある場合は，その部分を「未確認」として人間に回す．

## 自動で確認できない範囲（人間が確認する項目）(Out of Scope)

セルフテストが PASS でも，以下は確認していない．変更がこれらに関わる場合は「人間が確認すべき残り項目」に具体的に書く．

- **実カメラ SDK との結合**: Lucam / IScam の接続・パラメータ設定・フレームコールバック・撮影（Snap・Sink 切り替え）・ROI 切り出し・白板撮影（`WHITEBOARD`）
- **実プレビュー表示**: 実ウィンドウへの描画・表示の滑らかさ・副ディスプレイでの表示位置・F9 トグル（`MEMMONITOR`）
- **実チャートの検出性能**: 実物の舌診チャートを実照明・実レンズで撮った画像での ArUco 検出・固定判定・マスク位置（合成チャートは理想条件）
- **画像の見た目**: 校正後の sRGB 画像の色・露出・ホワイトバランスが臨床的に妥当か（セルフテストは合成チャートでの数値一致のみ）
- **長時間のメモリ / GDI**: 長時間プレビュー・連続撮影でのメモリリーク・GDI ハンドル枯渇（`docs/06_TEST/TEST_03_メモリ修正効果計測.md` の手順）
- 光源（シリアル通信）の制御，効果音の再生，エラー時のダイアログ表示（セルフテストでは記録のみに差し替えている）
- `Release` 構成の実行（セルフテストを含まないため，起動確認は人間が行う）

## 実行基盤の要件 (Runner Requirements)

- 決定的な処理（ツールの検出・bin/obj の削除・パッケージ復元・ビルド・テスト・セルフテスト実行・結果の集計）はランナー `scripts/verify/run_selftest.py` が行う．verifier は判断（結果の読み取り・報告）のみ行う
- 実行ごとに `.verify/selftest/run-<日時>/` へ出力し，前回の証拠を上書きしない
- ランナーは自分が起動したプロセス（MSBuild・vstest・TIASshot.exe）以外を停止しない．セルフテストは内部のウォッチドッグ（5 分）でハング時も終了コード 3 で終わる
- 前提を満たさない場合は破壊的な復旧を試みず，終了コード 2 と理由を出して終了する
- セルフテストの実装は `TIASshot/SelfTest/` と `TIASshot/UI/*.SelfTest.cs`（`#if SELFTEST`），ビルドフラグは `docs/02_ENV/ENV_06_ビルド構成とビルドフラグ.md` を参照する
