P14-03: Versioned shaft-wait saves, keep riders aboard, real-actor proof

- ElevatorService snapshot v2 carries the shaft-wide wait; v1 saves (written
  with every elevator dynamic_cost 0) migrate by deriving it on load, and any
  v2 mismatch, including all zeros, is rejected atomically. A genuine save
  from 50ceca0 is kept as tests/fixtures/p14-03/legacy_rush_v1.
- A guest aboard a cabin no longer decides, leaves or despawns until the
  elevator sets it down at a real landing; previously a rider told to leave
  vanished from a moving cabin.
- test_route_wait_choice now proves the rush detour and route loss with a
  worker who actually walks, every shared rider's arrival cell, service
  elevator and stair congestion, legacy migration and a new-PID restart.
- Register completion markers for the seven suites added this session and
  bring the P14 ledger in line with P14-02's completion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Takeshi
2026-10-20 16:51:41 +09:00
co-authored by Claude Opus 5.5
parent 62d1dcecad
commit a8edd3c3a1
15 changed files with 562 additions and 113 deletions
@@ -3723,8 +3723,8 @@
"path": "docs/spec/acceptance/P08-03.json",
"scope": "normal placement; native and OS pending"
}
}
,{
},
{
"test": "tests/ui/test_p23_life_observation.gd",
"template": "P23 life observation: <checks> checks, <failures> failures",
"required": true,
@@ -3789,6 +3789,125 @@
"path": "docs/plans/original-completion-2026-10-02/execution/p13-02/CODE1_TASK_SPEC.json",
"scope": "new backend suite marker; native execution pending"
}
},
{
"test": "tests/mvp/test_whole_tower_outage.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "dd786e8c7448eeb77d51d7effd9100059ae6febf638ca2c20f3a55683009c9ea",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 41,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
}
]
},
{
"test": "tests/s2/test_mana_recovery_new_pid.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "8ef67859db2a31213585fa49dfc687ea69bf215c41c8d1d8cbb3f84d00dd5d17",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 26,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
}
]
},
{
"test": "tests/s2/test_mana_reserve_cycle.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "048d79e067cc85eef1c5c5f3e412198e652a2acc08e6d910e0761c7148a34cb6",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 64,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if failures == 0 else \"FAIL\", checks])"
}
]
},
{
"test": "tests/s2/test_mana_reserve_new_pid.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "9f42e4f7f5aac403ee4a6091e54ebf3995e25d11a450000e5a8ce870094f7a6b",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 26,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
}
]
},
{
"test": "tests/s2/test_runtime_mana_reserve.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "323750a533f2bbdf95b96b1615996cd3c4155bdadc6dcfb0936eaaa19a04809b",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 25,
"text": "\t\tprint(\"PASS: %d checks\" % _checks)"
}
]
},
{
"test": "tests/transport/test_route_wait_choice.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "c4db1db2e60f1a24fbbcb6c2c3706a7ee1534c7191f612e1d8df40d0a39a97bc",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 49,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
}
]
},
{
"test": "tests/transport/test_runtime_warp.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "cfbb69e63aca12d9d198393fd853c76be63df95e8d1e777e7a14194dbd181234",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
},
"source_marker_lines": [
{
"line": 40,
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
}
]
}
],
"unknown_roster_suite_policy": "refuse, explicitly review its source/schema before accepting",
@@ -1,29 +1,47 @@
# P14-03 混雑と経路選択:証拠(候補 0754f41)
# P14-03 混雑と経路選択:証拠
作業内容は「客用・業務用EV、階段、ワープの待ち時間をA*費用へ反映する」です。受入条件は「ラッシュ、相乗り、50%減速、救護同伴、経路喪失を、実際の位置と人数で検証する」です。
## A*費用に入るもの
候補は、実装 `0754f41` に次の修正を加えた正確版です。正確な commit は ACCEPTANCE.json に記録します。
- 保存契約 v2
- 乗車中の客の退館修正
- 試験の強化
| 移動手段 | 費用の内訳 |
## 製品の変更
1. **EVの待ち時間を shaft 全体で数える。** 同じ籠で別の階の組を待っている人も、1人あたり1分(ORTHOGONAL_MOVE_COST)として各接続の `dynamic_cost` に公開します。更新するのは、乗車の要求・取消の時と、籠の毎分処理の時です。同じ階の組の行列、満員の籠1回あたり2分、移動階数×階あたり時間は、これまでどおり `estimate_elevator_wait_cost` が数えます。
2. **保存契約を v2 にする。** ElevatorService の snapshot version を 1 から 2 に上げます。
- v1 は旧版の保存で、昇降機の `dynamic_cost` は常に0でした。v1 は0のときだけ受理し、読込後に待ち時間を導出します。
- v2 は、行列から導いた値との完全一致を必須にします。全0を含めて、一致しなければ原子的に拒否します。
- 旧版 `50ceca0` が書き出した本物の保存(`tests/fixtures/p14-03/legacy_rush_v1`)で移行を確認しました。
3. **乗車中の客は籠の中で判断・退館しない。** 乗車中(BOARDED)の客は、籠が有効な階に降ろすまで判断も退館もしません。試験で再現した欠陥は、乗車中に退館を決めた客が乗車中のまま消えるものでした(1094分)。修正後は1115分に1階で降車し、そのあと退館します。
## A*費用
| 移動手段 | 内訳 |
|---|---|
| EV | 基本費用と移動階数×階あたり時間に、待ち時間を足します。待ち時間は2つの和です。1つは同じ階の組の待ち行列(1人1分と、満員の籠1回ごとに2分)。もう1つは今回追加した「同じ籠で別の階の組を待つ人数×1分」で、各接続の dynamic_cost として公開します。行列が変わるたび(乗車の要求・取消・籠の毎分処理)に更新し、保存を読むときは行列から再計算した値と一致するかを検証します。 |
| 階段 | 費用150です。待ち時間はありません。移動は半速(2分)です。セルの混雑費用が足されます。 |
| ワープ | 費用400です。到着先セルの混雑費用(定員を超えた1人ごとに75)が足されます。停止中や残量不足のときは経路から除外します(P14-02)。 |
| 通路・セル | 定員を超えた1人ごとに75を足します。2人以上が重なると移動速度は50%になります。 |
| EV(客用・業務用) | 基本費用+移動階数×階あたり時間+同じ組の待ち行列+shaft の他の組の待ち人数×1分 |
| 階段 | 費用150(移動は半速の2分)+セルの混雑費用 |
| ワープ | 費用400+到着先セルの混雑費用。停止中・残量不足のときは除外 |
| 通路 | 定員を超えた1人ごとに75。2人以上が重なると移動速度は50% |
## 証拠
## 証拠(`tests/transport/test_route_wait_choice.gd`。full は119 checks、`-- writer` は39、`-- reader` は7)
| 観点 | 試験 |
| 観点 | 確認したこと |
|---|---|
| ラッシュ | `test_route_wait_choice`: 実際の客12人が籠Aで3階行きを待つと、0→1階の経路は空いている籠Bへ迂回します。籠Aの0→1階の接続は、12人分の待ちを費用に含みます。取消後は、その分の費用がちょうど消えます。保存・読込で完全に一致し、待ち費用を改ざんした保存は原子的に拒否されます。`test_shift_rush`: シフト交代時の通勤ラッシュです。 |
| 相乗り | `test_route_wait_choice`: 実際の客6人が籠Bで同乗し、同時に乗るのは定員4人以内です。6人全員が運ばれ、消えた客はいません。 |
| 50%減速 | `test_path_congestion`: 同じ経路の5人で定員を超える密度を作り、半速、2分目までに必ず進むこと、混雑した経路を避けることを確認します。 |
| 救護同伴 | `test_rescue_elevator`: 救護者と患者の2人組が同じ籠を予約し、乗車中に保存・読込しても一致し、停電時は2人一緒に降ります。ワープでの2人分課金はコードで確認済みです(P14-02 レビュー)。 |
| 経路喪失 | `test_route_wait_choice`: 籠Bを撤去すると、経路は残る籠Aに切り替わります。`test_path_congestion` と `test_runtime_path_traffic`: 30分動けない状態からの安全な回復と、構造の再構築を確認します。 |
| ワープの混雑 | `test_route_wait_choice`: 出口に客6人がいると、ワープ経路のA*費用が上がります。 |
| ラッシュ | 実際の客12人が籠Aで3階行きを待つと、0→1階の経路は籠Bへ迂回します。籠Aの0→1階の接続は12人分の待ちを費用に含みます。登録した従業員が実際に歩き、籠Bに乗り、籠Aには乗らずに目的地へ着きます。取消後は、待ち費用がちょうど消えます。 |
| 保存・再開 | v2 の保存は完全に往復します。v2 の待ち費用を0や300に改ざんした保存は、原子的に拒否されます。旧版 v1 の本物の保存は移行し、v2 として往復します。v1 に非0値を入れた改ざんは拒否されます。別PIDで再開すると、同じ従業員が同じ物理経路を歩き、全状態の digest が一致します。 |
| 相乗り | 実際の客6人が目的地を持ち、Runtime 自身が乗車を要求します。同時に乗るのは定員4人以内です。全員の乗車が終わり、乗車中に退館を指示された1人を除く5人は、存在したまま1階の乗り場 (4,1,0) にいます。退館を指示された1人も、1階で降車してから退館します。 |
| 経路喪失 | 籠Bを撤去すると、経路は籠Aに切り替わります。実際に歩く従業員も籠Aで目的地へ着きます。撤去後の状態も保存・往復します。 |
| 業務用EV | 裏層の業務用 shaft は従業員専用です。実際の従業員4人が並ぶと、A*費用が上がります。 |
| 階段 | 階段で2階をつなぎ、上の乗り場に客6人がいると、階段経路の費用が上がります。 |
| ワープ | 出口に客6人がいると、ワープ経路の費用が上がります。 |
| 50%減速 | `test_path_congestion` で、同じ経路の5人が定員を超えると半速になり、2分目までに必ず進み、混雑した経路を避けることを確認します。 |
| 救護同伴 | `test_rescue_elevator` で、2人組が同じ籠を予約し、乗車中の保存・読込が完全に一致し、停止時は2人一緒に降りることを確認します。ワープの2人分課金は P14-02 のレビュー(コード確認)です。 |
## 主張しないこと
- 階段の費用150(2分)とEVの階あたり10分の釣り合いは変えていません。近くに階段があればEVより優先されます。
- 階段(150、2分)とEV(階あたり10分)の費用の釣り合いは変えていません。近くに階段があればEVより優先されます。
- ワープ到着先の定員4は、混雑費用以外では扱いません。
- Windows 実機は対象外です。
- 退館中の客は位置に関係なく次の分に消えます(既存の仕組み。P06-02 の非主張)。今回の修正は、乗車中だけに限ったものです。
- Windows 実機は、適用ありの未検証として残します。
@@ -243,7 +243,7 @@
"facility_value_contracts_validation.json": "bf490a9ca585c48fc318e76c4a069ef244827d828d46e6415399f7710590dd16",
"performance_audit.json": "0b630b15a392c3e0366f1362bfe4ddacb968f915f54549f82e4d6fa315384762",
"progress_contract.json": "ccbdfce8c8365d5c13f59531955708834cce45acd0e136561c95295dfdb5df77",
"progress_snapshot.json": "2b3fc779aa0a9e182d4e45571f415c7e48bee0c7c2229e21a771c73a9ec52835",
"progress_snapshot.json": "faae14c8b560bcdf681cf17055797aea38207d25a9e2a3b77aef0d81cc49d983",
"progress_validator_guard_checks.json": "72ee513fcf1cad85c24bfd0a4b472f425f6acf117928ece0f6b41e29018da627",
"progress_validator_guard_checks.r01.json": "14c26adffa5a1938241db4912e50ea54b660641ee6fee2e412d467dce14d0519",
"requirement_coverage.json": "03b10e4da367579db502d0671a6e0dda36fd385111c6f1c0084acaff7605f593",
@@ -253,6 +253,6 @@
"validate_facility_value_contracts.py": "91640e2a76cd6cc9186f7b85c0bf379b24b6cac0d48962815235ee628e7ae43e",
"validate_plan.py": "4973d3c2797289d266f5c6e7c36f3d2c31931b23a1022eab3d94328267bcc1b4",
"validator_update_checks.json": "f997d0f3de2cea29550840b1d5cb23647d64f81329de46758551e2b6c8fecc27",
"work_packages.json": "a9964db9d8e73b2fe8e4d524668bfc8a6c7a50ac81cc7000f172ea743faef0a3"
"work_packages.json": "5b5481c60b40f20bef4eb1886f97d8ba9b6adbf879c3562a4207c08ae3bf75a6"
}
}
@@ -156,8 +156,8 @@
},
"current_delivery_state_counts": {
"completed": 21,
"in_progress": 19,
"planned": 48
"in_progress": 20,
"planned": 47
},
"completed_current_deliveries": [
"P00-01",
@@ -8258,7 +8258,7 @@
},
"legacy_state": "planned",
"state_scope": "package-level; completed means whole package accepted; partial implementation is not completion",
"current_summary": "採用済み1299参照は初回独立レビューと同一。初回に確認した失敗時保存保護、正常順序・型・所有権・保存復元・経路、1200Kernel ticksの全状態・イベント一致、同版172回帰14636checks/0を維持する。追加証拠で20,000セル・200客・50職員・150施設、既存施設要求200・物流要求200・イベント50の同一Operations OFF状態を採用前後で復元し、snapshot/manual/autosaveを各5試行、各64checks/0として確認。平均snapshotは75.985→74.624ms、manualは425.955→445.803ms、autosaveは280.757→297.499ms。手動保存平均19.848ms、自動保存平均16.741msの実測増加を保持し、ゼロ回帰とは扱わない。採用後保存最大515.903msは既存5秒予算内。P14-01の性能回帰測定不足B01を解消し、この成果単位のみ正式完了可と判定する。5試行の保存・snapshot測定に限定し、simulation、Operations ON、描画、全P28・全P14・全原子・完成素材・全体完成へ拡張しない。Windowsは適用ありの未検証を保持し、通常OS全受入も未完のままとする。 P14-02/03・全P14・Windows全体受入は未完。",
"current_summary": "採用済み1299参照は初回独立レビューと同一。初回に確認した失敗時保存保護、正常順序・型・所有権・保存復元・経路、1200Kernel ticksの全状態・イベント一致、同版172回帰14636checks/0を維持する。追加証拠で20,000セル・200客・50職員・150施設、既存施設要求200・物流要求200・イベント50の同一Operations OFF状態を採用前後で復元し、snapshot/manual/autosaveを各5試行、各64checks/0として確認。平均snapshotは75.985→74.624ms、manualは425.955→445.803ms、autosaveは280.757→297.499ms。手動保存平均19.848ms、自動保存平均16.741msの実測増加を保持し、ゼロ回帰とは扱わない。採用後保存最大515.903msは既存5秒予算内。P14-01の性能回帰測定不足B01を解消し、この成果単位のみ正式完了可と判定する。5試行の保存・snapshot測定に限定し、simulation、Operations ON、描画、全P28・全P14・全原子・完成素材・全体完成へ拡張しない。Windowsは適用ありの未検証を保持し、通常OS全受入も未完のままとする。 P14-02は2026-10-20に正式完了(8917c19、A07/A08)。P14-03は実装中、全P14・Windows全体受入は未完。",
"remaining_deliverables": [
{
"id": "P14-01",
@@ -8337,13 +8337,8 @@
],
"progress_summary": "A07表層マナワープ(客用)とA08裏層業務用ワープ(従業員用)を通常建設・経路・課金・停止・保存へ接続。建設済み同種ワープを全組接続し1歩移動、出発確定時に1人3マナを一回課金。残量不足・停電・到着封鎖で経路除外し費用・移動なし。別PID再開で費用・移動の二重反映なし。独立レビューr1/r2 PASS(8917c19)。Windows・完成素材・描画目視は対象外。",
"remaining_gaps": [
"通常建設できる2x2 FRONTのワープ施設と原本/承認値の費用・工期等の定義",
"通常経路選択から対象と出口の所有/予約/実位置へ接続する処理",
"承認済み費用確定規約と二重計上拒否",
"マナ不足/機能停止/到着不能の原状保持",
"途中保存・別PID再開で費用と移動が二重反映されない証拠",
"通常UI、影響回帰、性能、完成素材の適用範囲と独立レビュー",
"適用する同一配布版のWindows実機条件"
"完成素材と描画の目視確認(非主張)",
"同一配布版のWindows実機条件(非主張)"
],
"updated_at_jst": "2026-10-20T00:00:00+09:00",
"completion_acceptance": {
@@ -8351,15 +8346,24 @@
"sha256": "ac7bfd6fb88cb8ac8c195c8051ebe22ba95a6a6393342de089aae88fdcc73307",
"candidate": "8917c19",
"owner_approval": "delegated_by_user 2026-10-20「承認するかどうかの判断はそちらでやって」"
}
},
"remaining_gaps_at_completion": "2026-10-20に解消済み(execution/p14-02-warp/ACCEPTANCE.json)。完成素材・描画目視・Windows実機は非主張として残す。"
},
{
"id": "P14-03",
"title": "混雑と経路選択",
"work": "客用・業務用EV、階段、ワープの待ち時間をA*費用へ反映する。",
"acceptance": "ラッシュ、相乗り、50%減速、救護同伴、経路喪失を実位置と人数で検証する。",
"state": "planned",
"evidence": []
"state": "in_progress",
"evidence": [],
"progress_summary": "実装0754f41(shaft全体の待ちをA*費用へ)+保存契約v2(旧v1は待ちを導出して移行、v2改ざんは全0含め原子拒否)+乗車中の客が籠内で退館しない修正。test_route_wait_choiceで実客・実歩行によるラッシュ迂回、相乗り定員内と全員の到着位置、経路喪失、業務EV・階段・ワープ出口の混雑、旧版保存移行、別PID再開を確認中。正式全回帰・性能・独立レビュー前。",
"remaining_gaps": [
"正確候補での正式全回帰(tools/run_all_tests.py)",
"代表負荷の性能回帰計測",
"独立Opusレビュー",
"Windows実機(適用あり未検証として残す)"
],
"updated_at_jst": "2026-10-20T00:00:00+09:00"
}
],
"estimate_person_days_historical_2026_09_29": [