From cc3a377841a2ca44d32a91122365fc9bcbdef341 Mon Sep 17 00:00:00 2001 From: Agent Control Runner Date: Fri, 16 Oct 2026 04:14:48 +0900 Subject: [PATCH] =?UTF-8?q?P04-01:=20=E5=90=8C=E6=99=82=E7=99=BA=E7=94=9F?= =?UTF-8?q?=E3=81=AE=E6=9B=B4=E6=96=B0=E8=A6=8F=E7=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../execution/p04-01/s2b/S2B_ROOT_RUN.md | 98 ++++ .../p04-01/s2b/p04_s2b_shift_probe.gd | 522 ++++++++++++++++++ .../execution/p04-01/s2b/run_s2b_native.py | 274 +++++++++ 3 files changed, 894 insertions(+) create mode 100644 docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/S2B_ROOT_RUN.md create mode 100644 docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/p04_s2b_shift_probe.gd create mode 100644 docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/S2B_ROOT_RUN.md b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/S2B_ROOT_RUN.md new file mode 100644 index 0000000..c879446 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/S2B_ROOT_RUN.md @@ -0,0 +1,98 @@ +# P04-01 S2b code preparation + +変更は本ディレクトリの `p04_s2b_shift_probe.gd`、`run_s2b_native.py`、本書の3ファイルのみ。 +S1 r02、S2a native r03(ROOT_S2A_NATIVE_R03_RESULT.json と build/p04-s2a-native-r03/)は保持し、再実行しない。 +原15条件+EV、7領域、3安全結果、原30/88、部分点なしを維持する。本提出による完成加点は0。 +原P04全体の達成、D05、独立レビュー、統合は確定しない。 + +## 構成と固定入力 + +QA overrideは `tests/s3/test_runtime_staff_assignment.gd` の1件だけ。 +単一GDがWriter内で2回fresh Runtime/Clock/GameStateを生成する。Readerも保存点ごと、replay開始時にfresh生成する。 +型推論によるVariant受取を避け、GDの変数宣言は明示型。state snapshotはString定義と比較する。 + +- night_absent: supplied `approved-shift-current-r01.gd` のseed420410、2 DAY servers、実通勤、元のcafeteria/kitchen fixtureを使用。 + queue_atだけ `boundary + 2 - base_service_minutes - 1` にし、予定完了を1082へ移す。 + service duration/priceは定義値、Guest planned_exitは元の `boundary + base_service_minutes` のまま。 + kitchenの物理配置済FLEX cookは既存診断fixtureの前提であり、自然雇用の証拠と混同しない。 +- overtime: `tests/supply/test_supply_logistics_bridge.gd:40-79` のseed653322、warehouse/kitchen/staff_door施工、 + catalogue Gina公開hire/DAY assignment、実通勤、購入8、公開request、実pickupから_loadedを得る。 + synthetic dispatch rejection、expiry snapshot変更、fault injectionは採用しない。 + 実taskのinterrupt_level==0を確認し、元のtarget cellだけ一時blockして通常clockを進める。 + +ROOT_S2B_EXISTING_SOURCE_AUDIT.jsonの8ファイルは毎回bytes/SHAを選択rootと照合する。 +供給18:00 GD、監査JSON、S1/S2a wrapperと関連文書/GD、本3ファイルをplanでpinする。 +inherit先hardening/kitchen fixtureを含む選択root全sourceもpinする。 +旧source checkout1300のcheck-onlyは製品1338の代替証拠にしない。 + +## 境界判定 + +night_absentでは1079を基準に1080/1081で未完了、wallet/player cash/ledger無変更、同じ非空予約IDとACTIVE予約内容、 +元requestの所有を検査する。DAYの退出とNIGHT登録0を確認し、1081でWAITING_FOR_STAFFを必須にする。 +退出予定式と元5分graceも検査する。完了を早めたり、元のduration、price、shift値を変更しない。 + +overtimeでは1078→1079にworked+1/overtime+0、1079→1080と1080→1081はworked+0/overtime+1を検査する。 +各tickの総勤務差分は1。正常勤務分と残業分を両方+1と誤読しない。 +同じstaff/task/shift/request/cargo所有、8個のcargo、crosslinks、is_overtimeを実オブジェクト/snapshotで検査する。 +各1079/1080/1081のtick後に公開 `StaffService.advance_one_minute(current_minute)` を1回呼び、 +実戻り値MINUTE_ALREADY_APPLIEDと全Runtime bytes不変で二重勤務記録なしを確認する。 +private `_update_shift_states` と `record_duty_minute` は呼ばない。 +解除後は各tickを保存し、safe unloading→元task/request COMPLETED→OFF_DUTY、8個移送と所有解放を確認する。 + +## Formal save / Reader + +両ケースの1079/1080/1081を公開SaveServiceで保存・inspectする(合計6slot)。 +Writer全Runtime snapshotとinspect snapshotのvar_to_bytes完全一致、保存時のlive Runtime不変を必須とする。 +Readerは別PID/profileで各slotのjournal.log/manifest.json/thumbnail.png/world.binを全コピーし、 +原本/コピーの全SHAと、load前後/replay後の不変を検査する。余分なmember/directoryは失敗。 +公開load/restore後はWriterの全Runtimeをvar_to_bytesで比較する。項目の削除、JSON正規化による一致化はしない。 + +Readerはさらに各1079 slotをfresh loadして通常clockの2tickを再現し、1080/1081全Runtimeと勤務差分、 +同分guardを検査する。FACILITY_COMPLETION前後とAI_DECISIONS後のphase順と全snapshotもWriterに一致させる。 +overtimeはunblock後のCOMPLETED/OFF_DUTYまで全Runtime一致を追加確認する。 +raw Dictionary getterと保存用canonical Operationsは別々に保存する。getter joinだけStringName/Stringとkey順差を明示し、 +型・値・欠落・配列順を検査する。全Runtime typed条件は緩和しない。 + +## Rootの正式実行 + +Rootだけが製品1338でfresh plan/nativeを実行する。workerはGodot起動(parse/version含む)、Runner操作、全回帰、git書込みをしない。 +以下は本提出checkoutからRootが実行する手順。既存output/planを上書きせず、失敗時は最初のphaseで停止し自動再試行しない。 + +```sh +export BUILD_OUTPUT_DIR='/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build' +export PYTHONDONTWRITEBYTECODE=1 +python3 docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py --check-only --plan "$BUILD_OUTPUT_DIR/p04-s2b-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s2b-native-r01" +python3 docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py --plan "$BUILD_OUTPUT_DIR/p04-s2b-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s2b-native-r01" +``` + +製品root既定値は `/Users/korizatou114/Projects/とりにく工房ゲーム制作部`。 +native admissionはこのrootかつsource1338を必須にする。worker checkout planをnativeへ転用しない。 +check-onlyはsource/material/cache receiptのみ読取り、engine0、default/private profile読取り0。 +cache receiptの読取拒否はplanに実エラーを残し、nativeは拒否する。Rootは自身の環境で確認する。 + +S1/S2aと同じsource_refs→QA1→private source stage→終了済cacheの別inode clone→import→parse→Writer→Reader→ +owned finally cleanup→全guardを維持する。source/default player/private source/original cache不変、 +固定pin、実PIDとreceipt、4phase/4owned PIDの終了、正式slot不変は失敗時にも検査する。 +通常policy、watchdog、strict marker/diagnostic/ownership/audio gateを変更しない。 + +## 必須出力と残件 + +rawにはseed/input、実command結果(継承fixture分もcommand_completedで採取)、minute、実staff/task/shift/request/service ID、 +reservation/cargo、全ledger、全Runtime、phase観測、duty差分、同分guard結果、save/load結果をJSONと型保持.varの対で残す。 +Writerのloaded、1078-1079-duty、各1079/1080/1081とsave、phase、branch、unload、Readerの全restored/replayとSHA表を保持する。 +正式6slotはWriter/Reader private profileに保持する。 +source_before/after、selected-source-map、runner-input、cache-reuse、parse-paths、contracts、各phaseログ/receipt、 +combined-raw.log、PID-set、outer-owned-finally、execution、runner-ended、raw-inventoryも保持する。 +obsolete 500000-byte capに合わせた省略・圧縮はしない。 + +worker検証範囲はpy_compile、wrapper --check-only、GDのVariant推論宣言grepのみ。 +GDScript parse、製品1338 nativeの条件成立、formal replay一致は未実行であり合格を主張しない。 +Root native実測と正式独立レビューが残る。これらは予定された後続作業であり、未実装を外部blockedとは報告しない。 + +worker実施記録: Python py_compile成功。GD grepは `:=` と禁止された2直接呼出しが0件(rg exit1)。 +既定製品rootでのcheck-onlyは `tools/run_all_tests.py` 読取りがPermissionErrorとなりexit2。 +明示worktree rootでのcheck-onlyはexit0、source1300、QA1、固定pin13、engine0。 +終了済cacheのrunner-ended.jsonもPermissionErrorであり、planのcache_accessへ実path/errorを記録した。 +private/default profileの読取りは0。製品1338のcheck-only成功・native成功を主張しない。 +最初のworker planは `build/p04-s2b-worker-check-r01.json`。その後の本書/GD調整を含む最終pin確認には +fresh `build/p04-s2b-worker-check-r02.json` を使用し、r01を上書きしない。 diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/p04_s2b_shift_probe.gd b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/p04_s2b_shift_probe.gd new file mode 100644 index 0000000..69e1094 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/p04_s2b_shift_probe.gd @@ -0,0 +1,522 @@ +extends "res://tests/s3/test_runtime_actor_hardening.gd" + +const REQUEST: StringName = &"supply.bridge_test" +const PORTER: StringName = &"staff_gina" +var _evidence_dir: String = "" +var _mode: String = "" +var _case: String = "" +var _seed: int = 0 +var _serial: int = 0 +var _slots: Dictionary = {} +var _branches: Dictionary = {} +var _commands: Array = [] +var _command_results: Array = [] +var _phases: Array = [] +var _facility_id: StringName = &"" +var _service_id: StringName = &"" +var _request_id: StringName = &"p04.order.shift.request" +var _guest_id: StringName = &"p04.order.shift.guest" +var _staff_ids: Array[StringName] = [&"p04.order.server.a", &"p04.order.server.b"] +var _source: StringName = &"" +var _destination: StringName = &"" +var _loaded: Dictionary = {} +var _task_id: StringName = &"" +var _shift_id: StringName = &"" +var _target: Vector3i = Vector3i.ZERO + +func _run() -> void: + _evidence_dir = OS.get_environment("THE_TREE_S2B_EVIDENCE") + _mode = OS.get_environment("THE_TREE_S2B_MODE") + if _evidence_dir.is_empty() or _mode not in ["writer", "reader"]: + quit(2) + return + _expect(DirAccess.make_dir_recursive_absolute(_evidence_dir) == OK, "evidence directory") + if _mode == "writer": + for branch: String in ["night_absent", "overtime"]: _writer_branch(branch) + else: + _reader() + _write("branches", _branches) + var receipt: Dictionary = {&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), + &"slots": _slots, &"checks": _checks, &"failures": _failures, &"branches": _branches.keys(), + &"whole_goal_accepted": false, &"unit_accepted": false} + _write(_mode, receipt, true) + _cleanup() + print("S2b shift: %d checks, %d failures" % [_checks, _failures]) + quit(1 if _failures else 0) + +func _fresh(seed: int) -> void: + _seed = seed + _serial = 0 + _commands.clear() + _command_results.clear() + _phases.clear() + _facility_id = &"" + _service_id = &"" + _source = &"" + _destination = &"" + _task_id = &"" + _shift_id = &"" + _target = Vector3i.ZERO + _loaded = {} + _reset_runtime(seed) + _runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) + _runtime.command_completed.connect(func(result: CommandResult) -> void: + _command_results.append({&"minute": _clock.absolute_minute, &"result": result.to_dictionary()}) + ) + +func _cmd(kind: StringName, payload: Dictionary) -> CommandResult: + _serial += 1 + var result: CommandResult = _runtime.submit_command(kind, payload, &"player", &"", 10, -1, StringName("p04.order.shift.command.%d" % _serial)) + _commands.append({&"kind": kind, &"input": payload.duplicate(true), &"minute": _clock.absolute_minute, &"result": result.to_dictionary()}) + return result + +func _command(kind: StringName, payload: Dictionary) -> bool: + # Supply fixture retains its original public submit defaults. + var result: CommandResult = _runtime.submit_command(kind, payload) + _commands.append({&"kind": kind, &"input": payload.duplicate(true), &"minute": _clock.absolute_minute, &"result": result.to_dictionary()}) + return result.is_success() + +func _prepare_night() -> void: + _fresh(420410) + var facility: FacilityInstance = _build_cafeteria(true) + if facility == null: return + _facility_id = facility.instance_id + var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id) + for id: StringName in _staff_ids: + _expect(_cmd(&"register_staff", {&"staff_id": id, &"role_id": &"server", &"options": {&"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i.ZERO}}).is_success(), "register real Staff actor " + String(id)) + _expect(_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"DAY", &"facility_id": _facility_id}).is_success(), "original DAY 06:00-18:00 shift " + String(id)) + # Publicly registered Staff actors walk normally. The inherited fixture's + # kitchen cook is explicitly placed at the kitchen and uses its existing FLEX + # shift; this is not natural hiring/unlock or a proof of the whole labor flow. + var boundary: int = ShiftInstance.DAY_END + var queue_at: int = boundary + 2 - definition.base_service_minutes - 1 + _expect(queue_at > _clock.absolute_minute, "real ticks can reach exact admission minute") + _clock.advance_minutes(queue_at - _clock.absolute_minute) + _expect(facility.active_staff_ids == _staff_ids, "both daytime actors are physically projected before service") + for id: StringName in _staff_ids: + var worker: StaffRuntime = _runtime.staff_service.get_staff(id) + _expect(_runtime.staff_service.requires_attendance(id) and StaffCommuteService.is_at_facility(worker, facility) and worker.current_cell != Vector3i.ZERO, "actual workplace arrival and attendance plan " + String(id)) + _expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.order.shift.stock"), "one actual meal to reserve") + _expect(_cmd(&"spawn_guest", {&"guest_id": _guest_id, &"guest_type_id": &"traveler", &"gold": definition.base_price * 2, &"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO, &"planned_exit_at": boundary + definition.base_service_minutes}}).is_success(), "real public Guest actor and wallet") + _expect(_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": _request_id, &"guest_ids": [_guest_id]}).is_success(), "original price and duration service request") + _clock.advance_one_minute() + var active: Array = _runtime.facility_service.get_active_services(_facility_id) + _expect(active.size() == 1, "exactly one actual service starts") + if active.size() != 1: return + var service: ServiceInstance = active[0]; _service_id = service.service_id + _expect(service.duration_minutes == definition.base_service_minutes and service.price_snapshot == definition.base_price, "no price or duration override") + _expect(service.started_at + service.duration_minutes == boundary + 2, "unchanged duration with admission shifted two minutes") + _clock.advance_minutes(boundary - 1 - _clock.absolute_minute) + _expect(service.elapsed_minutes == service.duration_minutes - 3, "three real service minutes remain at 17:59") + +func _prepare_overtime() -> void: + _fresh(653322) + for row: Array in [[&"warehouse", Vector3i(-8, 0, 1)], [&"kitchen", Vector3i(2, 0, 1)], [&"staff_door", Vector3i(0, 0, 1)]]: + _expect(_command(&"build_facility", {&"definition_id": row[0], &"origin": row[1]}), "build %s" % row[0]) + _clock.advance_minutes(180) + for facility: FacilityInstance in _runtime.facility_service.get_instances(): + if facility.definition_id == &"warehouse": _source = facility.instance_id + if facility.definition_id == &"kitchen": _destination = facility.instance_id + if _source.is_empty() or _destination.is_empty(): + _expect(false, "normal construction completes") + return + _expect(_command(&"hire_staff_candidate", {&"candidate_id": PORTER}), "hire the catalogue porter") + _expect(_command(&"set_staff_work_assignment", {&"staff_id": PORTER, &"facility_id": _source, &"template_id": &"DAY"}), "assign porter to a repeating real shift") + _expect(_worker().state == StaffRuntime.State.COMMUTING, "porter assignment begins a real warehouse commute") + for minute in range(40): + if _worker().state == StaffRuntime.State.AVAILABLE: break + _clock.advance_minutes(1) + _expect(_worker().state == StaffRuntime.State.AVAILABLE and StaffCommuteService.is_at_facility(_worker(), _runtime.facility_service.get_instance(_source)), "porter checks in physically before taking a supply job") + _expect(_command(&"purchase_supplies", {&"warehouse_id": _source, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 8}), "purchase the source stock through the ledger") + _expect(_command(&"create_logistics_request", {&"request_id": REQUEST, &"source_inventory_id": _inventory(_source), &"destination_inventory_id": _inventory(_destination), &"item_requirements": {SupplyCatalog.RAW_FOOD: 8}}), "create the reserved supply request") + _expect(_quantity(_inventory(_destination)) == 0, "request creation does not deliver anything") + var check_in_cell: Vector3i = _worker().current_cell + _clock.advance_minutes(1) + _expect(_request().assigned_carrier_id == PORTER and _worker().current_cell.distance_to(check_in_cell) <= 1.0, "dispatcher selects the checked-in employee without teleporting") + _wait_for(LogisticsRequest.State.DELIVERING, 40) + _expect(_request().state == LogisticsRequest.State.DELIVERING and _worker().current_cell.distance_to(check_in_cell) <= 1.0, "physical pickup starts delivery from the checked-in warehouse access without teleporting") + if _request().state != LogisticsRequest.State.DELIVERING: + return + _expect(_quantity(_inventory(_source)) == 0 and _quantity(_cargo()) == 8 and _quantity(_inventory(_destination)) == 0, "pickup moves all eight units into owned cargo only") + _expect(_crosslinks(), "loaded Request, Task, reservations and cargo agree") + _loaded = _runtime.create_snapshot() + + _write("loaded", {&"seed": _seed, &"snapshot": _loaded, &"commands": _commands, &"command_results": _command_results}) + _expect(_task().interrupt_level == 0, "actual loaded task is non-interruptible") + _task_id = _task().task_id + _shift_id = _worker().shift_id + _target = _task().target_cell + _runtime.path_service.set_cell_temporary_block(_target, true) + _clock.advance_minutes(1078 - _clock.absolute_minute) + var before: Dictionary = _capture() + _clock.advance_one_minute() + var after: Dictionary = _capture() + _write("1078-1079-duty", _duty_delta(before, after)) + _expect(_clock.absolute_minute == 1079, "real loaded branch reaches pre-boundary") + +func _wait_for(state: int, minutes: int) -> void: + for index: int in minutes: + if _request().state == state: return + _clock.advance_minutes(1) + + +func _inventory(id: StringName) -> StringName: + return _runtime.facility_service.get_instance(id).local_inventory_id + + +func _request() -> LogisticsRequest: + return _runtime.logistics_requests[REQUEST] + + +func _worker() -> StaffRuntime: + return _runtime.staff_service.get_staff(PORTER) + + +func _task() -> StaffTask: + return _runtime.staff_service.get_task(_worker().current_task_id) + + +func _cargo() -> StringName: + return StringName("cargo:%s" % PORTER) + + +func _quantity(inventory_id: StringName) -> int: + return _runtime.inventory_service.total_quantity(inventory_id, SupplyCatalog.RAW_FOOD) + + +func _crosslinks() -> bool: + return SupplyLogisticsBridge.validate_crosslinks(_runtime.logistics_requests, _runtime.staff_service, _runtime.inventory_service, _runtime.facility_service) + +func _writer_branch(branch: String) -> void: + _case = branch + if branch == "night_absent": _prepare_night() + else: _prepare_overtime() + if _clock.absolute_minute != 1079: + _expect(false, "setup must reach exact pre-boundary minute") + return + _connect_phases() + var before: Dictionary = _capture() + if branch == "overtime": _guard_same_minute() + _save_point("1079", before) + var previous: Dictionary = before + var points: Dictionary = {"1079": before} + for minute: int in [1080, 1081]: + _clock.advance_one_minute() + var actual: Dictionary = _capture() + _expect(actual[&"minute"] == minute, "normal clock advances one boundary tick") + if branch == "overtime": + _write(str(minute) + "-duty", _duty_delta(previous, actual)) + _guard_same_minute() + else: + _assert_night(before, actual) + _save_point(str(minute), actual) + points[str(minute)] = actual + previous = actual + if branch == "overtime": points["unload"] = _unload() + points["phase_observations"] = _phases.duplicate(true) + points["commands"] = _commands.duplicate(true) + points["command_results_including_inherited_fixture"] = _command_results.duplicate(true) + _branches[branch] = points + _write("branch", points) + +func _capture() -> Dictionary: + var snapshot: Dictionary = _runtime.create_snapshot() + var raw_day: Dictionary = _runtime.operations_coordinator.get_day_summary(1) + var canonical_operations: Dictionary = _runtime.operations_coordinator.create_snapshot() + var canonical_day: Dictionary = {} + var day_count: int = 0 + for day: Dictionary in canonical_operations["day_summaries"]: + if int(day["day"]) == 1: + canonical_day = day + day_count += 1 + var getter_differences: Array = [] + _expect(_getter_join(raw_day, canonical_day, "$", getter_differences) and day_count == (0 if raw_day.is_empty() else 1), "raw getter joins canonical save contract without relaxing Runtime equality") + var ledger: Array = [] + for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary()) + var row: Dictionary = {&"seed": _seed, &"minute": _clock.absolute_minute, &"snapshot": snapshot, + &"inputs": {&"case": _case, &"seed": _seed, &"facility_id": _facility_id, &"service_id": _service_id, + &"source": _source, &"destination": _destination, &"task_id": _task_id, &"shift_id": _shift_id, + &"target": _target, &"request_id": _request_id, &"guest_id": _guest_id, + &"boundary": ShiftInstance.DAY_END}, + &"ledger": ledger, &"raw_day_getter": raw_day, + &"canonical_operations": canonical_operations, &"canonical_day1": canonical_day, + &"getter_differences": getter_differences, &"getter_bytes_equal": var_to_bytes(raw_day) == var_to_bytes(canonical_day), + &"commands": _commands.duplicate(true), &"command_results": _command_results.duplicate(true)} + # Raw getters retain their actual StringName keys and insertion order. They + # are diagnostics, never substituted for the entire canonical Runtime save. + if _case == "night_absent": + row[&"inputs"].merge({&"planned_completion": 1082, &"admission_offset": 2, + &"kitchen_premise": "original physically placed FLEX cook; not natural hire"}) + var facility: FacilityInstance = _runtime.facility_service.get_instance(_facility_id) + var service: ServiceInstance = _runtime.facility_service.get_service(_service_id) + var guest: GuestRuntime = _runtime.guest_service.get_guest(_guest_id) + var workers: Array = [] + var night_count: int = 0 + var staff_snapshot: Dictionary = _runtime.staff_service.create_snapshot() + for shift: Dictionary in staff_snapshot[&"shifts"]: + if shift[&"template_id"] == "NIGHT" and not shift[&"cancelled"]: night_count += 1 + for id: StringName in _staff_ids: + var worker: StaffRuntime = _runtime.staff_service.get_staff(id) + workers.append(worker.create_snapshot()) + var reservations: Array = [] + for id: StringName in service.inventory_reservations: + var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id) + _expect(reservation != null, "original service reservation exists") + if reservation != null: reservations.append(reservation.create_snapshot()) + var request: ServiceRequest = _runtime.facility_service.get_request(_request_id) + row.merge({&"facility": facility.create_snapshot(), &"service": service.create_snapshot(), + &"guest": guest.create_snapshot(), &"workers": workers, &"night_staff_count": night_count, + &"reservation_ids": service.inventory_reservations.duplicate(), &"reservations": reservations, + &"request": request.create_snapshot() if request != null else {}, + &"wallet": _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(_guest_id))), + &"player_cash": _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT)}) + else: + row[&"inputs"].merge({&"supply_request_id": REQUEST, &"porter_id": PORTER, &"quantity": 8, + &"staff_premise": "public catalogue Gina hire and real DAY commute"}) + var worker: StaffRuntime = _worker() + var task: StaffTask = _runtime.staff_service.get_task(_task_id) + var shift: ShiftInstance = _runtime.staff_service.get_shift(_shift_id) + row.merge({&"worker": worker.create_snapshot(), &"task": task.create_snapshot() if task != null else {}, + &"shift": shift.create_snapshot() if shift != null else {}, &"request": _request().create_snapshot(), + &"cargo_id": _cargo(), &"cargo_quantity": _quantity(_cargo()), + &"source_quantity": _quantity(_inventory(_source)), &"destination_quantity": _quantity(_inventory(_destination)), + &"inventory": _runtime.inventory_service.create_snapshot(), &"crosslinks": _crosslinks(), + &"reserved_load": _runtime.logistics_dispatcher.get_reserved_load(PORTER)}) + return row + +func _assert_night(before: Dictionary, actual: Dictionary) -> void: + var service: Dictionary = actual[&"service"] + _expect(service[&"state"] != "COMPLETED" and int(service[&"elapsed_minutes"]) < int(service[&"duration_minutes"]), "original service remains unfinished") + _expect(actual[&"night_staff_count"] == 0, "no NIGHT shift registered") + _expect(actual[&"wallet"] == before[&"wallet"] and actual[&"player_cash"] == before[&"player_cash"], "unfinished service charges neither wallet nor player") + _expect(var_to_bytes(actual[&"ledger"]) == var_to_bytes(before[&"ledger"]), "no ledger posting while unfinished at boundary") + _expect(var_to_bytes(actual[&"reservation_ids"]) == var_to_bytes(before[&"reservation_ids"]) and not actual[&"reservation_ids"].is_empty(), "same nonempty original reservation IDs") + _expect(var_to_bytes(actual[&"reservations"]) == var_to_bytes(before[&"reservations"]), "original active reservations unchanged") + _expect(not actual[&"request"].is_empty(), "original request retained") + for reservation: Dictionary in actual[&"reservations"]: + _expect(reservation[&"state"] == "ACTIVE" and reservation[&"requester_id"] == String(_request_id) and reservation[&"purpose"] == "facility_service", "reservation state and original request ownership") + _expect(actual[&"request"][&"reserved_inventory_ids"].has(reservation[&"reservation_id"]), "request still owns service reservation") + _expect(actual[&"facility"][&"active_staff_ids"].is_empty(), "DAY staff have left cafeteria") + for worker: Dictionary in actual[&"workers"]: + _expect(worker[&"state"] == "OFF_DUTY", "DAY worker is off duty") + _expect(int(actual[&"guest"][&"planned_exit_at"]) == 1080 + int(service[&"duration_minutes"]) and int(actual[&"guest"][&"planned_exit_at"]) > 1081, "original planned exit formula; not early exit") + if actual[&"minute"] == 1081: + _expect(service[&"state"] == "WAITING_FOR_STAFF", "night_staff_absent at 1081") + _expect(int(service[&"staff_grace_deadline"]) == int(service[&"interrupted_at"]) + FacilityService.STAFF_REPLACEMENT_GRACE_MINUTES, "original five-minute replacement grace") + +func _duty_delta(before: Dictionary, after: Dictionary) -> Dictionary: + var worked: int = int(after[&"shift"][&"worked_minutes"]) - int(before[&"shift"][&"worked_minutes"]) + var overtime: int = int(after[&"shift"][&"overtime_minutes"]) - int(before[&"shift"][&"overtime_minutes"]) + var minute: int = after[&"minute"] + var expected_overtime: int = 1 if minute >= ShiftInstance.DAY_END else 0 + _expect(worked == 1 - expected_overtime and overtime == expected_overtime, "exactly one duty minute in original normal/overtime bucket") + _expect(after[&"worker"][&"is_overtime"] == (minute >= ShiftInstance.DAY_END), "actual overtime flag at each minute") + for point: Dictionary in [before, after]: + _expect(point[&"task"][&"interrupt_level"] == 0, "actual task interrupt_level zero") + _expect(point[&"worker"][&"current_task_id"] == String(_task_id) and point[&"task"][&"assigned_staff_id"] == String(PORTER), "same Staff/Task pair") + _expect(point[&"worker"][&"shift_id"] == String(_shift_id) and point[&"shift"][&"staff_id"] == String(PORTER), "same real ShiftInstance") + _expect(point[&"worker"][&"carried_inventory_id"] == String(_cargo()) and point[&"cargo_quantity"] == 8 and point[&"destination_quantity"] == 0 and point[&"source_quantity"] == 0, "same eight-unit cargo owner while held") + _expect(point[&"request"][&"assigned_carrier_id"] == String(PORTER) and point[&"request"][&"state"] == "DELIVERING" and point[&"crosslinks"], "loaded request crosslinks remain valid") + return {&"before": before, &"after": after, &"worked_delta": worked, &"overtime_delta": overtime, + &"duty_recorded_once": worked == 1 - expected_overtime and overtime == expected_overtime} + +func _guard_same_minute() -> void: + var before: Dictionary = _runtime.create_snapshot() + var minute: int = _clock.absolute_minute + var result: Dictionary = _runtime.staff_service.advance_one_minute(minute) + var after: Dictionary = _runtime.create_snapshot() + _expect(not bool(result.get(&"ok", true)) and result.get(&"reason") == &"MINUTE_ALREADY_APPLIED", "public same-minute duplicate guard") + _expect(var_to_bytes(before) == var_to_bytes(after), "same-minute guard records no second duty minute; all Runtime fields unchanged") + _write(str(minute) + "-guard", {&"minute": minute, &"result": result, &"before": before, &"after": after}) + +func _unload() -> Dictionary: + _runtime.path_service.set_cell_temporary_block(_target, false) + var observations: Array = [] + for index: int in range(40): + if _request().state == LogisticsRequest.State.COMPLETED: break + _clock.advance_one_minute() + observations.append(_capture()) + var completed: Dictionary = _capture() + _expect(_request().state == LogisticsRequest.State.COMPLETED and completed[&"task"][&"state"] == "COMPLETED", "safe unloading completes original request and task") + _expect(_quantity(_inventory(_destination)) == 8 and _quantity(_cargo()) == 0 and _crosslinks(), "safe unloading conserves all eight units") + _expect(_worker().current_task_id.is_empty() and _worker().carried_inventory_id.is_empty() and _runtime.logistics_dispatcher.get_reserved_load(PORTER) == 0, "completion releases cargo/task/dispatcher ownership") + _clock.advance_one_minute() + var off: Dictionary = _capture() + _expect(_worker().state == StaffRuntime.State.OFF_DUTY and not _worker().is_overtime, "OFF_DUTY after safe completion") + var row: Dictionary = {&"observations": observations, &"completed": completed, &"off_duty": off} + _write("unload", row) + return row + +func _connect_phases() -> void: + _clock.register_tick_handler(_clock.TickPhase.FACILITY_COMPLETION, &"aa.p04.s2b.observe", func(minute: int) -> void: + if minute in [1080, 1081]: _phase("before-facility") + ) + _clock.register_tick_handler(_clock.TickPhase.FACILITY_COMPLETION, &"zz.p04.s2b.observe", func(minute: int) -> void: + if minute in [1080, 1081]: _phase("after-facility") + ) + _clock.register_tick_handler(_clock.TickPhase.AI_DECISIONS, &"zz.p04.s2b.observe", func(minute: int) -> void: + if minute in [1080, 1081]: _phase("after-staff") + ) + +func _phase(label: String) -> void: + var row: Dictionary = {&"label": label, &"minute": _clock.absolute_minute, &"snapshot": _runtime.create_snapshot()} + _phases.append(row) + _write(str(_clock.absolute_minute) + "-" + label, row) + +func _save_point(label: String, point: Dictionary) -> void: + _write(label, point) + var path: String = "user://saves/p04_s2b/" + _case + "-" + label + var result: Dictionary = SaveService.save_snapshot(path, point[&"snapshot"]) + var inspected: Dictionary = SaveService.inspect_snapshot(path) + _expect(bool(result.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "public formal save and inspect") + _expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(point[&"snapshot"]), "formal save retains every typed Runtime field") + _expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(point[&"snapshot"]), "saving does not mutate live Runtime") + _slots[_case + "-" + label] = ProjectSettings.globalize_path(path) + _write(label + "-save", {&"save_result": result, &"inspect_result": inspected}) + +func _write(label: String, value: Variant, global_name: bool = false) -> void: + var name: String = label if global_name else _mode + "-" + _case + "-" + label + var file: FileAccess = FileAccess.open(_evidence_dir.path_join(name + ".var"), FileAccess.WRITE) + _expect(file != null, "typed artifact " + name) + if file != null: + file.store_buffer(var_to_bytes(value)); file.flush(); file.close() + file = FileAccess.open(_evidence_dir.path_join(name + ".json"), FileAccess.WRITE) + _expect(file != null, "JSON artifact " + name) + if file != null: + file.store_string(JSON.stringify(value, "\t")); file.flush(); file.close() + +func _read_point(label: String) -> Dictionary: + var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer-" + _case + "-" + label + ".var"), FileAccess.READ) + _expect(file != null, "Writer typed artifact " + label) + if file == null: return {} + var value: Variant = bytes_to_var(file.get_buffer(file.get_length())) + file.close() + _expect(value is Dictionary, "typed dictionary artifact") + return value if value is Dictionary else {} + +func _restore_inputs(point: Dictionary) -> void: + var inputs: Dictionary = point[&"inputs"] + _seed = int(inputs[&"seed"]) + _facility_id = StringName(inputs[&"facility_id"]) + _service_id = StringName(inputs[&"service_id"]) + _source = StringName(inputs[&"source"]) + _destination = StringName(inputs[&"destination"]) + _task_id = StringName(inputs[&"task_id"]) + _shift_id = StringName(inputs[&"shift_id"]) + _target = inputs[&"target"] + +func _reader() -> void: + var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ) + _expect(file != null, "Writer receipt") + if file == null: return + var writer: Dictionary = JSON.parse_string(file.get_as_text()) + file.close() + _expect(int(writer["pid"]) != OS.get_process_id() and String(writer["profile"]) != OS.get_user_data_dir(), "independent Reader PID/profile") + for branch: String in ["night_absent", "overtime"]: + _case = branch + var points: Dictionary = {} + var hashes: Dictionary = {} + for label: String in ["1079", "1080", "1081"]: + var key: String = branch + "-" + label + var original: String = writer["slots"][key] + var target: String = ProjectSettings.globalize_path("user://saves/p04_s2b/" + key) + _expect(DirAccess.make_dir_recursive_absolute(target) == OK, "fresh Reader slot") + var directory: DirAccess = DirAccess.open(original) + _expect(directory != null, "Writer slot accessible") + if directory == null: return + var members: PackedStringArray = directory.get_files() + members.sort() + _expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "exact four-member formal package") + var member_hashes: Dictionary = {} + for member: String in members: + var sha: String = FileAccess.get_sha256(original.path_join(member)) + _expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy formal member") + var copied_sha: String = FileAccess.get_sha256(target.path_join(member)) + _expect(sha == copied_sha and not sha.is_empty(), "original/copy SHA equality") + member_hashes[member] = {&"original_before": sha, &"copy_before": copied_sha} + hashes[key] = {&"original": original, &"copy": target, &"members": member_hashes} + _slots[key] = target + var expected: Dictionary = _read_point(label) + if expected.is_empty(): return + points[label] = expected + _fresh(int(expected[&"seed"])) + _restore_inputs(expected) + var loaded: Dictionary = SaveService.load_snapshot(target) + _expect(bool(loaded.get(&"ok", false)), "public formal load") + if not bool(loaded.get(&"ok", false)): return + _expect(var_to_bytes(loaded[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all typed saved fields match Writer") + _expect(_runtime.restore_snapshot(loaded[&"snapshot"]), "public Runtime restore") + var actual: Dictionary = _capture() + _expect(var_to_bytes(actual[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all typed Runtime fields match Writer after restore") + _write(label + "-restored", {&"expected": expected, &"actual": actual, &"load_result": loaded}) + if branch == "night_absent" and label != "1079": _assert_night(points["1079"], actual) + # A separate fresh load of the pre-boundary package drives both real ticks. + _fresh(int(points["1079"][&"seed"])) + _restore_inputs(points["1079"]) + var pre: Dictionary = SaveService.load_snapshot(_slots[branch + "-1079"]) + _expect(bool(pre.get(&"ok", false)), "replay public pre-boundary load") + if not bool(pre.get(&"ok", false)): return + _expect(_runtime.restore_snapshot(pre[&"snapshot"]), "replay public restore") + _expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(points["1079"][&"snapshot"]), "replay starts at exact all-field pre-boundary state") + _connect_phases() + var previous: Dictionary = _capture() + if branch == "overtime": _guard_same_minute() + for label: String in ["1080", "1081"]: + _clock.advance_one_minute() + var actual: Dictionary = _capture() + _expect(var_to_bytes(actual[&"snapshot"]) == var_to_bytes(points[label][&"snapshot"]), "two-tick replay entire typed equality " + label) + if branch == "overtime": + _write(label + "-duty", _duty_delta(previous, actual)) + _guard_same_minute() + else: _assert_night(points["1079"], actual) + _write(label + "-replay", actual) + previous = actual + var writer_branch: Dictionary = _read_point("branch") + _expect(var_to_bytes(_phases) == var_to_bytes(writer_branch["phase_observations"]), "same boundary phase order and full typed states") + if branch == "overtime": + var unload: Dictionary = _unload() + for label: String in ["completed", "off_duty"]: + _expect(var_to_bytes(unload[label][&"snapshot"]) == var_to_bytes(writer_branch["unload"][label][&"snapshot"]), "safe unloading replay all typed fields " + label) + for key: String in hashes: + var row: Dictionary = hashes[key] + for member: String in row[&"members"]: + var member_row: Dictionary = row[&"members"][member] + member_row[&"original_after"] = FileAccess.get_sha256(String(row[&"original"]).path_join(member)) + member_row[&"copy_after"] = FileAccess.get_sha256(String(row[&"copy"]).path_join(member)) + _expect(member_row[&"original_before"] == member_row[&"original_after"] and member_row[&"copy_before"] == member_row[&"copy_after"], "original and copied members unchanged after loads/replay") + _branches[branch] = {&"member_hashes": hashes, &"phase_observations": _phases.duplicate(true)} + _write("branch", _branches[branch]) + +func _getter_join(raw: Variant, saved: Variant, path: String, differences: Array) -> bool: + # Only diagnostic getter-to-save joining permits StringName/String and key + # ordering differences. All formal/replay Runtime checks above remain bytes. + if typeof(raw) != typeof(saved): + var text_pair: bool = typeof(raw) in [TYPE_STRING, TYPE_STRING_NAME] and typeof(saved) in [TYPE_STRING, TYPE_STRING_NAME] + differences.append({"path": path, "kind": "StringName_String" if text_pair else "type_mismatch", "raw_type": typeof(raw), "saved_type": typeof(saved)}) + return text_pair and String(raw) == String(saved) + if raw is Dictionary: + var matched: Dictionary = {} + var ok: bool = raw.size() == saved.size() + var raw_order: Array = [] + var saved_order: Array = [] + for key: Variant in saved: saved_order.append(String(key)) + for key: Variant in raw: + var name: String = String(key) + raw_order.append(name) + if matched.has(name) or not saved.has(name): + ok = false + continue + matched[name] = true + if typeof(key) != TYPE_STRING: + differences.append({"path": path + "/" + name, "kind": "key_StringName_String" if typeof(key) == TYPE_STRING_NAME else "invalid_key_type", "raw_type": typeof(key), "saved_type": TYPE_STRING}) + ok = typeof(key) == TYPE_STRING_NAME and ok + ok = _getter_join(raw[key], saved[name], path + "/" + name, differences) and ok + if raw_order != saved_order: differences.append({"path": path, "kind": "key_order", "raw": raw_order, "saved": saved_order}) + return ok + if raw is Array: + if raw.size() != saved.size(): return false + var ok: bool = true + for index: int in range(raw.size()): ok = _getter_join(raw[index], saved[index], path + "/" + str(index), differences) and ok + return ok + return var_to_bytes(raw) == var_to_bytes(saved) + diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py new file mode 100644 index 0000000..f53189b --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2b/run_s2b_native.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +"""S2b-only native entry. --check-only never loads ownership helpers or starts Godot.""" +from pathlib import Path +import argparse +import hashlib +import importlib.util +import json +import os +import shutil +import subprocess +import sys +sys.dont_write_bytecode = True + +HERE = Path(__file__).resolve().parent +BUILD = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build") +PRODUCT = Path("/Users/korizatou114/Projects/とりにく工房ゲーム制作部") +CACHE_RUN = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p18-01-model/.runtime/P18-01-CODE/build/p18-relationship-evidence") +TEST = "tests/s3/test_runtime_staff_assignment.gd" +QA = HERE / "p04_s2b_shift_probe.gd" +ENGINE = Path("/Applications/Godot.app/Contents/MacOS/Godot") +PATTERNS = BUILD / "audit-support/p04-whole-closure-r01/current-patterns" +AUDIT = BUILD / "audit-support/p04-whole-closure-r01/ROOT_S2B_EXISTING_SOURCE_AUDIT.json" +MATERIALS = [PATTERNS / "approved-shift-current-r01.gd", AUDIT] + [ + HERE.parent / folder / name for folder, name in [ + ("s1", "run_s1_native.py"), ("s1", "S1_ROOT_RUN.md"), + ("s2", "run_s2a_native.py"), ("s2", "p04_s2a_midnight_probe.gd"), + ("s2", "S2_ROOT_RUN.md"), + ] +] + +def template_guard(root): + audit = json.loads(AUDIT.read_text()) + for row in audit["source_pins"]: + actual = pin(root / row["path"]) + if (actual["bytes"], actual["sha256"]) != (row["production_bytes"], row["production_sha256"]): + raise ValueError("Root source audit mismatch: " + row["path"]) + probe = QA.read_text() + if ":=" in probe: + raise ValueError("probe must use explicit types; inferred declaration found") + for forbidden in ["._update_shift_states(", ".record_duty_minute("]: + if forbidden in probe: + raise ValueError("unsupported duty mutation: " + forbidden) + +def module(name, path): + spec = importlib.util.spec_from_file_location(name, path) + value = importlib.util.module_from_spec(spec) + spec.loader.exec_module(value) + return value + +def pin(path): + path = Path(path) + if path.is_symlink() or not path.is_file(): + raise ValueError("missing exact input: " + str(path)) + data = path.read_bytes() + return dict(path=str(path), bytes=len(data), sha256=hashlib.sha256(data).hexdigest()) + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--project-root", type=Path, default=PRODUCT) + parser.add_argument("--output", type=Path, default=BUILD / "p04-s2b-native-r01") + parser.add_argument("--plan", type=Path, default=BUILD / "p04-s2b-plan-r01.json") + parser.add_argument("--cache-run", type=Path, default=CACHE_RUN) + parser.add_argument("--godot", type=Path, default=ENGINE) + parser.add_argument("--check-only", action="store_true") + args = parser.parse_args() + root = args.project_root.resolve() + out = args.output.resolve() + if not out.is_relative_to(BUILD.resolve()) or not args.plan.resolve().is_relative_to(BUILD.resolve()): + raise ValueError("plan/output must stay in task build") + r = module("s2b_standard_runner", root / "tools/run_all_tests.py") + # Preserve the reviewed source_refs -> exactly one QA override order. + source_before = r.source_refs(root) + refs = dict(source_before) + refs[TEST] = pin(QA) + template_guard(root) + expected = {n: dict(bytes=p["bytes"], sha256=p["sha256"]) for n, p in refs.items()} + fixed = [pin(p) for p in MATERIALS + [ + QA, Path(__file__), HERE / "S2B_ROOT_RUN.md", + root / "tests/transport/test_runtime_transport.gd", + root / "tests/staff/test_rescue_elevator.gd", + root / "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json", + ]] + selected = dict(root_before=source_before, map={n: p["sha256"] for n, p in refs.items()}, timeout=1200) + cache_note = None + cache_receipts = [] + for leaf in ["runner-ended.json", "runner-input.json"]: + path = args.cache_run / leaf + try: + cache_receipts.append(pin(path)) + except OSError as caught: + cache_note = dict(path=str(path), error=type(caught).__name__ + ": " + str(caught), + required_before_native=True) + break + plan = dict(protocol="p04-s2b-preparation-v1", project_root=str(root), output=str(out), + source_before=source_before, selected_source_map=selected["map"], + QA_overrides=[TEST], fixed_input_pins=fixed, cache_run=str(args.cache_run), + cache_receipts=cache_receipts, cache_access=cache_note, + phases=["import", "parse", "writer", "reader"], + native_engine_launches=0, whole_goal_accepted=False, unit_accepted=False) + # Check-only only reads source/materials and writes the plan in task build. + # It deliberately does not read default-player data, inspect processes or stat Godot. + if args.check_only: + args.plan.parent.mkdir(parents=True, exist_ok=True) + if args.plan.exists(): + r.require(r.read(args.plan) == plan, "existing plan differs; use a fresh plan path") + else: + r.write(args.plan, plan) + print(json.dumps(dict(check_only=True, engines=0, source_files=len(source_before), + QA_overrides=[TEST], fixed_pins=len(fixed), plan=str(args.plan), + plan_sha256=r.sha(args.plan), cache_access=cache_note), ensure_ascii=False)) + return 0 + r.require(root == PRODUCT.resolve() and len(source_before) == 1338, "Root native requires product 1338; worker plan is not native evidence") + r.require(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first") + def fixed_guard(): + template_guard(root) + for value in fixed + cache_receipts: + r.require(pin(value["path"]) == value, "fixed input changed: " + value["path"]) + fixed_guard() + # Native admission is Root-only and fails closed on unreadable cache. + r.require(cache_note is None, "Root must read exact cache receipts: " + str(cache_note)) + old = args.cache_run + r.require(r.read(old / "runner-ended.json")["passed"], "ended cache run must have passed") + old_map = r.read(old / "runner-input.json")["selected_source_map"] + current_map = {n: p["sha256"] for n, p in source_before.items()} + r.require(old_map == current_map, "ended cache selected source must equal current product") + protected = r.default_player(root) + outer = module("s2b_owned_outer", r.SUPPORT / "owned_outer.py") + r.require(not outer.native.helper["live_engines"](), "another Godot owns CPU") + engine = args.godot.resolve() + r.require(engine.is_file(), "Root native Godot path") + r.require(shutil.disk_usage(out.parent).free >= 1024**3 + engine.stat().st_size + 136*1024**2, + "reviewed native disk admission") + r.require(not out.exists(), "fresh output required; preserve previous attempts") + out.mkdir() + stage = out / "stage" + stage.mkdir() + (out / "starters").mkdir() + (out / "bootstrap").mkdir() + phases = [] + previous = {} + error = None + guards = [] + cleanup = [] + cleanup_errors = [] + cache_before = None + formal_before = None + formal_after = None + original_phase = outer.run_phase + def run_phase(argv, folder, timeout, env, **kwargs): + env = dict(env) + if folder.name in ["writer", "reader"]: + env.update(THE_TREE_S2B_EVIDENCE=str(out / "raw"), THE_TREE_S2B_MODE=folder.name) + return original_phase(argv, folder, timeout, env, **kwargs) + outer.run_phase = run_phase + r.write(out / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(), + plan=pin(args.plan), engines_launched=0)) + try: + r.write(out / "source_before.json", source_before) + r.write(out / "selected-source-map.json", refs) + # Private source stage first, then literal reviewed bootstrap. + for name, value in refs.items(): + target = stage / name + target.parent.mkdir(parents=True, exist_ok=True) + r.copy_source(Path(value["path"]), target) + r.require(r.inventory(stage, stage=True) == expected, "whole private source") + for name, value in r.read(r.SUPPORT / "bootstrap-scripts.json").items(): + data = value["text"].encode() + r.require(hashlib.sha256(data).hexdigest() == value["sha256"], "bootstrap pin") + (out / "bootstrap" / name).write_bytes(data) + cache = old / "stage/.godot" + cache_before = r.inventory(cache) + r.require(bool(cache_before), "nonempty ended cache") + for name in cache_before: + src = cache / name + dst = stage / ".godot" / name + dst.parent.mkdir(parents=True, exist_ok=True) + subprocess.run(["/bin/cp", "-c", str(src), str(dst)], check=True) + r.require((src.stat().st_dev, src.stat().st_ino) != (dst.stat().st_dev, dst.stat().st_ino), + "cache clone is a separate inode") + r.require(r.inventory(stage / ".godot") == cache_before, "whole cache clone hashes") + r.write(out / "cache-reuse.json", dict(ended_run=str(old), receipts=cache_receipts, + whole_cache=cache_before, independent_inode=True, + ordinary_import_required=True, ordinary_parse_required=True)) + paths = ["res://" + n for n in sorted(refs) if n.endswith(".gd")] + r.write(out / "parse-paths.json", paths) + markers = module("s2b_markers", root / "tools/strict_test_markers.py") + contracts = markers.load_contracts(root / "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json") + contracts[TEST] = dict(test=TEST, template="S2b shift: checks, failures", required=True) + policy = r.read(r.SUPPORT / "suite-policy.json") + r.require(TEST not in policy["suite_policies"], "no normal suite watchdog/policy override") + r.write(out / "contracts.json", contracts) + r.write(out / "runner-input.json", dict(plan, owner_runner_pid=os.getpid(), + wrapper_sha256=r.sha(__file__), QA=pin(QA), runner_sha256=r.sha(root / "tools/run_all_tests.py"), + default_player_before=protected, fixed_input_pins=fixed, + functional_watchdog_seconds=1200, source_adoption=False)) + diagnostics = module("s2b_diagnostics", r.SUPPORT / "diagnostic_gate.py") + owners = module("s2b_owners", r.SUPPORT / "ordinary_proof_gate.py") + audio = module("s2b_audio", r.SUPPORT / "audio_evidence_gate.py") + public = (stage / "project.godot").read_bytes() + for phase, test in [("import", None), ("parse", None), ("writer", TEST), ("reader", TEST)]: + if phase == "reader": + writer = r.read(out / "raw/writer.json") + formal_before = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()} + r.require(len(formal_before) == 6, "three formal points for each of two fresh branches") + row = r.execute_phase(str(engine), stage, out, phase, test, selected, expected, + previous, policy, contracts, paths, public, protected, root, + outer, markers, diagnostics, owners, audio) + phases.append(row) + # execute_phase uses the same test log name; retain each phase's exact log too. + if test and row.get("log"): + (out / (phase + "-combined-raw.log")).write_bytes((out / row["log"]).read_bytes()) + row["strict_runner_log_path"] = row["log"] + row["log"] = phase + "-combined-raw.log" + r.write(out / (phase + "-actual-phase.json"), row) + print(json.dumps(dict(phase=phase, passed=row["passed"], error=row.get("validation_error"))), flush=True) + r.require(row["passed"], "first strict phase failure: " + phase) + if phase in ["writer", "reader"]: + receipt = r.read(out / ("raw/" + phase + ".json")) + r.require(receipt["checks"] > 0 and receipt["failures"] == 0, + "S2b actual outcome receipt") + r.require(sorted(receipt["branches"]) == ["night_absent", "overtime"], + "exact independent Writer cases and formal Reader case") + r.require(receipt["pid"] == row["actual_pid"], "actual native PID/receipt") + writer = r.read(out / "raw/writer.json") + reader = r.read(out / "raw/reader.json") + r.require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "distinct formal processes") + formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()} + r.require(formal_before == formal_after, "Reader leaves Writer packages unchanged") + except BaseException as caught: + error = type(caught).__name__ + ": " + str(caught) + finally: + cleanup, cleanup_errors = outer.finish_captured(outer.OWNED) + try: + r.write(out / "outer-owned-finally.json", dict(cleanup=cleanup, errors=cleanup_errors)) + except BaseException as caught: + guards.append("finally receipt: " + type(caught).__name__ + ": " + str(caught)) + # Fixed guard always follows ownership cleanup, even if setup or an engine failed. + try: + fixed_guard() + after = r.source_refs(root) + r.write(out / "source_after.json", after) + r.require(after == source_before, "whole product source unchanged") + r.require(r.default_player(root) == protected, "default player unchanged") + if (stage / "project.godot").exists(): + r.require(r.inventory(stage, stage=True) == dict(expected, **previous), "whole private source unchanged") + if cache_before is not None: + r.require(r.inventory(old / "stage/.godot") == cache_before, "original cache unchanged") + if formal_before is not None: + writer = r.read(out / "raw/writer.json") + formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()} + r.require(formal_before == formal_after, "Writer formal packages unchanged after Reader, including failure") + except BaseException as caught: + guards.append(type(caught).__name__ + ": " + str(caught)) + pids = [p.pid for p in outer.OWNED] + passed = (error is None and not guards and not cleanup_errors and len(phases) == 4 + and all(p["passed"] for p in phases) and len(pids) == len(set(pids)) == 4 + and all(p["engine_ended"] for p in cleanup)) + execution = dict(passed=passed, error=error, guard_errors=guards, phases=phases, + actual_captured_PIDs=pids, cleanup=cleanup, cleanup_errors=cleanup_errors, + formal_before=formal_before, formal_after=formal_after, + whole_goal_accepted=False, unit_accepted=False) + r.write(out / "raw-inventory.json", r.inventory(out / "raw")) + r.write(out / "PID-set.json", dict(owner_runner_pid=os.getpid(), engine_PIDs=pids)) + r.write(out / "execution.json", execution) + r.write(out / "runner-ended.json", dict(execution, ended=r.now())) + print(json.dumps(dict(passed=passed, error=error, guard_errors=guards, output=str(out)))) + return 0 if passed else 1 + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as caught: + print(type(caught).__name__ + ": " + str(caught), file=sys.stderr) + raise SystemExit(2)