Skip to content

2.4b3: 進捗トランスポートの統合+実行時制約のテスト昇格(B-006 の構造的後始末) - #45

Merged
kumahide merged 3 commits into
mainfrom
feature/progress-transport
Jul 20, 2026
Merged

2.4b3: 進捗トランスポートの統合+実行時制約のテスト昇格(B-006 の構造的後始末)#45
kumahide merged 3 commits into
mainfrom
feature/progress-transport

Conversation

@kumahide

Copy link
Copy Markdown
Owner

B-006(バッチのプログレスバーが実態を表さず GUI ごと固まる)の構造的な後始末。b2 で症状は直したが、その根本原因=「単一とバッチで進捗実装が2本あること」と「実行時制約がテストで守られていないこと」が残っていた。

③④ 進捗トランスポートの統合(本丸)

単一実行とバッチが各々で持っていた「キュー+after ポーリング」を views/progress.pyProgressPump へ括り出した。

狙いは重複の削減ではなくライフサイクルの統一。従来バッチのポーラは __init__ で1回起動して永久に回り、単一のポーラは実行ごとに起動・停止していた。この非対称そのものが b2 の停止バグ(停止後に残った1回のポーリングが完了表示を上書きする)を生んだ。両者とも「実行のあいだだけ回す」に揃えたので、同種のバグが構造的に成立しなくなる。

2者の差はパラメータ2つに閉じた:

  • 単一 latest_only=True — バーとラベルは最新の状態だけが意味を持つ。取得は1サンプルごとに push されるので全件描くと Tcl 呼び出しが取得時間そのものを支配する(B-006 で実測30倍差)
  • バッチ latest_only=Falsedone/complete が結果を運ぶので1件も落とせない

副次的に見つかった欠陥:実行中にウィンドウを閉じると破棄済みウィジェットへ after し続けて invalid command name になる経路があった(マップ破棄と同じクラス)。閉じる側で stop し、部品側も after 失敗時は静かに止まるようにした。

パイプライン本体は統合しない。出口が対話グラフと確定成果物で異なる=2.3 で確定した設計思想そのものが違うため(共有シナリオランナーは 2.5 の scenario.py)。

①② プリフェッチ相・地形キャッシュの設計判断

  • 地形キャッシュ=バッチにも効かせる。単一は fetch_elevations_cached を使うのにバッチだけ素通りしており、同一バッチの再実行で毎回 DEM を取り直していた(偶然の非対称)
  • プリフェッチ相=設けない。総取得量は変わらず得られるのは進捗の見え方だけで、UI と停止処理が一相増えるのは軽量哲学に反する

開発環境 C-b3(B-006 が露呈した死角)

環境は「構造の正しさ」に強く「実行時の振る舞い」に盲目、という診断への対処。

  • 未強制の「禁止」コメント棚卸し禁止/必須/してはいけない 系を全件判定し、未強制だった4件をガード化。全件ミューテーション検証済み(制約を壊すと実際に落ちる)
    • レポート描画の非並列(rcParams がプロセス共有)/DEM ロック保持中のネットワーク取得禁止/マップ破棄経路の集約(AST 静的検査)/スレッド生成規約(ThreadPoolExecutor 不使用・daemon=True、従来メモリにしか無くコードに痕跡ゼロだった)
    • 判定は二択でなく三択だと判明:coords.py の「必ず DD 正規化」は SimParams が DMS を ValueError で弾く=構造的に強制済みでテスト不要
  • phase 境界ログ:最も時間を食う区間(per-path 描画・サマリ地図)にログ行が1つも無く、B-006 で所要時間が最後まで測れなかった
  • README のテスト件数表記を削除:実数と70件ずれていた。ガードを足すのではなく主張そのものを撤去してドリフト面を消した
  • フル監査チェックリストに**面10「実行時の振る舞い」**を本文化

検証

  • 全テスト通過(518件)・カバレッジ 95.67%(floor 85)・ruff・pyright 0・bandit・ローカル LLM レビュー NO ISSUES
  • 新規ガードはすべて「壊すと落ちる」ことを確認済み
  • 実機βは未実施=進捗表示の見え方とバッチ再実行のキャッシュ効きは要確認

🤖 Generated with Claude Code

kumahide and others added 3 commits July 20, 2026 21:26
B-006 の診断で判明した「環境は構造の正しさに強く、実行時の振る舞いに盲目」
という死角への対処。

① 未強制の「禁止」コメント棚卸し
   `禁止/必須/してはいけない` 系の実行時制約を全件洗い出し、テストで
   守られているかを1件ずつ判定した。未強制だった4件をガード化:
   - レポート描画を並列化しない(mpl_fonts が rcParams をプロセス共有で
     書き換えるため)=test_path_rendering_is_never_parallel
   - DEM キャッシュのロック保持中にネットワーク取得を行わない
     =test_network_fetch_runs_without_holding_the_cache_lock
   - マップ実体の破棄経路が close_map_safely に集約されている(静的検査)
     =test_map_widget_is_destroyed_only_through_close_map_safely
   - スレッド生成規約(ThreadPoolExecutor 不使用・daemon=True/静的検査)
     =test_thread_pool_executor_is_not_used / test_all_threads_are_daemon
     (従来メモリ上の規約でしかなくコードに痕跡が無かった)
   4件とも制約を壊すと実際に落ちることをミューテーションで確認済み。
   コメント側からも対応するガードを指し示すようにした。
   coords.py の「必ず DD 正規化」は SimParams が DMS を ValueError で弾く
   ため構造的に強制済みと確認(テスト不要)。

② phase 境界ログ
   バッチで最も時間を食う区間(per-path のレポート描画・サマリ地図生成)に
   ログ行が1つも無く、B-006 では所要時間が最後まで測れなかった。バッチ開始・
   パス別描画・サマリ地図・全体の所要秒数を radiosim.log に記録する。

④ README のテスト件数表記を削除
   件数は読者に行動可能な情報を与えない一方、テスト追加のたび更新を強制する
   (実際に実数と 70 件ずれていた)。ガードを足すのではなく主張そのものを
   撤去してドリフト面を消した。

あわせて CHANGELOG に記載漏れだった 2.4b2(B-006)の修正も追記。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
単一実行は fetch_elevations_cached(TX/RX 座標+サンプル数が同じなら DEM を
再取得しない)を使うのに、バッチだけが素の fetch_elevations を呼んでおり、
同一バッチを再実行するたびに DEM を取り直していた。B-006 が指摘した単一/
バッチの非対称のうち①②にあたる。

- batch._fetch_sync を fetch_elevations_cached へ統一。1 行だけ直しての
  再実行でも、変えていない行の取得が消える。
- プリフェッチ相はバッチには設けない(ユーザー判断)。遅延取得でも総取得量は
  変わらず、得られるのは進捗の見え方だけで、B-006 でバー配分と段階ラベルを
  入れた今その価値はさらに小さい。

テスト:
- 回帰ガード test_reuses_terrain_cache_across_runs(同一条件の再実行で実取得
  ゼロ回)。キャッシュ利用を外すと落ちることを確認済み。
- conftest に地形キャッシュのリセットを追加。_terrain_cache はプロセス共有で
  キーが座標+サンプル数のみのため、消さないと前のテストの結果が次へ漏れる
  (実際、取得失敗を検証するテストが前のテストのキャッシュにヒットして緑に
  なった)。
- test_per_path_failure_does_not_abort_batch の失敗行に別座標を与えた。
  フェイクは freq で失敗を通知するが freq はキャッシュキーに含まれない(地形に
  影響しないので正しい)ため、同一座標だと先行行のキャッシュにヒットしていた。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
単一実行とバッチが各々で持っていた「キュー+after ポーリング」を
views/progress.py の ProgressPump へ括り出した。

狙いは重複の削減ではなくライフサイクルの統一。従来バッチのポーラは
__init__ で 1 回起動して永久に回り、単一のポーラは実行ごとに起動・停止
していた。この非対称そのものが B-006 の停止バグ(停止後に残った 1 回の
ポーリングが完了表示を上書きする)を生んだ。両者とも「実行のあいだだけ
回す」に揃えたので、同種のバグが構造的に成立しなくなる。

2 者の差はパラメータ 2 つに閉じた:
- 単一 (latest_only=True): バーとラベルは最新の状態だけが意味を持つ。
  取得は 1 サンプルごとに push されるので全件描くと Tcl 呼び出しが取得
  時間そのものを支配する(B-006 で実測 30 倍差)。
- バッチ (latest_only=False): done / complete が結果を運ぶので 1 件も
  落とせない。順序どおり全件処理する。

副次的に見つかった欠陥も修正:
- 実行中にウィンドウを閉じると、破棄済みウィジェットへ after し続けて
  `invalid command name` になる経路があった(マップ破棄と同じクラス)。
  閉じる側で stop し、部品側も after 失敗時は静かに止まるようにした。

パイプライン本体は統合しない。単一の出口は what-if スライダ付きの対話
グラフ、バッチの出口は確定成果物ファイルで、「シングル=条件を詰める場/
バッチ=確定条件で成果物を作る場」という 2.3 確定の設計思想そのものが
違うため(共有シナリオランナーは 2.5 の scenario.py で扱う)。

テスト:
- tests/test_progress.py(13 件)=不変条件をフェイク scheduler で検証。
  停止後の残存ポーリング・二重 start でポーリング連鎖が 2 本にならない
  こと・latest_only の畳み込み・破棄済みウィジェット・スレッド安全性。
- tests/test_smoke.py に配線の確認を追加(単一・バッチとも生成時は
  止まっている/閉じたら止まる)。旧ライフサイクルへ戻すと落ちることを
  確認済み。
- CI の pyright curated 列に views/progress.py を追加。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kumahide
kumahide merged commit 71e41ae into main Jul 20, 2026
1 check passed
@kumahide
kumahide deleted the feature/progress-transport branch July 20, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant