# 開発進捗ログ

<!--
本ファイルは追記型のフル進捗ログ．

- 最新エントリは下に追記する（時系列順．古いものが上，新しいものが下）
- CLAUDE.md の「開発進捗」セクションは最新 1 行のみ（上書き運用）
- 詳細・失敗パターン・設計判断はすべて本ファイルに残す
- 書式・運用ルールは .claude/rules/progress-log.md を参照
  （本ファイルの編集時に自動ロードされる）
-->

- [x] **/setup フェーズ 1〜2 完了（要件定義・技術選定）**: 旧実装 `org/src`（C++ / MFC / OpenCV 2.4）を解析し，同一機能の再構築を目的として PLAN_01・ENV_01 を作成．開発モードは solo．
  - 設計判断: 技術スタックは Python + PySide6 + opencv-python を採用．MFC 継続・C# 案は Mac 対応の可能性が無いため却下（ユーザー要件: 当面 Windows，将来 Mac も視野）．SVM は現行と同じ OpenCV 実装（`cv2.ml.SVM`）を使い診断結果の同一性を担保する方針
  - 旧実装は `org/` に置き参照専用とする（.gitignore で Git 管理外）
- [x] **/setup フェーズ 3 完了（環境構築）**: ENV_02（メンバー用）・ENV_03（管理者用）を作成．「どのデバイスでも clone → uv sync で開発再開できる」を方針とし，環境定義は pyproject.toml / uv.lock でリポジトリ内に持たせる（デバイス側は git / gh / uv のみ）．ENV_04（開発コマンド）は pyproject 作成時に確定させるため後回し．GitHub リポジトリ作成はユーザー実施待ち（ENV_03 参照）
- [x] **/setup フェーズ 4 完了（仕様設計）**: 旧実装の解析から SPEC_01（画面仕様）・SPEC_02（診断ロジック仕様）を作成．
  - 設計判断: ① 診断失敗検出は現行のマジックナンバー比較（スコア 42/0/5/11/3 一致）をやめ原因の直接検出に変更 / ② 日本語結果画面は文字入りビットマップ背景をやめ描画生成に変更（アセット非依存・表示は現行踏襲）/ ③ Lab 変換は CIE 標準の低輝度分岐を持たない現行式を意図的に踏襲（結果同一性優先）
- [x] **/setup フェーズ 5 完了（規約整備）**: Python スタック向け path-scoped rules（coding-style / project-structure / testing / error-handling）を作成．パッケージ名は `tiasjudge`（src レイアウト，core と gui を分離），テストは旧実装との回帰テストを最重視，GUI 自動テストは書かない方針
- [x] **/setup フェーズ 6 完了（開発計画）**: PLAN_02 を作成．core 先行の 8 ステップ構成（骨格 → csv_io → color → svm → 回帰テスト → GUI 2 画面 → 配布）．M1 = 旧実装との結果一致の証明（ステップ 5，旧 exe での入出力採取にユーザー協力が必要），M2 = アプリ置き換え可能
- [x] **/setup フェーズ 7 完了（立ち上げ完了）**: CLAUDE.md を最終更新（概要にスタック明記・必須ルール・ドキュメント一覧）．プロジェクト立ち上げフローはすべて完了．次は PLAN_02 ステップ 1（プロジェクト骨格）を `/implement` で開始する
- [x] **PLAN_02 ステップ 1 完了（プロジェクト骨格）**: pyproject.toml（uv + hatchling，src レイアウト）・`src/tiasjudge/{core,gui}` 骨格・`data/data.csv`（CP932→UTF-8 変換配置）・ENV_04（開発コマンド）を作成．`uv sync` → `uv run pytest` 通過を確認
  - 設計判断: opencv-python は無指定だと 5.0.x が入るため，ENV_01 の「OpenCV 4.x」と `cv2.ml.SVM` の挙動同一性を優先して `>=4.10,<5` に固定
  - 発見: `data.csv` の実体は 198 列（医師 39 ブロック）だが末尾ブロックは全 0 の未使用列で，有効データは SPEC_02 どおり 38 名分＝193 列（SPEC_02 に注記済み）．旧実装の読込列数はステップ 2 で `org/src` と突き合わせて確定する
  - 未実装・後段: `__main__.py` はプレースホルダ（ステップ 6 で GUI 起動に差し替え）．pytest-cov はステップ 2 以降で導入検討
- [x] **PLAN_02 ステップ 2 完了（core: CSV 読込）**: `core/csv_io.py`（変換行列 17×3・data.csv の読込と形式検証，専用例外 3 種）を実装（無人運転）
  - 確定事項: 旧実装の data.csv 読込は **193 列**（`EXP_OUTPUT_DIM=190`＝38 名 × 5 色，`CsvRead` cols=193）．実ファイル末尾 5 列は旧実装でも未読．SPEC_02 の注記を確定版に更新済み
  - 設計判断: dtype は旧実装（`CV_32F`・int キャスト）に合わせ lab=float32 / evaluations=int32．変換行列は計算精度優先で float64（旧実装も double で計算）
  - レビューポイント（仕様に規定なし・挙動は現状維持のまま起床後の判断待ち）: ① 変換行列の nan/inf セルを受理する（有限値チェック追加の要否）/ ② 評価列 `1.0` 表記を 1 と解釈する / ③ UTF-8 失敗時の cp932 フォールバックの要否（ENV_01 の UTF-8 統一方針との整合）/ ④ 変換行列は 17 行ちょうどを要求（旧実装は 18 行以上でも先頭 17 行で通す）
  - 旧実装の潜在バグ発見: `Readfile()` の係数初期化ループが `i < 16` までで `a[16]` 等が未初期化（正常な 17 行ファイルなら影響なし．新実装には持ち込まない）
- [x] **PLAN_02 ステップ 3 完了（core: 色変換）**: `core/color.py`（RGB→XYZ 17 項多項式・XYZ→Lab 現行式）を実装（無人運転）．C++ 逐語移植の参照実装と 4 ケースでビット一致を確認
  - 設計判断: 旧実装の精度を忠実に再現（係数 float32 保持 → double 演算 → Lab を float32 丸め）．SPEC_02 に精度踏襲の注記を追加．CIE 低輝度分岐は仕様どおり実装しない（テストは「分岐が入っていないこと」まで検証済み）
  - テストの検出力をミューテーション 6 種（項順入替・クリップ削除・CIE 分岐追加等）で確認し全検出
  - 留意（ステップ 4 へ）: XYZ=0 のとき L\*=−16 と負値になり学習データの範囲（0〜100）外の入力が SVM に入りうる（旧実装と同一挙動．問題ではないが把握しておく）
- [x] **PLAN_02 ステップ 4 完了（core: SVM 分類・ROI）**: `core/svm.py`（190 列 SVM 学習・予測・5 色スコア・上位 2 色・失敗検出）と `core/roi.py`（51×51 ROI 平均色）を実装（無人運転）
  - 確定事項: 旧実装の SVM パラメータは C_SVC / RBF / γ=0.011 / C=12.0 / termCrit=EPS のみ（OpenCV 2.4 と 4.x で termCrit の解釈が同一であることをソース比較で確認．COUNT を足すと挙動が変わるため足さない）．旧失敗判定マジックナンバー（42/0/5/11/3）は「域外入力に対する SVM の既定応答」であることを実測で特定し，Lab=(−16,0,0) で新実装から完全再現（旧実装との挙動一致の強い傍証）
  - **⚠ 要レビュー（無人運転中の仕様変更判断）**: 失敗検出を「直接検出のみ」とする /setup 時の設計判断を一部戻した．直接検出だけでは L\* が小さい正値の域外入力（旧実装は失敗表示）を取りこぼし「紫 42%」等の誤結果を表示してしまうことが tester の実測で判明したため，最上位ルール「診断結果は旧実装と一致」を優先し，**旧パターン判定（42/0/5/11/3 → OUT_OF_DOMAIN）を直接検出と併用する形で復活**させた．SPEC_02「診断失敗の判定」節を改訂済み．異議があれば `LEGACY_FAILURE_PERCENTAGES`（svm.py）と当該節を戻すこと
  - 設計判断: 集計の単精度丸め（float32 逐次加算・float32 100 倍 → 偶数丸め）と上位 2 色タイブレーク（旧 std::map キー順 crimson→dark_red→pink→purple→pale）まで旧実装を再現．ROI 範囲外は旧実装のクラッシュを ValueError に置換（GUI 側でガード前提）
  - 未実装・後段: TRAINING_FAILED / PREDICTION_FAILED は健全な入力から再現不能のためテスト未発火．GUI 実装時に 5 点目クリックのガードが必要（MAX_ROI_POINTS は core 側の定数のみ）
- [x] **PLAN_02 ステップ 5 準備完了（回帰テスト骨格・採取手順書）**: `tests/test_regression.py`（フィクスチャ CSV があれば照合，無ければ skip）・フィクスチャ形式定義・`docs/06_TEST/TEST_01_回帰テスト採取手順.md`・単色 PNG 生成スクリプト（`scripts/`）を作成（無人運転）．**採取は旧 exe を使うユーザー作業**（TEST_01 参照）で，フィクスチャを置くと回帰テストが自動で有効化される
  - 設計判断: 旧 exe は RGB/Lab 直接入力不可・クリック位置固定不能のため，全面単色 PNG（ROI がどこでも既知 RGB）で入力を厳密再現する方式．旧 exe の表示は Lab・スコアとも整数（%3.0lf）のため許容誤差は Lab ±0.51・スコア完全一致と定義
  - 採取時の注意（旧実装のバグ回避）: 右クリックリセットはクリック数・平均色を戻さない／5 点目は加算済みのままエラー表示．手順書で「1 点のみクリック・やり直しは再起動」と規定
- [x] **PLAN_02 ステップ 6 完了（GUI: メイン画面）**: `gui/main_window.py`（QWidget 非依存ロジック層＋画面層の 2 層構成）と `__main__.py` の GUI 起動化（`--check` オプション付き）を実装（無人運転）．**実機での動作確認は未実施**（オフスクリーンでの構築・スモークのみ）
  - 設計判断: 旧実装のバグ 3 件を意図的に非踏襲（右クリックリセットでクリック数も戻す／5 点目は加算しない／端クリックは新規ガード文言で案内．SPEC_01 に追記済み）．日本語パス対応のため imread でなく read_bytes + imdecode を採用
  - 途中で発見・修正: 0 バイト画像で cv2.error が ImageLoadError に包まれずクラッシュする不具合（テストで検出 → 修正 → xfail 解消の赤 → 緑を確認）
  - 後段（ステップ 7 へ）: 結果画面の口は diagnosis_finished シグナル＋ result_presenter コールバック．暫定 QMessageBox（_present_result / _summarize）はステップ 7 で削除する
  - 実機確認が必要な項目は起床後の報告参照（クロップ位置・クリック座標対応・ガード文言・旧 exe との結果一致・高 DPI 表示等）
- [x] **PLAN_02 ステップ 7 完了（GUI: 結果画面・結合）**: `gui/result_window.py`（純ロジック層＋ QPainter 描画生成＋モードレス ResultWindow）と main_window との結合（暫定 QMessageBox 削除）を実装（無人運転）．**実機での動作確認は未実施**
  - 設計判断: 日本語結果画面は設計判断どおり描画生成（座標・色は旧 ResultDlg.cpp の値を移植）．5 色目の日本語表記は旧実装が画面間で異なっていた（メイン「淡白」・結果「淡白紅」）ことを旧背景画像 img_student2.bmp の実確認で確定し，現行踏襲でどちらも維持（SPEC_01 修正済み）
  - 旧実装との軽微な意図的差（SPEC_01 規定なし・要レビュー）: % 記号の色（旧黒 → 新赤）・y オフセット +7 の省略・英語時の切替ボタンラベル（旧 "Japanese" → 新「日本語」）
  - 残タスク: ステップ 5 の採取（ユーザー作業，TEST_01）→ 回帰テスト有効化が M1．ステップ 6〜7 の実機確認が M2．__main__ の QGuiApplication 既存時の堅牢性は既知の軽微リスクとして記録
- [x] **結果画面の見た目調整＋プレビューコマンド**: ユーザーの実機確認フィードバックを受けた調整（動作自体は問題なしと確認済み）
  - 文字色を旧背景画像 img_student2.bmp のピクセル実測値に完全一致させ，5 色の色名のみ黒縁取り（旧画像実測に基づく幅 2 px）．フォントサイズも旧画像のインク高実測に合わせ全体拡大（説明文は Medium 500・40 px で確定．ユーザーが試行の上決定）
  - 英語の長文が行に収まらない場合は省略でなく段階縮小で全文維持する方式（_fit_font）に変更
  - `uv run tiasjudge --preview-result`（/ `--failed`）を追加: 結果ウィンドウ単体をダミー結果で即起動するデバッグコマンド（毎回の診断操作を省くため．ENV_04 記載）
- [x] **結果画面のサブモニタ最大化表示（2 画面運用）**: ユーザー要望による追加仕様（旧実装に無い）．結果画面をメイン画面と別のモニタに最大化表示する（SPEC_01「表示先モニタ」節を新設）
  - 設計判断: 基準はプライマリ固定でなく「メイン画面が載っているモニタ」．モニタ 1 台時は従来表示にフォールバック．同一モニタ判定は identity → 同名の 2 段（Qt が別ラッパを返すケース対策）．移動 → 最大化の順序を厳守（逆だと元モニタで最大化される）
  - 2 画面実機での動作確認済み（ユーザー確認）
- [x] **結果画面の表示専用化（操作をメイン画面へ移動）＋英語説明文の行単位サイズ**: ユーザー要望による変更（実機確認済み）
  - 結果画面から言語切替・閉じるボタンを削除し表示専用に（患者側モニタに操作 UI を出さない）．メイン画面に「結果画面の操作」グループを新設（言語切替は常時有効・閉じるは結果画面が開いている時のみ有効．言語状態はメイン画面が保持し次に開く画面へ継承）．プレビューは `--english` オプションで英語確認
  - 英語の説明文サイズは行単位の固定値（Red / Dark red 36 px・Pale red 30 px・他 40 px）．一律縮小 → 行単位と試行し，「スコア付き強調行でも縮小なしで収まる実測最大値」を行ごとに採用（強調時にサイズが変わるチカつきを排除）．Red は当初 40 px 指定だったが実測で収まらず 36 px に変更（ユーザー承認済み）
- [x] **PLAN_02 ステップ 5 完了（回帰テスト）— M1 達成**: 旧 exe の実測 12 ケースで RGB→Lab（±0.51）・5 色スコア（完全一致）・失敗判定（完全一致）の**全 25 テストが一致**．診断ロジックの旧実装同一性が証明された
  - 採取は全自動化（`scripts/auto_capture.py`）: 旧 exe を 1 枚ごとに起動し，Win32 メッセージ（WM_SETTEXT / クリック / WM_GETTEXT）で操作・読取．手動採取は不要になった
  - 経緯: ① 旧実装のバグで紫スコアが画面から読めない（IDC_PURPLE のフォント指定ミスで 150px の巨大文字）→ WM_GETTEXT で吸い出す `scripts/read_legacy_values.py` を作成 ② それを発展させ全自動化 ③ 初回失敗の原因は「結果画面（#32770）をエラーダイアログと誤検出」というスクリプト側のバグで，旧 exe への WM_SETTEXT 入力は最初から機能していた
  - failed 判定はスコア組（42/0/5/11/3）から導出（旧 ResultDlg の表示条件そのもの）．この変換行列では純色だけでなく一部の舌色（115,62,68 / 170,92,112 / 196,74,74）も失敗パターンになることが判明（学習データ域外）
  - 残: `lab_to_scores.csv`（高精度照合用の任意フィクスチャ）は未採取のまま skip（Debug ビルドが必要．必要になったら TEST_01 末尾の手順で）
- [x] **PLAN_02 ステップ 8 完了（配布）— 全ステップ完了**: PyInstaller で Windows 単体 exe（onefile・コンソール非表示・`dist/TIASjudge.exe`）を作成．同梱 data.csv での起動・診断を確認し，ユーザーの実機確認も済み（ENV_04「配布用ビルド」に手順記載）
  - 設計判断: エントリはパッケージの `__main__.py` 直指定でなく薄いラッパ `packaging/entry.py` を新設（`__main__.py` 直指定だと PyInstaller がパッケージを辿れないことがある）．同梱リソースのパス解決は frozen 判定＋候補リスト方式（環境変数 → `sys._MEIPASS` → パッケージ同梱 → リポジトリ直下）
  - アプリアイコンは旧実装と同一の ico を `packaging/TIASjudge.ico` に配置（`org/` は Git 管理外のため管理下コピーはこれが唯一）．exe・ウィンドウ・タスクバーで統一（SPEC_01 に注記）
  - 失敗パターン: exe 差し替え後もエクスプローラーの表示が変わらない事象はアイコンキャッシュが原因（exe 内容は正しい）．別名コピーでの確認・`ie4uinit.exe -ClearIconCache`＋エクスプローラー再起動で解消．ショートカット（.lnk）は別途 IconLocation の再設定が必要
  - 後段メモ: GUI を出さない配布物検証用に `--verify-data` を追加．`_verify_bundled_data()` は CsvIoError のみ捕捉（diagnose の例外は素通り）・他フラグ併用時は `--verify-data` 優先で黙って無視，の 2 点は既知の改善余地．リソースパス解決を `core/` へ移す案も申し送りあり（現状は gui 層）
