P04-01: 同時発生の更新規約
This commit is contained in:
@@ -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側の後続。
|
||||
+317
@@ -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()
|
||||
|
||||
@@ -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> checks, <failures> 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)
|
||||
Reference in New Issue
Block a user