024 まちがい時計：教材検証記録
検証日：2026-10-05 UTC
対象：01_offsets.py、02_differences.py、03_assignments.py、04_possible.py、05_puzzle.py
環境：cloud Linux 6.18.44、Python 3.12.14、Pyxel 2.9.9、SDL_AUDIODRIVER=dummy。
仕様はPython 3.11以上。Python 3.11そのものの実行は未検証です。
指定された既存の仮想環境を使用。新規インストールは実施していません。

1. 純粋モデル・実Controller・配布整合性：37テスト PASS
再実行：python -B -m unittest -v test_stages.py
ログ：qa/model_tests.txt
・01：5値×4台=625割当、局所編集、不変の旧状態、上下両境界。
・02：target−sourceの向き、最後の札だけの不成立、一律移動で変わらない差。
・03：625割当を全検査して原作由来fixtureと一致。成立2行。
  範囲外・型・長さ・0がない/複数・差が合わない行の拒否。
・04：どれかの成立行で0という存在条件、全成立行で0という全称条件の区別。
  候補はB/C、必ず正確な時計はなし。空の割当集合・順序非依存・4時計の証拠行。
・05：Level、全割当、候補集合、4仮説、384遷移、103履歴列を同梱fixtureと照合。
  全16候補集合のうちB/Cだけが成功。過不足、誤答後の編集、終了後の拒否。
・全段階の実際のController.tick：無入力、長押し、解放、同時押し、矛盾するpressed/held、循環移動。
  Nで2行を循環、編集で閲覧位置を解除、Xで可能/不可能、検査と選択の分離。
  提出・仮置きは履歴を増やさず、成功後もZで戻れる。Rでカーソル・仮置き・履歴も初期化。
・文中の操作練習を実Controllerへ入力して確認。
・CLIの段階外入力、未知入力、headless専用オプション、smoke/replay同時指定を拒否。
・全Pythonの原作ライセンス全文、固定requirements、五段階schema、実コード片一致、4差分一致、明示UTF-8 I/O。
純粋モデル/合成入力の試験です。実OSキーボード入力を確認したという意味ではありません。

2. 原作Cとの直接比較：PASS
原作model.pyのLEVELS[2]「まだ二つある」を読み取り専用で使用。
原作model.py、app.py、README.txt、LICENSE.txtは事前に全文確認。
-Bとsys.dont_write_bytecodeでcanonicalへのpyc書込みを防止。
比較前後で原作フォルダー全ファイルのSHA256が一致。初回参照hashとも一致。
原作hash・参照元：qa/provenance.json
直接比較結果：qa/canonical_comparison.json
同梱の再検証用データ：qa/canonical_reference.json
・Levelの名前、時計、全差の札、margin、displayが一致。
・625通りを調べるassignmentsの全結果2行、answerのB/Cが一致。
・4台のhypothesisについてoffsets、outside、zeros、consistent、validが一致。
・16選択集合×3判定状態×8操作=384の構築遷移で状態・accepted・eventが一致。
・手書き3経路とseed=2405の100経路×40入力、合計103経路4,020操作。
  選択、提出、無効操作、Undo、reset、成功後の拒否を含み、各操作後の現在状態と全履歴を照合。
  履歴はselected/verdict/reason/witnessを含む正規化JSONのSHA256で検証。
有限の構築状態と固定seedの比較であり、無限の編集履歴に対する数学的証明ではありません。
01〜04は段階的な分析用実験台。最終05の純粋モデルだけを原作Cと同等と宣言します。
原作の3票選択・UI・矢印リピート・入力優先順位の同等性は宣言しません。
通常37テストに原作は不要です。任意の再比較だけ次を使います：
python -B tools/verify_original.py "原作024_wrong_clocksのフォルダー"

3. 実Pyxel headless描画：18シナリオ PASS
tools/render_qa.pyから実Pyxelを起動し、ゲームと同じController.tickへ合成キーを投入。
PNGは実際のdraw_boardの描画です。コマンド・終了状態：qa/render_runs.json。
各qa/*.jsonにフレーム数、入力列、全操作後snapshot。各qa/*.logに実行ログ。
全5本の初期画面は12フレーム・操作0・ready。
追加13画面：
01_bounds：+2境界で停止。
02_all_differences：[-2,-1,0,+1]で3札ともPASS。
02_last_difference_fails：Dを0へ変え、最後の札だけFAIL。
03_first_assignment / 03_second_assignment：成立2行を別々に表示、どちらも0は1個。
04_no_witness：Aには証拠なし。04_b_witness：Bの証拠行、POSSIBLE B C、CERTAIN none。
05_missing：Bだけの提出で不足。
05_excluded_hypothesis：Aを選んだ誤答、仮A=0ではD=+3で範囲外。
05_complete：B/Cで成功。05_undo：成功後からBだけのeditingへ。
05_terminal：成功後のSpace/EnterでB/Cと履歴2を保持。
05_reset：成功・仮置きの後Rで完全初期化。
全18PNGをview_imageで実際に開き、文字のはみ出し、枠、値、条件表示、候補、仮置き、結果、履歴を観察。
最終再実行後の18PNGは観察時のSHA256と全て同一。qa/visual_review.jsonに記録。
音声処理はなくdummy audio driverを利用。可聴の音を確認したとは主張しません。

4. 単独ファイル配布・文字コード・CWD：5本すべて PASS
各.pyを1本だけ日本語と空白を含むフォルダーへコピーし、別の日本語・空白CWDから実Pyxel起動。
01/02は値変更、03/04は成立行閲覧、05はB/C選択成功まで確認。
他の4段階、原作、画像、フォントを置かず、元ファイル以外の入力ファイルも生成されていないことを確認。
相対出力relative.pngとrelative.jsonは指定どおり別CWDへ保存。
PYTHONUTF8=0、PYTHONIOENCODING=cp932で起動し、JSONは明示UTF-8で読み書き。
これは日本語Windowsの実機検証や全ロケール再現ではありません。
記録：qa/isolation_runs.json

5. 配布一式だけのオフライン再試験：37テスト PASS
教材とfixtureだけを隔離コピーし、原作なし・site-packages無効の-S実行で再試験。
Pyxelを読み込まず全37テストが通過。ログ：qa/standalone_tests.txt、qa/standalone_tests.json。
純粋モデルをimportしてもウィンドウを開きません。GUI実行時だけPyxelをimportします。

6. 権利とfreeze
LICENSEは原作LICENSE.txtと同一。各Pythonの先頭にMIT全文を保持。
原作のNoto別ライセンス注記も原文保存しますが、教材はNoto資産を含みません。
標準ASCII・図形以外の外部素材なし。新しいライセンスや著作権者は創作していません。
qa/freeze_manifest.jsonは配布全ファイルのSHA256を記録。
自己参照を避けるためmanifest自体とqa/freeze_verification.txtだけ対象外。
python -B tools/check_freeze.pyで欠落・変更・余分なファイルを検査できます。
原作や他バッチは変更せず、教材内で作業。隔離用の一時フォルダーも教材内に作成して自動削除。

7. 未実施・集計の区別
人間の面白さ・難易度・読みやすさ評価、実OSキーボード入力、Windows/macOS GUI、Python 3.11実行、
ブラウザ、音声評価、Site公開、ユーザーPC操作。
原作側の既存18テストや自動デモの結果は、この教材37テスト・18画面には合算していません。
