diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/S2_ROOT_RUN.md b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/S2_ROOT_RUN.md new file mode 100644 index 0000000..05ce134 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/S2_ROOT_RUN.md @@ -0,0 +1,85 @@ +# P04-01 S2a Root run(code-ready提出用・実測未実施) + +原P04の完成加点は0。独立したwhole受入の前段の準備資料であり、D05、whole受入、独立レビュー、統合を確定しない。 +Rootは新しいcode-readyの提出が出るまでengineを起動しない。 +workerはGodot起動(parse、--check-only、--versionを含む)、S1再実行、全回帰、commitを行わない。 + +## 入力の確認 + +`BUILD_OUTPUT_DIR/audit-support/p04-whole-closure-r01/current-patterns/` の供給ファイルを直接読んだ結果は以下のとおり。指定値に全一致した。 + +| ファイル | bytes | SHA256 | +| --- | ---: | --- | +| paid-shift-midnight-formal-r01.gd | 7667 | 4c56ff05b1bcf8775bb526a30ed021a4a2630ea4f360a5075f880806b052e6d1 | +| p04-paid-shift-midnight-native-r01.py | 6580 | 23bf2e241c7828b6533aa8107b3d33e962d5c53300a71b198e6fe371e0d4f4f0 | +| p04-paid-shift-midnight-formal-native-r01.py | 7765 | e3b6eab32dd4fe3dfbff7ae9b67b34dd674236018bdb6bc7c64da25898ff5ed6 | + +`SUPPLEMENT_SOURCE_MANIFEST_R02.json`を読み、上記3ファイルと合わせてwrapperが直接pinする。旧SOURCE_MANIFESTに名前がないことを欠落とは解釈しない。 +paid-shift-midnight / formal / idempotency-paid-formalの各GDと対応native Python、ROOT_S1_NATIVE_R02_RESULT.json、既存S1の3ファイルもpinする。 +Root S1結果のSource1338、owned engine4終了は履歴資料でありS2aの実行結果へ転用しない。 + +セットアップは原paid-shift-midnight-r01.gdの開始から23:59までと_setup_paid_receptionを機械的に抽出して保持した。 +wrapperは毎回templateを直接読み、両ブロックのliteral一致を確認する。公的hire、DAY/NIGHT、原catalog賃金、原施設価格・サービス時間、材料1つを変更しない。 +厨房を1439で閉じる処理を省くことだけがシナリオ差分。公開helperを継承し、snapshot内部への書込みはない。 + +## Root手順 + +既定製品rootは `/Users/korizatou114/Projects/とりにく工房ゲーム制作部`。 +以下のBUILD_OUTPUT_DIRは `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`。 +製品rootのfresh source planを作成し、nativeを1回だけ実行する。既存のplan/outputは上書き・削除しない。 +失敗時は最初の失敗で停止し、自動再試行しない。再実行はRootの判断と新しいplan/outputを必要とする。 + +```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/s2/run_s2a_native.py --check-only --plan "$BUILD_OUTPUT_DIR/p04-s2a-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s2a-native-r01" +python3 docs/plans/original-completion-2026-10-02/execution/p04-01/s2/run_s2a_native.py --plan "$BUILD_OUTPUT_DIR/p04-s2a-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s2a-native-r01" +``` + +wrapperのパスはこの提出checkoutのものを使う。実行cwdに依存して製品sourceを選ばず、既定product rootを使用する。 +workerのcheckout向けcheck-only planはRootの製品planへ転用しない。 +check-onlyはwrapperのmodeであり、Godot起動0、default/private profile読取0。sourceと固定資料、cache receiptだけを読む。 +cache receiptが読めない場合は実際のpath/errorをplanに記録し、native admissionは拒否する。 + +## ケースと証拠 + +Writerはseed424242の独立fresh Runtimeを3回作り、各branchを1回ずつ実施する。 + +- C1 midnight_sale_payroll_upkeep:原のclock advanceで1440にserviceを完了し、同じadvanceでDay1決算。実sale transaction IDをservice.paymentのIdempotency記録でservice IDへjoinし、request ID・予約IDと照合する。WAGEのsource_entity_idを実payroll settlement IDへjoinし、各paid rowのstaff_idとsettlement.day(1始まり)をShiftInstance.staff_id/start_day(0始まり)へjoinする。取引IDをdistinctで数え、行数・勤務分数を取引数の代わりにしない。 +- C2 same_minute_recall:別fresh stateから1440へ到達した直後、current_minute==1440で公開advance_to(1440)を1回呼ぶ。ok/code/payloadとCommandResult全体を記録し、全typed snapshot、ledger、payroll、day_summary、shifts不変を検査。request再送や1443からの過去呼出しで代用しない。 +- C3 formal_restore:別fresh stateからC1と同じ境界に到達し、公的SaveServiceで1slotを保存。別PID/profileのReaderが4member全コピーとSHA一致、公的load/restore、typed snapshot完全一致、ledger/payroll/day_summary一致を確認。復元直後の1440でadvance_to(1440)を再度呼び、実結果と再決算0を採取する。 + +勤務分は全shiftのbefore/after、worked_minutesとovertime_minutes差分を保持する。 +孤立予約0は対象serviceの予約と実予約行のrequester_id/purpose/CONSUMEDを照合し、ACTIVE facility_service予約は原requestのreserved_inventory_idsへ照合する。未知stateは失敗する。 +sale/payroll/upkeep/dutyは全ledgerと正式snapshotに加えjoin行に保持する。停止条件はないため、1440の売上・給与・維持費ledgerを「停止中課金ledger0」と判定しない。「停止中課金0」は実停止条件を満たす別probeの受入条件として残る。 + +出力は `BUILD_OUTPUT_DIR/p04-s2a-native-r01/`。rawの各観測はJSON(閲覧用)と.var(型保持)の対で保存する。FAILも原値のまま残す。 +seed、入力、日、minute、実取引/service/request/予約ID、勤務差分、ledger全行、commands、全Runtime snapshot、operations/Idempotency、upkeep accrual/settlementを保持する。 +Writer各branchのbefore/midnight/join/branch、C2 recall、Reader C3 branch/recall、phase receiptを保存する。 +private profile内のWriter/Reader formal package(journal.log、manifest.json、thumbnail.png、world.bin)を保持する。 +source_before/after、selected-source-map、runner-input、cache-reuse、parse-paths、contracts、各phase raw logs/receipts、退避combined-raw.log、PID-set、outer-owned-finally、execution、runner-ended、raw-inventoryも全保存する。 + +## strict guard + +S1 wrapperの手順を同順序で複製:source_refs → 明示QA1 → 別inodeのprivate source/cache → import1 → parse1 → Writer → 別PID Reader → owned finally → 全guard。 + +- fresh output、exact check-only plan、全source pin、固定資料pin、供給3pinと補足manifest、literal setup一致 +- passed終了済cache receiptと製品selected source map完全一致、cache全hash、cp -c別inode +- 他engine不在、既存disk admission、既存watchdog/policy/diagnostic/ownership/audio/marker gate +- 製品source/default player不変、private source全体不変、原cache不変 +- Writer/Readerの実PIDとreceipt一致、別PID/profile、4phase/4owned PID、全engine owned終了 +- formal全member hash一致、Writer原slot不変(Reader失敗時もguard)、typed完全一致 +- finallyでcaptured owned process cleanup。cleanup/fixed guard失敗も失敗receiptに保持 + +exit0でも原P04の完成ではない。native未実行のため、実測checks数・case結果を予測で埋めない。 +worker検証はpy_compileとwrapper --check-onlyだけ。Godot parseはRootの正式nativeのparse phaseで行う。 + +worker静的確認:py_compile成功。wrapper --check-only成功、engine0・private profile読取0、source checkout1300件・QA1・固定pin17件。 +終了済cacheの `runner-ended.json` はOSからPermissionError(Operation not permitted)で読取拒否された。 +planはその実path/errorをcache_accessへ記録した。Root製品1338件の確認は代行していない。 +Rootは製品rootでfresh planを作り、cache receiptを自身の権限で確認する。worker planは実runへ転用しない。 + +## 残件 + +S2bの夜番不在と残業は勤務生成と時刻条件が別系統なので分ける。 +本提出の実測、元の停止条件の別probe、独立whole受入・レビュー・必要OS証拠・統合はRoot側の後続。 diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/p04_s2a_midnight_probe.gd b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/p04_s2a_midnight_probe.gd new file mode 100644 index 0000000..7545f0a --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/p04_s2a_midnight_probe.gd @@ -0,0 +1,317 @@ +extends "res://tests/s3/test_runtime_actor_hardening.gd" +var _mode := "" +var _slots: Dictionary = {} +var _branches: Dictionary = {} +var _commands: Array = [] + +func _run() -> void: + _evidence_dir = OS.get_environment("THE_TREE_S2A_EVIDENCE") + _mode = OS.get_environment("THE_TREE_S2A_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 ["C1", "C2", "C3"]: _writer_branch(branch) + else: _reader_branch() + _write_var("branches", _branches) + var receipt := {&"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} + var file := FileAccess.open(_evidence_dir.path_join(_mode + ".json"), FileAccess.WRITE) + _expect(file != null, "phase receipt") + if file != null: file.store_string(JSON.stringify(receipt, "\t")); file.flush(); file.close() + _cleanup() + print("S2a midnight: %d checks, %d failures" % [_checks, _failures]) + quit(1 if _failures else 0) + +func _writer_branch(branch: String) -> void: + _case = _mode + "-" + branch + _serial = 0; _expected_daily_wages = 0; _commands.clear(); _observations.clear() + _prepare_midnight() + var before := _capture() + _write_var("before", before) + _expect(before[&"active_reservation_orphans"] == 0, "pre-midnight active reservation ownership") + _expect(_clock.absolute_minute == 1439 and _runtime.operations_coordinator.current_minute == 1439, "fresh branch before midnight") + # Only scenario change: omit dependency closure. The ordinary clock performs + # completion and daily close in the same advance. No snapshot mutation. + _clock.advance_one_minute() + var after := _capture() + var joined := _join(before, after) + _write_var("midnight", after); _write_var("join", joined) + _expect(_clock.absolute_minute == 1440 and _runtime.operations_coordinator.current_minute == 1440, "midnight reached") + _expect(after[&"service"][&"completed_at"] == 1440 and joined[&"sales"].size() == 1, "actual one midnight service sale") + _expect(joined[&"sale_join_complete"], "sale transaction idempotency joins actual service and reservations") + _expect(int(after[&"day_summary"].get(&"closed_at", -1)) == 1440, "same advance closes Day 1") + _expect(joined[&"payroll_transaction_ids"].size() == 1 and joined[&"payroll_join_complete"], "actual payroll transaction joins paid Staff and day shifts") + _expect(not joined[&"upkeep_rows"].is_empty(), "actual upkeep observed") + _expect(joined[&"orphans"] == 0, "reservation orphans zero") + var row := {&"before": before, &"midnight": after, &"join": joined} + if branch == "C2": row[&"recall"] = _recall() + if branch == "C3": + var path := "user://saves/p04_s2a/C3" + var saved := SaveService.save_snapshot(path, after[&"snapshot"]) + var inspected := SaveService.inspect_snapshot(path) + row[&"save_result"] = saved + _expect(bool(saved.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "formal SaveService save/inspect") + _expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(after[&"snapshot"]) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(after[&"snapshot"]), "save preserves entire typed state") + _slots["C3"] = ProjectSettings.globalize_path(path) + _branches[branch] = row; _write_var("branch", row) + +func _capture() -> Dictionary: + var ledger: Array = [] + for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary()) + var reservations: Array = [] + for reservation: InventoryReservation in _runtime.inventory_service.get_reservations(): reservations.append(reservation.create_snapshot()) + var service: ServiceInstance = _runtime.facility_service.get_service(_service_id) + var staff: Dictionary = _runtime.staff_service.create_snapshot() + var active_orphans := 0 + for reservation: Dictionary in reservations: + if InventoryReservation.State.keys().find(String(reservation[&"state"])) < 0: active_orphans += 1 + if reservation[&"state"] != "ACTIVE" or reservation[&"purpose"] != "facility_service": continue + var request: ServiceRequest = _runtime.facility_service.get_request(StringName(reservation[&"requester_id"])) + if request == null or not request.reserved_inventory_ids.has(StringName(reservation[&"reservation_id"])): active_orphans += 1 + if service != null and not service.is_terminal(): + for id: StringName in service.inventory_reservations: + var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id) + if reservation == null or not reservation.is_active() or reservation.requester_id != service.request_id: active_orphans += 1 + return {&"seed": 424242, &"day": _clock.absolute_minute / 1440 + 1, &"minute": _clock.absolute_minute, + &"inputs": {&"facility_id": _facility_id, &"service_id": _service_id, &"request_id": _request_id, &"guest_id": _guest_id, &"original_price": _original_price}, + &"snapshot": _runtime.create_snapshot(), &"ledger": ledger, &"reservations": reservations, &"active_reservation_orphans": active_orphans, + &"service": service.create_snapshot() if service != null else {}, &"staff": staff, + &"payroll": staff[&"payroll_settlements"], &"shifts": staff[&"shifts"], + &"day_summary": _runtime.operations_coordinator.get_day_summary(1), + &"idempotency": {&"commands": _runtime.command_queue.create_snapshot(), &"economy": _runtime.economy_service.create_snapshot()[&"idempotency"]}, + &"upkeep": {&"accrual": _runtime.facility_upkeep.create_snapshot(), &"settlement": _runtime.upkeep_settlement.create_snapshot()}, + &"operations": _runtime.operations_coordinator.create_snapshot(), + &"commands": _commands.duplicate(true), &"setup_observations": _observations.duplicate(true)} + +func _recall() -> Dictionary: + var before := _capture() + _expect(_runtime.operations_coordinator.current_minute == 1440, "recall starts at current minute 1440") + var result: CommandResult = _runtime.operations_coordinator.advance_to(1440) + var command := {&"ok": result.is_success(), &"code": CommandResult.Code.keys()[result.code], &"payload": result.details.duplicate(true), &"result": result.to_dictionary()} + var after := _capture() + var row := {&"before": before, &"command": command, &"after": after, &"join": _join(before, after)} + _write_var("recall", row) + _expect(result.is_success() and result.is_replay and int(result.details.get(&"boundary_counts", {}).get(&"daily_closes", -1)) == 0, "actual replay result has zero daily closes") + _expect(var_to_bytes(before[&"snapshot"]) == var_to_bytes(after[&"snapshot"]), "same-minute public recall entire typed state unchanged") + for key: StringName in [&"ledger", &"payroll", &"day_summary", &"shifts"]: + _expect(var_to_bytes(before[key]) == var_to_bytes(after[key]), "no duplicate settlement " + String(key)) + _expect(row[&"join"][&"orphans"] == 0, "recall reservation orphans zero") + return row + +func _join(before: Dictionary, after: Dictionary) -> Dictionary: + var sales: Array = [] + var upkeep: Array = [] + var transactions: Dictionary = {} + var payroll_join: Array = [] + var complete := true + var sale_complete := true + var shift_delta: Array = [] + for shift: Dictionary in after[&"shifts"]: + var prior: Dictionary = {} + for old: Dictionary in before[&"shifts"]: + if old[&"shift_instance_id"] == shift[&"shift_instance_id"]: prior = old + shift_delta.append({&"before": prior, &"after": shift, &"worked_minutes_delta": int(shift[&"worked_minutes"]) - int(prior.get(&"worked_minutes", 0)), &"overtime_minutes_delta": int(shift[&"overtime_minutes"]) - int(prior.get(&"overtime_minutes", 0))}) + for entry: Dictionary in after[&"ledger"]: + if entry[&"category"] == "SERVICE_SALE" and entry[&"source_entity_id"] == String(_facility_id) and entry[&"credit_account"] == "GUEST_WALLET:" + String(_guest_id): + var payment_id: StringName = _runtime.economy_service.get_transaction_id_for_idempotency_key(StringName("service.payment.%s" % _service_id)) + sale_complete = sale_complete and String(payment_id) == entry[&"transaction_id"] and int(entry[&"absolute_minute"]) == 1440 and int(entry[&"amount"]) == _original_price + sales.append({&"ledger": entry, &"service_id": _service_id, &"reservation_ids": after[&"service"].get(&"inventory_reservations", [])}) + if entry[&"category"] == "MAINTENANCE_PAYMENT": upkeep.append(entry) + if entry[&"category"] != "WAGE": continue + var found := false + for settlement: Dictionary in after[&"payroll"]: + if settlement[&"settlement_id"] != entry[&"source_entity_id"]: continue + found = true + transactions[entry[&"transaction_id"]] = true + for paid: Dictionary in settlement[&"rows"]: + if int(paid[&"daily_wage"]) <= 0: continue + var shifts: Array = [] + # Settlement day is 1-based; ShiftInstance.start_day is 0-based. + for shift: Dictionary in after[&"shifts"]: + if shift[&"staff_id"] == paid[&"staff_id"] and int(shift[&"start_day"]) + 1 == int(settlement[&"day"]): shifts.append(shift) + complete = complete and not shifts.is_empty() + payroll_join.append({&"transaction": entry, &"settlement": settlement, &"paid_staff": paid, &"day": settlement[&"day"], &"shifts": shifts, &"service_id": _service_id, &"sale_rows": sales, &"upkeep_rows": upkeep, &"duty_delta": shift_delta}) + complete = complete and found + var orphans := 0 + var service: Dictionary = after[&"service"] + if service.get(&"inventory_reservations", []).is_empty(): orphans += 1 + for id: Variant in service.get(&"inventory_reservations", []): + var found := false + for reservation: Dictionary in after[&"reservations"]: + if reservation[&"reservation_id"] == String(id): + found = true + if reservation[&"requester_id"] != service[&"request_id"] or reservation[&"purpose"] != "facility_service" or reservation[&"state"] != "CONSUMED": orphans += 1 + if not found: orphans += 1 + for reservation: Dictionary in after[&"reservations"]: + if InventoryReservation.State.keys().find(String(reservation[&"state"])) < 0: orphans += 1 + if reservation[&"state"] == "ACTIVE" and reservation[&"purpose"] == "facility_service": + var request: ServiceRequest = _runtime.facility_service.get_request(StringName(reservation[&"requester_id"])) + if request == null or not request.reserved_inventory_ids.has(StringName(reservation[&"reservation_id"])): orphans += 1 + return {&"sales": sales, &"sale_join_complete": sale_complete and not sales.is_empty(), &"payroll_transaction_ids": transactions.keys(), &"payroll_join": payroll_join, + &"payroll_join_complete": complete and not payroll_join.is_empty(), &"upkeep_rows": upkeep, &"duty_delta": shift_delta, &"orphans": orphans} + +func _reader_branch() -> void: + _case = "reader-C3" + var file := FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ) + _expect(file != null, "Writer receipt available") + 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(), "distinct Reader PID/profile") + var original := String(writer["slots"]["C3"]) + var target := ProjectSettings.globalize_path("user://saves/p04_s2a/C3") + _expect(DirAccess.make_dir_recursive_absolute(target) == OK, "Reader fresh slot") + var directory := DirAccess.open(original) + _expect(directory != null, "Writer slot available") + if directory == null: return + var members := directory.get_files(); members.sort() + _expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "all formal package members") + var hashes: Dictionary = {} + for member: String in members: + var sha := FileAccess.get_sha256(original.path_join(member)) + _expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy formal member") + hashes[member] = {&"original_before": sha, &"copy": FileAccess.get_sha256(target.path_join(member))} + _expect(sha == hashes[member][&"copy"], "member SHA matches") + file = FileAccess.open(_evidence_dir.path_join("writer-C3-midnight.var"), FileAccess.READ) + _expect(file != null, "Writer typed point") + if file == null: return + var expected: Dictionary = bytes_to_var(file.get_buffer(file.get_length())); file.close() + _reset_runtime(424242) + var loaded := SaveService.load_snapshot(target) + _expect(bool(loaded.get(&"ok", false)), "formal public load") + if not bool(loaded.get(&"ok", false)): return + _expect(_runtime.restore_snapshot(loaded[&"snapshot"]), "public Runtime restore") + _facility_id = StringName(expected[&"inputs"][&"facility_id"]); _service_id = StringName(expected[&"inputs"][&"service_id"]) + _original_price = int(expected[&"inputs"][&"original_price"]) + var actual := _capture() + _expect(var_to_bytes(actual[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "formal entire typed snapshot equality") + for key: StringName in [&"ledger", &"payroll", &"day_summary"]: _expect(var_to_bytes(actual[key]) == var_to_bytes(expected[key]), "formal equality " + String(key)) + var recall := _recall() + for member: String in members: + hashes[member][&"original_after"] = FileAccess.get_sha256(original.path_join(member)) + _expect(hashes[member][&"original_before"] == hashes[member][&"original_after"], "original member unchanged") + _slots["C3"] = target + _branches["C3"] = {&"expected": expected, &"restored": actual, &"recall": recall, &"member_hashes": hashes} + _write_var("branch", _branches["C3"]) +var _observations: Array[Dictionary] = [] +var _receipts: Array[Dictionary] = [] +var _first_receipts: Array[Dictionary] = [] +var _receipt_hook: Callable +var _evidence_dir := "" +var _facility_id: StringName = &"" +var _service_id: StringName = &"" +var _request_id: StringName = &"p04.order.midnight.request" +var _guest_id: StringName = &"p04.order.midnight.guest" +var _staff_ids: Array[StringName] = [&"p04.midnight.server.a", &"p04.midnight.server.b"] +var _serial := 0 +var _case := "midnight" +var _original_price := 0 +var _observation_pass := "first" +var _first_summary: Dictionary = {} +var _paid_reception_id: StringName = &"" +var _expected_daily_wages := 0 + +# Setup below is literal paid-shift-midnight-r01.gd through 23:59, pinned by wrapper. +func _prepare_midnight() -> void: + _reset_runtime(424242) + var enabled: Dictionary = _runtime.enable_production_operations() + _expect(bool(enabled.get(&"ok", false)), "public production operations enabled at original Day 1 06:00") + if not bool(enabled.get(&"ok", false)): return + var facility := _build_cafeteria(true) + if facility == null: return + _facility_id = facility.instance_id + var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id) + _original_price = definition.base_price + _setup_paid_reception() + var cook_id := StringName("fixture.kitchen.worker.%s" % _facility_id) + _expect(_cmd(&"set_staff_work_assignment", {&"staff_id": cook_id, &"template_id": &"NIGHT", &"facility_id": facility.related_facility_id}).is_success(), "public original NIGHT assignment replaces inherited 23:59 cook fixture") + 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 workplace Staff " + String(id)) + _expect(_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"NIGHT", &"facility_id": _facility_id}).is_success(), "original NIGHT 18:00-next06:00 assignment " + String(id)) + var queue_at := 1440 - definition.base_service_minutes - 1 + _expect(queue_at > _clock.absolute_minute, "setup finishes before original service start") + if queue_at <= _clock.absolute_minute: return + _clock.advance_minutes(1079 - _clock.absolute_minute) + _observe("paid-before-handover-17-59") + _expect(_runtime.facility_service.get_instance(_paid_reception_id).active_staff_ids.has(&"staff_akane"), "normally hired daytime receptionist physically attends before original DAY boundary") + _clock.register_tick_handler(_clock.TickPhase.FACILITY_COMPLETION, &"zz.p04.paid.before-handover", func(minute: int): + if minute == 1080: + _observe("paid-at-completion-before-handover-18-00") + _expect(_runtime.facility_service.get_instance(_paid_reception_id).active_staff_ids.has(&"staff_akane"), "last DAY interval retains actual hired worker before handover") + ) + _clock.advance_one_minute(); _observe("paid-after-handover-18-00") + _expect(not _runtime.facility_service.get_instance(_paid_reception_id).active_staff_ids.has(&"staff_akane"), "daytime hired receptionist is removed after completion phase") + _clock.advance_minutes(queue_at - _clock.absolute_minute) + _expect(_runtime.facility_service.get_instance(_paid_reception_id).active_staff_ids.has(&"staff_bardo"), "normally hired NIGHT replacement physically arrives") + _expect(_clock.absolute_minute == queue_at and _runtime.operations_coordinator.current_minute == queue_at, "real production operations advance without blocked suffix") + if _clock.absolute_minute != queue_at: return + 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, "physical ordinary NIGHT workplace arrival " + String(id)) + var kitchen: FacilityInstance = _runtime.facility_service.get_instance(facility.related_facility_id) + var cook: StaffRuntime = _runtime.staff_service.get_staff(cook_id) + _expect(_runtime.staff_service.requires_attendance(cook_id) and StaffCommuteService.is_at_facility(cook, kitchen), "real kitchen NIGHT attendance, not legacy attendance bypass") + _expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.midnight.stock"), "one real meal to reserve") + _expect(_cmd(&"spawn_guest", {&"guest_id": _guest_id, &"guest_type_id": &"traveler", &"gold": _original_price * 2, &"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO}}).is_success(), "public actual Guest wallet") + _expect(_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": _request_id, &"guest_ids": [_guest_id]}).is_success(), "actual request original price/duration") + _clock.advance_one_minute() + var active: Array = _runtime.facility_service.get_active_services(_facility_id) + _expect(active.size() == 1, "one actual reserved service starts") + if active.size() != 1: return + var service: ServiceInstance = active[0]; _service_id = service.service_id + _expect(service.started_at + service.duration_minutes == 1440 and service.duration_minutes == definition.base_service_minutes and service.price_snapshot == _original_price, "original service completion coincides with 00:00") + _clock.advance_minutes(1439 - _clock.absolute_minute) + _expect(service.elapsed_minutes == service.duration_minutes - 1, "one original service minute remains at 23:59") + _expect(_runtime.operations_coordinator.get_day_summary(1).is_empty(), "real Day 1 not closed early") + +func _setup_paid_reception() -> void: + var definition: FacilityDefinition = _runtime.definition_registry.get_facility(&"reception") + _expect(_cmd(&"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(2, 0, WorldService.LAYER_FRONT)}).is_success(), "paid public reception construction") + _clock.advance_minutes(definition.build_minutes) + for instance: FacilityInstance in _runtime.facility_service.get_instances(): + if instance.definition_id == &"reception": _paid_reception_id = instance.instance_id + _expect(not _paid_reception_id.is_empty(), "reception completes using unchanged construction duration") + var catalog: Dictionary = JSON.parse_string(FileAccess.get_file_as_string("res://data/characters/mvp_staff_candidates.json")) + for candidate_id: StringName in [&"staff_akane", &"staff_bardo"]: + var original_wage := -1 + for row: Dictionary in catalog["entries"]: + if StringName(row["id"]) == candidate_id: original_wage = int(row["daily_wage"]) + _expect(original_wage > 0 and _cmd(&"hire_staff_candidate", {&"candidate_id": candidate_id}).is_success(), "public hire of existing paid candidate " + String(candidate_id)) + var worker: StaffRuntime = _runtime.staff_service.get_staff(candidate_id) + _expect(worker != null and worker.get_effective_daily_wage() == original_wage, "existing catalog wage unchanged for " + String(candidate_id)) + _expected_daily_wages += original_wage + _expect(_cmd(&"set_staff_work_assignment", {&"staff_id": candidate_id, &"template_id": &"DAY" if candidate_id == &"staff_akane" else &"NIGHT", &"facility_id": _paid_reception_id}).is_success(), "ordinary recurring paid DAY or NIGHT assignment") + +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, &"ok": result.is_success(), &"result": result.to_dictionary()}) + return result + +func _observe(label: String) -> void: + label = _case + "-" + _observation_pass + "-" + label + 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 paid: Array = [] + for paid_id: StringName in [&"staff_akane", &"staff_bardo"]: + var hired: StaffRuntime = _runtime.staff_service.get_staff(paid_id) + if hired != null: paid.append(hired.create_snapshot()) + var reception: FacilityInstance = _runtime.facility_service.get_instance(_paid_reception_id) + _observations.append({&"label": label + "-paid-actors", &"minute": _clock.absolute_minute, &"paid_staff": paid, &"paid_reception": reception.create_snapshot() if reception != null else {}, &"expected_catalog_wages": _expected_daily_wages}) + var workers: Array = [] + for id: StringName in _staff_ids: + var worker: StaffRuntime = _runtime.staff_service.get_staff(id) + var shift: ShiftInstance = _runtime.staff_service.get_shift(worker.shift_id) + workers.append({&"staff": worker.create_snapshot(), &"shift": shift.create_snapshot() if shift != null else {}, &"is_present": _runtime.staff_commute.is_present(worker, facility), &"requires_attendance": _runtime.staff_service.requires_attendance(id), &"at_facility": StaffCommuteService.is_at_facility(worker, facility), &"within_shift": shift.contains_minute(_clock.absolute_minute) if shift != null else false}) + _observations.append({&"label": label, &"minute": _clock.absolute_minute, &"workers": workers, &"facility_state": facility.state, &"power_available": facility.power_available, &"active_staff": facility.active_staff_ids.duplicate(), &"service": service.create_snapshot() if service != null else {}, &"request_present": _runtime.facility_service.get_request(_request_id) != null, &"wallet": _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(_guest_id))), &"available_meals": _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal"), &"reservations": _runtime.inventory_service.create_snapshot(), &"guest": guest.create_snapshot() if guest != null else {}, &"receipts": _receipts.duplicate(true), &"day_summary": _runtime.operations_coordinator.get_day_summary(1), &"operations": _runtime.operations_coordinator.create_snapshot()}) + _write_var(_observation_pass + "-" + label.trim_prefix(_case + "-" + _observation_pass + "-"), _runtime.create_snapshot()) + +func _write_var(label: String, value: Variant) -> void: + var file := FileAccess.open(_evidence_dir.path_join(_case + "-" + label + ".var"), FileAccess.WRITE) + _expect(file != null, "write whole typed evidence " + label) + if file != null: file.store_buffer(var_to_bytes(value)); file.flush(); file.close() + var json_file := FileAccess.open(_evidence_dir.path_join(_case + "-" + label + ".json"), FileAccess.WRITE) + _expect(json_file != null, "write JSON mirror alongside authoritative typed snapshot") + if json_file != null: json_file.store_string(JSON.stringify(value, "\t")); json_file.flush(); json_file.close() + diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/run_s2a_native.py b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/run_s2a_native.py new file mode 100644 index 0000000..64f2991 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s2/run_s2a_native.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +"""S2a-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_s2a_midnight_probe.gd" +ENGINE = Path("/Applications/Godot.app/Contents/MacOS/Godot") +PATTERNS = BUILD / "audit-support/p04-whole-closure-r01/current-patterns" +SUPPLIED = { + "paid-shift-midnight-formal-r01.gd": (7667, "4c56ff05b1bcf8775bb526a30ed021a4a2630ea4f360a5075f880806b052e6d1"), + "p04-paid-shift-midnight-native-r01.py": (6580, "23bf2e241c7828b6533aa8107b3d33e962d5c53300a71b198e6fe371e0d4f4f0"), + "p04-paid-shift-midnight-formal-native-r01.py": (7765, "e3b6eab32dd4fe3dfbff7ae9b67b34dd674236018bdb6bc7c64da25898ff5ed6"), +} +MATERIALS = [PATTERNS / n for n in [ + "paid-shift-midnight-r01.gd", "paid-shift-midnight-formal-r01.gd", + "idempotency-paid-formal-r01.gd", "p04-paid-shift-midnight-native-r01.py", + "p04-paid-shift-midnight-formal-native-r01.py", "p04-idempotency-paid-formal-native-r01.py", + "SUPPLEMENT_SOURCE_MANIFEST_R02.json", "ROOT_S1_NATIVE_R02_RESULT.json", +]] + [HERE.parent / "s1" / n for n in ["run_s1_native.py", "p04_s1_ev_power_probe.gd", "S1_ROOT_RUN.md"]] + +def template_guard(): + """Read literal setup from supplied template, including original public helpers.""" + for name, (size, digest) in SUPPLIED.items(): + actual = pin(PATTERNS / name) + if (actual["bytes"], actual["sha256"]) != (size, digest): + raise ValueError("supplied input mismatch: " + json.dumps(actual)) + supplement = json.loads((PATTERNS / "SUPPLEMENT_SOURCE_MANIFEST_R02.json").read_text()) + for name in SUPPLIED: + actual = pin(PATTERNS / name) + if not any(Path(row["supplied"]) == PATTERNS / name and + (row["bytes"], row["sha256"]) == (actual["bytes"], actual["sha256"]) + for row in supplement["files"]): + raise ValueError("supplement pin mismatch: " + name) + original = (PATTERNS / "paid-shift-midnight-r01.gd").read_text() + probe = QA.read_text() + setup = original[original.index("func _run_midnight()"):original.index('\t_expect(_cmd(&"set_facility_policy", {&"facility_id": kitchen.instance_id')] + setup = setup.replace("func _run_midnight() -> void:", "func _prepare_midnight() -> void:") + hire = original[original.index("func _setup_paid_reception()"):original.index("func _reopen_dependency")] + if setup not in probe or hire not in probe: + raise ValueError("probe must retain literal original setup and public paid hire") + +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-s2a-native-r01") + parser.add_argument("--plan", type=Path, default=BUILD / "p04-s2a-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() + r = module("s2a_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() + 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 / "S2_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-s2a-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(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first") + def fixed_guard(): + template_guard() + 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("s2a_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_S2A_EVIDENCE=str(out / "raw"), THE_TREE_S2A_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("s2a_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="S2a midnight: 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("s2a_diagnostics", r.SUPPORT / "diagnostic_gate.py") + owners = module("s2a_owners", r.SUPPORT / "ordinary_proof_gate.py") + audio = module("s2a_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) == 1, "one C3 formal writer point") + 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, + "S2a actual outcome receipt") + r.require(sorted(receipt["branches"]) == (["C1", "C2", "C3"] if phase == "writer" else ["C3"]), + "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)