012 ゴムひもの山歩き 教材QA
検証日：2026-10-05 UTC
対象：01_motion.py / 02_spring.py / 03_reel_release.py / 04_collision.py / 05_summit.py
環境：cloud Linux、Python 3.12.14、Pyxel 2.9.9、SDL_AUDIODRIVER=dummy。
仕様はPython 3.11以上。実Python 3.11・Windows/macOS GUIは今回未検証。
指定の既存仮想環境を利用し、新規インストールは行っていません。

1. 教材モデル・Controller・包装・描画記録：47テスト PASS
qa/model_tests.txt：原作指定あり、47件全てPASS。
qa/standalone_tests.txt：標準Python、46件PASS、任意の外部原作比較1件のみSKIP。
コマンド：python -B test_stages.py
原作直接比較のみORIGINAL_012_DIR環境変数が必要。その他のテストは原作に依存しません。
・単位、dt、4刻み、床着地、地上36/空中24、速度100上限、非有限値、純粋更新
・ゼロ距離、たるみ、距離=自然長、直上の伸び、3-4-5方向、内/外向き減衰、張力240上限
・接続距離68の境界、地上から引かれて浮く、切離しと未接続自由運動の一致
・巻取り12px/s・自然長14/42の上下限・未接続時無変更
・ゼロ線分、角の同着、薄い壁への掃引、上面着地95.99と横壁95.99の違い
・支持面から離れる、ひも遮断、届かない選択杭、SNAG停止/復帰
・距離90超の警告蓄積・0.3秒切離し・範囲内へ戻った警告のリセット
・人工的な危険岩への接触、04の到着なし、05の中棚復帰・復帰後の再攻略
・旗棚・未接続・静止・0.25秒休憩、到着後凍結とR
・実Controllerの左右相殺、保持と押し始め、P停止と再開、R時Space抑止、A/D選択
・実コードのsample_keysへPyxel相当のbtn/btnpを与えるアダプタ検査
・入力fixtureの押し始め整合性、実コード断片、MIT全文、差分一致
・原作由来fixtureの全状態hash、PNG寸法768×768、実描画記録とControllerの最終状態一致
合成入力試験です。OSが物理キーボード入力を届けたという検証ではありません。

2. 原作ルート3との一致
qa/source_provenance.json：原作model/app/README/LICENSE/routes/experimentのSHA256。
qa/canonical_reference.json：原作を-Bで読み取り、同じ入力列と各状態hashを保存。
qa/canonical_comparison.json：5系列、1,901入力、初期を含む1,906状態、全18状態項目が完全一致。
系列：無入力180、2杭で到着260、歩行落下280、固定seedの混合入力900、巻取り境界281。
直接比較も47テスト中に含めて実行しました。原作ファイル6点のhashは変化なし。
原作ルート3の座標・杭・ゴールは同じ。ASCIIにするためタイトルとヒント文字列のみ違います。
教材01〜03は意図的な簡略化、04は原作ルート1の配置で終了と中棚チェックポイントを省略。
05は原作ルート3の全モデル規則を維持しますが、原作の3ルート選択や録画UIは省略しています。
有限な試験であり、全可能状態・全ルート・全UIの同等性を証明するものではありません。
原作の24テストと自動デモは親作業側の別記録です。この47件に加算していません。

3. 実Pyxel headless：12画面PASS
再実行：python -B run_qa.py
qa/render_plan.json、qa/render_runs.jsonと各PNG/JSON/logを同梱。
・01〜05_initial：初期状態を12フレーム描画、モデル入力なし
・01_ground：右45フレーム、(66.30,100)、床着地
・02_stretched：右＋接続12フレーム、(59.07,95.42)、引張り68.28
・03_winding：巻取り30フレーム、自然長26、横速度50.84
・03_release：続いて切離し10フレーム、横速度保持・重力で縦速度変化
・04_landing：150フレーム、向こう岸GROUND 1、y95.99、落下0、終了はしない
・05_middle：130フレーム、GROUND 1、CHECKPOINT 1、自然長22
・05_summit：260入力フレーム、SUMMIT!、FALLS 0、CHECKPOINT 2、経過6.23秒で凍結
全12PNGを2枚のコンタクトシートで開き、05_summitの元PNGも開いて確認。
人物・ひもの接続/解放・速度矢印・棚・選択枠・旗・数値・操作説明・到着表示を確認。
文字の切れ、HUDの重なり、人物が棚を貫通したように見える問題は見つかりませんでした。
qa/visual_inspection.jsonに確認対象を記録。元PNGは768×768、ゲーム内部は384×384。

4. 独立ファイルと日本語・空白パス：5本PASS
qa/isolation_runs.json。
各.pyを1本ずつ、日本語と空白を含むフォルダーへコピー。
毎回別の無関係CWDから実Pyxel headless起動し、相対指定のPNG/UTF-8 JSONがCWDへ出力されることを確認。
コピー先のプログラムフォルダーにはその.py以外のファイルなし。原作・隣の段階・画像・フォントを参照しません。
PYTHONIOENCODING=cp932、PYTHONUTF8=0で実行。これは実WindowsでのGUI検証ではありません。

5. 未検証・省略
人の楽しさ/難易度評価、実OSキーイベント、Windows/macOS GUI、ブラウザ上の操作、音声出力。
音は実装していません。dummy driverは音を聞いた証拠になりません。
原作ルート2の危険岩付き実攻略は教材に収録せず、危険岩処理は人工的なモデル試験で確認。
Sites公開・Site checkout変更・外部投稿・原作変更はこの作業では行っていません。

6. 配布と凍結
5本のPython、requirements、LICENSE、README、LESSON、lesson_meta、lessons、4差分、2検証用Pythonとqaを同梱。
qa/freeze_manifest.jsonはmanifest自身以外の配布全ファイルのSHA256。
凍結後に変更した場合は、影響する検証を再実行し、関連差分とmanifestを更新してください。
