Representative input (602 min, 200 guests) cannot start Operations (Day1 06:00 only, population cap 100), so Ops ON uses the real new-game opening and 200-guest Ops ON stays unmet. Pin the autoload/main startup order, define render counts from the world_preview model, separate input shortfall from natural change, split checker/static review/owned-run proof, and make the collector self-contained (tracked run_all_tests.py and owned_outer.py only). Budgets and original denominator unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
18 KiB
18 KiB
P28-01 現HEADの性能基準:最小バッチ仕様
この文書は Runner の spec_ref です。work_packages.json 全体の代わりに、この対象に必要な原本定義だけを固定します。
1. 原本定義(変更しない)
- 分母は原30パッケージ・88成果単位で、部分点はありません。開始時点の正式完了は22/88、完了パッケージは4/30です。
- 成果単位 P28-01「現HEADの性能基準」(
work_packages.jsonの P28.remaining_deliverables[0])- work: 実Runtimeの20,000マス・200客・50職員・150施設を基準にTick、経路、snapshot、描画、保存を測る。
- acceptance: 機種、ビルド、シード、速度、測定区間、平均・P95・最大を残す。
- パッケージ P28「性能・操作感・長期バランス」
- requirement_ids: B-1.1, B-3.1a, B-4.3a, B-4.3c, B-11.1, B-12.1, B-14.4
- decisions: D04, D08, D10
- package acceptance(P28-01 の完了では満たしたことにしない): 既存性能予算と新しいWindows描画目標を満たす。到達不可能な収支/信仰/ランク条件なし。高速自動再生だけでプレイ時間・操作感を合格にしない。
- P28 横断条件6件(各成果で適用し、適用外にするときは理由を記録する)
- 原本条件とUI操作が一致
- Runtimeの実利用・会計・行動へ接続
- 途中保存・再起動で再現
- 異常/境界/中断を検証
- 関連回帰PASS
- 要件別ログとソースハッシュを保存
2. 完全原本参照
docs/traceability/original/BASELINE.txt(SHA256 75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a)。行番号は requirement_coverage.json の source_lines です。
| ID | 行 | この対象での意味 |
|---|---|---|
| B-1.1 | 2–10 | PC (Windows)、想定プレイ時間 |
| B-3.1a | 58–60 | 現実10秒=ゲーム1時間、Pause/x1/x2/x3 |
| B-4.3a / B-4.3c | 84–87 | A*経路、EV待ち時間、渋滞時の50%減速 |
| B-11.1 | 213–216 | メイン画面、スクロール、ズーム、表と裏 |
| B-12.1 | 228–230 | ドット絵マップの描画 |
| B-14.4 | 258 | UIレスポンス向上 |
既存の予算値は scripts/core/performance_budget.gd:17-24 のとおりで、変更しません。x1 平均4 ms、x3 P95 12 ms、経路 P95 2 ms、main-thread stall の最大50 ms、保存5 s、読込8 s です。
3. 再利用する既存証拠(再生成しない)
- source: main
8c36c100b2073baf8a9c536528f319a5d0837a63。製品 source map は正式全回帰p14-03-formal-regression-83b170a-r01と同じ1407件です。この対象では製品と source map の対象(autoload/scripts/scenes/data/tests/assets/tools と直下4ファイル)を変更しません。変更がなければ、正式192 suite の証拠はそのまま有効です。 - 代表入力:
artifacts/original-completion-2026-10-02/p28-current1299-runtime-scale-build-candidate-native-r01/fixture/reference.variant(SHAeb4f7700…f9bd)。20,000/200/50/150、Operations OFF です。全生成はしません。 - engine:
/Applications/Godot.app/Contents/MacOS/Godot(SHAc7cccbf8…8ccf、4.7.2.stable.official.ed1daf0bf)。 - 既存の headless 実測 r02(
p14-03-representative-perf-r01/run-r02/execution.jsonSHA553971f5…9fc1)- 条件: Mac16,10・10コア・24 GB、Operations OFF、x1、300分
- 1分 tick: 平均826.6 ms、P95 1702.7 ms、最大2918.1 ms
- 経路 cold: P95 0.61 ms
- snapshot: 平均77.6 ms
- 保存: manual 297 ms、autosave 327 ms
- 読込: 平均3193 ms、最大3368 ms
- これは x1 headless・Operations OFF の行としてそのまま採用し、再測定しません。
- collector の型:
p14-03-representative-perf-r01/collect_perf.py(SHA43f3e429…c063)とmeasure.gd(SHA95e59678…f76a)、run-r02/profile_guard.gd(SHA597696c7…a6f)。以下を引き継ぎます。- 私有の CoW stage、private user:// 保存
- owned Popen による PID の捕捉、guard、cleanup
- 通常 player の保全
- 全 raw stdout と SHA の結合
- 上の artifacts/ は gitignore 対象のため、Root が worker へ build/ 配下の読取専用として渡します。
- collector の依存は自己完結させる。 r02 の
collect_perf.pyは、gitignore 対象のwindows-current1297-tomari-release-adapter-prep-r01/windows_adapter.pyを動的に import しています(:153)。そのadapterは、さらにlegacy/windows_adapter.pyとsupport/owned_outer.pyを読み込みます。新しい collector はこの外部パスに依存しません。- import してよいのは、Git で追跡している次の2つだけで、どちらも変更しません。
tools/run_all_tests.py:source_refs、default_player、private_project、copy_source、profile_pathtools/regression/owned_outer.py:run_phase、finish_captured。SHAc2e3483b…66e7で、adapter の support 版とバイト一致し、内部でowned_diagnostic_process.pyを読み込みます
- それ以外の補助処理は、allowed_paths の中に実装します。対象は、private import 用の project、
run_engine相当の owned phase の実行(上限は1800 s で、adapter の固定値1200 s ではない)、profile_files、no_live_engine、厳密な診断です。 - adapter の2ファイルは仕様の参照にだけ使い、import しません。読取専用入力の SHA は REGISTRATION_PROPOSAL.json に記載します。
- import してよいのは、Git で追跡している次の2つだけで、どちらも変更しません。
4. 入力の選択と前提(API監査の結果を反映)
4.1 Operations ON は代表入力では開始できない(事実)
SimulationRuntime.enable_production_operations()(autoload/simulation_runtime.gd:1534-1544)は、Day 1 06:00、つまりabsolute_minute == OPERATIONS_START_MINUTE(360、:49)のときだけ開始できます。- 代表入力は602分(Day 1 10:02)に保存されているため、
INVALID_START_TIMEになります。 - clock だけを巻き戻すことは禁止です。
- 代表入力は602分(Day 1 10:02)に保存されているため、
- Operations ON の人口上限は100人です(
scripts/operations/operations_coordinator.gd:85、:203、:333)。- 開始時には
set_population_count(guest_count)(simulation_runtime.gd:1579)が上限を照合するため、200客ではINITIAL_POPULATION_INVALIDになります。 - 正規契約の上での200客 Operations ON の状態は存在しません(PERFORMANCE_AUDIT.md「100客上限と200客診断」)。
- 開始時には
- 既存の成果物の中に、代表規模の Operations ON の保存はありません。代表入力の build 記録は、
operations_enabled_mixed_load_accepted: falseです。
4.2 選択した入力(製品変更なし、fixtureの全生成なし)
| 入力 | 中身 | Operations の実状態 | 使い道 |
|---|---|---|---|
| R:代表入力 | 上の reference.variant(SHA eb4f7700…、602分、20,000/200/50/150) |
main の _ready が enable_production_operations() を呼び、INVALID_START_TIME で失敗する。この実結果を記録したうえで OFF |
原 work の「20,000マス・200客・50職員・150施設」の windowed x1/x3 |
| O:新規開始 | 起動時に autoload の SimulationRuntime._ready が行う initialize(GameClock, GameState, DEFAULT_WORLD_SEED)(:201-206)。Day 1 06:00 |
main の _ready が開始に成功した実際の ON |
通常UIで始まる Operations ON の自然営業 |
- O は代表規模ではありません。O の値を「200客」や「代表負荷」と呼ばず、実数を併記します。
- 200客での Operations ON は、製品契約(上限100)のために測定できません。これは P28 の未達として残し、P28-01 では満たしたことにしません。
4.3 起動順序(独立した Runtime は作らない)
計測するのは、既定の autoload(/root/GameClock、/root/GameState、/root/SimulationRuntime)と、main が描画する同じ世界だけです。SimulationRuntime.new() や別の initialize で作った Runtime は計測しません。
- engine は phase ごとに1プロセスを起動し、r02 と同じ Guard の bootstrap(
profile_guard.gd)と--scriptの wrapper を使います。windowed なので--headlessは付けません。 - autoload の準備が終わったら、
GameClock.set_paused(true)で時間を止めます。 - R の場合:
/root/SimulationRuntime.restore_snapshot(<get_var(false)で読んだ入力>)で復元します。- 復元が成功し、登録数が20,000/200/50/150と完全に一致すること、および入力 SHA の一致を確認します。
- 一致しなければ開始前の不足として停止します(§5)。
- O の場合は何も復元しません。
res://scenes/main/main.tscnを instantiate して root に追加します。main の_ready(main.gd:218-250)の処理は次のとおりで、それぞれの戻り値とget_operations_report().enabledの実値を記録します。_pause_fresh_openingenable_production_save_policyenable_production_operationsの自動呼出し
focus_operating_areaの deferred 処理が終わってから、camera(zoom、pan、layer、viewport の大きさ)を記録し、計測中はそのまま固定します。- 速度は
main._set_speed(n)(:2727、GameClock.set_speedを呼ぶ)で設定し、一時停止の解除はmain._toggle_pause()(:2689)で行います。どちらも UI のボタンと同じ処理です。- 以降、時間は
SimulationKernel._process → process_real_deltaだけで進めます(MAX_TICKS_PER_FRAME 8、MAX_BACKLOG 120、simulation_kernel.gd:30-31、:54-72)。 advance_minutes、advance_one_minute、spawn_guestなどの直接呼出しはしません。
- 以降、時間は
- production save policy の下では autosave が起きることがあります。保存先は collector の private user:// に限り、通常 player の保全を前後で照合します。区間内に起きた autosave は stall の原因として記録します。
5. 測定
phase は次の4つです。それぞれ別の engine プロセスで実行し、区間は warm-up 30ゲーム分のあと、計測120ゲーム分です。
| phase | 入力 | 速度 |
|---|---|---|
| P1 | R(OFF) | x1 |
| P2 | R(OFF) | x3 |
| P3 | O(ON) | x1 |
| P4 | O(ON) | x3 |
- x1 headless での保存・読込・経路・snapshot は r02 の値を採用し、windowed では測りません。
段階0(同じ collector)
- P1・P2 の長い計測の前に、R を x1 で10ゲーム分だけ動かします。O についても P3・P4 の前に、x1 で10ゲーム分の限定動作を行います。
- 各段階0で次のものを記録してから、各 phase の所要時間を外挿します。
- Operations の enable の実結果
- 1分ごとの時間
- 実数
- 完了時間
- 見積り: R は 827 ms × 150分 × 2 ≒ 250 s です。O は段階0の実測から外挿します。
- phase ごとの上限は1800 s です。
各 sample に残すもの(取得できない値は null と理由にし、0 や合格で補いません)
- 機種: hw.model、コア数、メモリ
- build: engine の文字列・hash・SHA、project の commit、source map の SHA
- seed: restore 後、または新規開始後の
SimulationRuntime.world_seedの実値 - RNG state:
rng_streams。seed とは別欄にします - 入力 SHA と、phase 準備状態の SHA
- 速度と、
GameClock.speedの実値 - 区間: 開始・終了のゲーム分と wall 時刻、frame 数、各 frame で進んだ分数
- 時間の統計: frame 時間、kernel の1分 tick(
get_performance_reports)、stall(50 ms を超えた frame の数と最大)、描画 CPU/GPU の時間(RenderingServer.viewport_set_measure_render_time、viewport_get_measured_render_time_cpu/_gpu)、Performance.TIME_PROCESS。それぞれについて平均・P95・最大・標本数を記録します - 実数は3種類を分けて記録します
- 登録総数: 客・職員・施設・歩行可能セル
- 実稼働数: 退館中でない在館の客、勤務中の職員、稼働中の施設
- 描画数: 描画に実際に使われた model の数です。world_preview は Canvas に即時描画するため(
_draw:512、_draw_pixel_actor:1525)、人数分の Node はありません。そのため Node の数ではなく、次のように数えます。- actor:
world_preview.get_life_observation_report()(:564)のactor_drawingsのうち、現在の layer にあるもの(_observe_actorsが layer で絞ったもの)の件数と、そのうちatがRect2(Vector2.ZERO, size)の中にあるものの件数。observation_serial も添えます - 施設: 完成した施設のうち、現在の layer にあり、cell の外接矩形が viewport と交わるもの(
_draw_world_entity_icons:966 と同じ判定)の件数 - セル: bounds の中で
get_cell_rect(cell)が viewport と交わるセル(_draw_world_cells:765 と同じ判定)の件数 - あわせて layer、zoom、pan、viewport の大きさを記録します
- actor:
判定: 予算(§2)との合否を生の値で並べます。新しい許容値は作りません。
6. 停止条件と人数の扱い
- 開始前の不足: 次のいずれかに当たれば、その phase の計測を始めずに原記録を残して止め、
input_shortfallとします。- R の入力 SHA の不一致
- 復元の失敗
- 登録数が20,000/200/50/150と一致しない
- O の Operations が開始できない(enabled が false、または開始が360分でない)
- 計測中の自然な変化: 客の退館・来館、職員の状態の変化は、すべての sample の実数として記録します。これは停止条件ではありません。
- 計測の途中で客を再注入したり、状態を固定したり、復元をやり直したりすることは、通常の計測に入れません。
- 区間の要約には、登録数と実稼働数の最小・最大を必ず併記します。登録が200で、実稼働数も示した区間だけを「200客」と表記できます。
- 途中停止: 次のいずれかに当たれば、そこまでの原記録を残して止めます。次の最小手順は判断担当が決めます。
- phase の上限(1800 s)を超えた
- engine が異常終了した
- Operations の runtime failure の latch、または terminal(破産)になった
- 要対応イベントのために clock が止まり、進まない
- collector がイベントを自動で選んで解消することはしません。
7. 証明の役割分担
| 担当 | 確認すること | 確認しないこと |
|---|---|---|
checker(check_p28_01_report.py)と、合成 JSON での unittest |
report の形式、必須の欄、null に理由があること、自己矛盾がないこと。たとえば次のものを拒否します。 ・速度とゲーム分・frame の不整合 ・seed 欄が RNG state と同じ値 ・登録数が稼働数や描画数の欄に流用されている ・GPU 時間が 0 で理由がない ・「200客」の表記と実数の不一致 |
経路・seed・実稼働数が事実かどうか |
| probe の実経路の静的レビュー(独立 Opus) | §4.3 の順序、_set_speed/_toggle_pause/_process の経路、autoload の世界を計測していること、直接 advance・再注入・別 Runtime がないこと |
実行したかどうか |
| Root の実測(owned PID、raw の結合) | 実際の engine プロセスで、PID が一致すること、raw stdout と report が一致すること、source map と入力 SHA を照合した実行であること | — |
この3つがすべてそろったときに限り、記録は事実として扱われます。
8. 対象外と非主張
- Windows 実機は未検証のまま残します。
- 200客での Operations ON は、製品の上限100のために測定できず、未達として残します。
- P28 パッケージの予算達成、P28-02、P28-03、長期バランス、プレイ時間は対象外です。
- 4回分の全回帰と、fixture の全生成は行いません。
9. 完了の扱い
- この Runner task: collector、probe、checker が C1〜C9 を満たし、Runner の validation、独立レビュー、integration、final を通ること。これで原本の加算は0です。
- 原本 P28-01 の完了: Root が統合済みの collector で実測し、その receipt を結合した後の証拠ゲートが通ったときに限ります。記録の項目がそろえば、予算が未達でも P28-01 の受入は成立します。未達の予算と200客 Operations ON は、P28 の未達として残します。
10. 登録条件(collector task の baseline)
- C1: 速度は
main._set_speed/_toggle_pause→SimulationKernel._processだけで与え、直接 advance の値を x3 とは呼ばない。checker は不整合を拒否し、probe は静的レビューの対象とする。 - C2: 入力 SHA と phase 準備状態の SHA、登録・実稼働・描画(§5の定義)の3種類の実数を、別の欄に記録する。
- C3: seed の実値(
world_seed)と RNG state を別の欄にし、取得できなければ null と理由にする。 - C4: GPU や描画時間を取得できなければ null と理由にし、0 や合格への補完を checker が拒否する。
- C5: 段階0(R と O のそれぞれ)と外挿の記録、phase ごとの上限1800 s、超過時に原記録を保存して止めることを実装する。
- C6: private user://、owned PID・guard・cleanup・通常 player の保全・全 raw ログと SHA の結合を、r02 と同等に保つ。
- C7: 製品 source map が開始前と終了後で変わらないことを照合し、変われば拒否する。
- C8: 機種・build・seed・速度・区間・平均・P95・最大の各欄を全 sample に要求し、欠けた欄や偽の値を checker が拒否する。
- C9: §4.3 の起動順序(autoload の世界、main の
_readyの実際の Operations の結果を記録)と、§6 の停止条件(開始前の不足と、計測中の自然な変化を区別する。再注入も状態の固定もしない)を実装する。