Add supply auto-purchase and preserve save recovery state

This commit is contained in:
Takeshi
2026-10-02 07:35:03 +09:00
parent 285528b7a3
commit 39f6068461
44 changed files with 2625 additions and 61 deletions
@@ -0,0 +1,262 @@
extends SceneTree
## A real inn from the default 5,000 G / 50 materials. No inserted guests,
## inventory, employees, completed construction, or free resource commands.
var _checks := 0
var _failures := 0
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _ids: Dictionary = {}
var _saw_loaded := false
var _saw_movement := false
var _saw_batch := false
var _save_checked := false
var _last_porter_cell := Vector3i.ZERO
var _save_path := "user://tests/normal_auto_supply_%d/cargo" % OS.get_process_id()
var _main: Control
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_runtime = root.get_node("SimulationRuntime")
_clock = root.get_node("GameClock")
_state = root.get_node("GameState")
_clock.reset(1, 6, 0)
_state.reset()
_runtime.initialize(_clock, _state, 424242)
_clock.set_paused(true)
root.size = Vector2i(1440, 810)
_main = load("res://scenes/main/main.tscn").instantiate() as Control
_main.set("manual_save_path", "user://tests/supply_auto_normal_%d/manual" % OS.get_process_id())
root.add_child(_main)
await process_frame
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_expect(_state.get_resource(&"gold") == 5000 and _state.get_resource(&"materials") == 50, "ordinary starting resources")
for row: Array in [[&"reception", Vector3i(-8, 0, 0)], [&"cafeteria", Vector3i(2, 0, 0)], [&"warehouse", Vector3i(-8, 0, 1)], [&"kitchen", Vector3i(2, 0, 1)], [&"staff_door", Vector3i(0, 0, 1)]]:
if not _command(&"build_facility", {&"definition_id": row[0], &"origin": row[1]}):
_finish()
return
await _advance(180)
_command(&"build_facility", {&"definition_id": &"standard_room", &"origin": Vector3i(-8, 2, 0)})
_command(&"build_facility", {&"definition_id": &"guest_stairs", &"origin": Vector3i(-4, 1, 0)})
await _advance(60)
_command(&"build_facility", {&"definition_id": &"guest_stairs", &"origin": Vector3i(-4, 2, 0)})
await _advance(10)
for x: int in [-5, -6]:
_command(&"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(x, 2, 0)})
await _advance(10)
for facility: FacilityInstance in _runtime.facility_service.get_instances():
_ids[facility.definition_id] = facility.instance_id
for assignment: Array in [[&"staff_akane", &"reception"], [&"staff_gina", &"warehouse"], [&"staff_chika", &"kitchen"], [&"staff_iris", &"cafeteria"], [&"staff_juno", &"cafeteria"]]:
if not _command(&"hire_staff_candidate", {&"candidate_id": assignment[0]}) or not _command(&"set_staff_work_assignment", {&"staff_id": assignment[0], &"facility_id": _ids[assignment[1]], &"template_id": &"DAY"}):
_finish()
return
_expect(_state.get_resource(&"materials") == 4, "all buildings and the staff door fit within the starting material budget")
var warehouse: FacilityInstance = _runtime.facility_service.get_instance(_ids[&"warehouse"])
var cash_before: int = _state.get_resource(&"gold")
_command(&"purchase_supplies", {&"warehouse_id": warehouse.instance_id, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 40})
_expect(_state.get_resource(&"gold") == cash_before - 120 and _runtime.inventory_service.total_quantity(warehouse.local_inventory_id, SupplyCatalog.RAW_FOOD) == 40, "40 raw food costs exactly 120 G and arrives only in the warehouse")
_main.call("_activate_management_mode", &"product")
var auto_panel := _main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel
_expect(auto_panel != null, "normal product UI exposes automatic paid ordering")
if auto_panel == null: _finish(); return
auto_panel.auto_purchase_enabled.button_pressed = true
auto_panel.auto_purchase_target.value = 20 # Explicit player-input fixture; not a default balance value.
auto_panel.auto_purchase_button.pressed.emit()
_expect(_runtime.auto_purchase_plans.get_plan(warehouse.instance_id, SupplyCatalog.RAW_FOOD)[&"enabled"], "normal product controls arm player-selected target20")
_command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": true, &"target_stock": 8})
await _advance(120)
_expect(_saw_movement and _saw_loaded, "an employed porter walks the real route and carries purchased food")
_expect(_saw_batch, "the kitchen starts autonomous production without a guest request")
_expect(_save_checked, "a mid-delivery disk save restores ownership and replays exactly")
_expect(_completed_deliveries(SupplyCatalog.RAW_FOOD) > 0 and _completed_deliveries(SupplyCatalog.BASIC_MEAL) > 0, "both warehouse-to-kitchen and kitchen-to-cafeteria deliveries complete")
_expect(_sales(&"cafeteria") > 0, "natural guests pay for meals made from purchased food")
await _capture()
if OS.get_environment("THE_TREE_SUPPLY_PREVIEW") == "1":
_start_preview()
return
if OS.get_environment("THE_TREE_SUPPLY_CAPTURE_ONLY") == "1":
_finish()
return
for day: int in range(1, 4):
await _advance(360 + 1440 * day - _clock.absolute_minute)
var summaries: Array = _runtime.operations_coordinator.get_day_summaries()
_expect(summaries.size() == day and bool(summaries[-1].get(&"balanced", false)), "business day %d closes with a reconciled ledger" % day)
_expect(_state.get_resource(&"gold") >= 0, "day %d retains cash after real payroll and purchases" % day)
_expect(int(summaries[-1].get(&"arrears_total", -1)) == 0, "day %d pays all real wages without arrears" % day)
_check_auto_financial_close(summaries[-1])
_expect(_auto_order_count() >= 2, "at least two real shortages cause paid automatic orders without another manual restock")
_expect(_sales(&"standard_room") > 0, "the same inn earns lodging revenue")
_expect(_sales(&"reception") == 0, "three days do not depend on artificial reception revenue")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "three-day supply history remains restorable")
var evidence := {"checks": _checks, "failures": _failures, "meal_sales": _sales(&"cafeteria"), "lodging_sales": _sales(&"standard_room"), "raw_deliveries": _completed_deliveries(SupplyCatalog.RAW_FOOD), "meal_deliveries": _completed_deliveries(SupplyCatalog.BASIC_MEAL), "cash": _state.get_resource(&"gold"), "closes": _runtime.operations_coordinator.get_day_summaries(), "supply": _runtime.get_supply_management_report(), "upkeep": _runtime.upkeep_settlement.get_report(), "maintenance_expense": _runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE)}
var evidence_dir := "res://artifacts/supply-auto-chain" if OS.get_environment("THE_TREE_BUSINESS_ACCEPTANCE") == "1" else "res://artifacts/supply-auto-chain"
if not OS.get_environment("THE_TREE_SUPPLY_EVIDENCE_DIR").is_empty():
evidence_dir = OS.get_environment("THE_TREE_SUPPLY_EVIDENCE_DIR")
var evidence_path := ProjectSettings.globalize_path(evidence_dir.path_join("normal-play-report.json"))
DirAccess.make_dir_recursive_absolute(evidence_path.get_base_dir())
var file := FileAccess.open(evidence_path, FileAccess.WRITE)
if file != null:
file.store_string(JSON.stringify(evidence, "\t"))
print("NORMAL_AUTO_PURCHASE_REPORT ", JSON.stringify({"cash": evidence["cash"], "meal_sales": evidence["meal_sales"], "raw_deliveries": evidence["raw_deliveries"], "meal_deliveries": evidence["meal_deliveries"]}))
_finish()
func _auto_order_count() -> int:
var count := 0
for row: Dictionary in _runtime.economy_service.create_snapshot()[&"idempotency"]:
if String(row[&"key"]).begins_with("supply.auto."): count += 1
return count
func _check_auto_financial_close(summary: Dictionary) -> void:
var day := int(summary[&"day"])
var start: int = _runtime.operations_coordinator.get_financial_period_start(day)
var end := int(summary[&"closed_at"])
var purchase_expense := 0
var signing_expense := 0
var auto_expense := 0
var cash_in := 0
var cash_out := 0
for entry: LedgerEntry in _runtime.economy_service.get_entries():
if entry.category != &"INITIALIZATION":
if entry.debit_account == &"PLAYER_CASH": cash_in += entry.amount
if entry.credit_account == &"PLAYER_CASH": cash_out += entry.amount
if entry.absolute_minute < start or entry.absolute_minute >= end: continue
if entry.category == &"STAFF_SIGNING_COST": signing_expense += entry.amount
if entry.category == &"OPERATING_EXPENSE":
purchase_expense += entry.amount
if entry.source_entity_id == _ids[&"warehouse"] and _runtime.economy_service.get_transaction_id_for_idempotency_key(
SupplyAutoPurchasePlans.order_key(_ids[&"warehouse"], SupplyCatalog.RAW_FOOD, entry.absolute_minute)) == entry.transaction_id:
auto_expense += entry.amount
var receipt: Dictionary = SupplyAutoPurchasePlans.receipt_from_ledger(_runtime.economy_service, _ids[&"warehouse"], SupplyCatalog.RAW_FOOD, entry.absolute_minute, entry.transaction_id)
_expect(not receipt.is_empty() and receipt[&"quantity"] * 3 == entry.amount and receipt[&"unit_price"] == 3 and receipt[&"total_cost"] == entry.amount, "actual automatic receipt retains current normal3G quote and unchanged cost/source/category")
_expect(summary[&"category_totals"][&"cost_of_goods"] == purchase_expense + signing_expense, "day%d actual purchase+contract cost exactly reconciles daily cost_of_goods" % day)
_expect(int(summary[&"category_totals"][&"cost_of_goods"]) >= auto_expense, "automatic payment is included in operating expense once")
_expect(_runtime.economy_service.get_balance(&"PLAYER_CASH") == 5000 + cash_in - cash_out, "real initial cash+all ledger flows exactly equals player cash")
var warehouse_row: Dictionary = summary[&"facility_profit_loss"].get(_ids[&"warehouse"], {})
_expect(not warehouse_row.is_empty() and int(warehouse_row.get(&"expenses", 0)) >= auto_expense, "actual warehouse P&L retains automatic expense attribution")
func _advance(minutes: int) -> void:
for index: int in maxi(minutes, 0):
var before: int = _clock.absolute_minute
_clock.advance_minutes(1)
if _clock.absolute_minute != before + 1:
_expect(false, "clock must progress: %s" % _runtime.get_operations_report())
return
var porter: StaffRuntime = _runtime.staff_service.get_staff(&"staff_gina")
if porter != null:
_saw_movement = _saw_movement or porter.current_cell != _last_porter_cell
_last_porter_cell = porter.current_cell
if not porter.carried_inventory_id.is_empty():
_saw_loaded = true
if not _save_checked:
_check_loaded_save()
if _ids.has(&"kitchen"):
_saw_batch = _saw_batch or not _runtime.kitchen_production.get_report(_ids[&"kitchen"]).get(&"active_batch", {}).is_empty()
if index % 120 == 119:
print("SUPPLY_PROGRESS ", _clock.formatted_time(), " meals=", _sales(&"cafeteria"))
await process_frame
func _check_loaded_save() -> void:
_save_checked = true
var saved: Dictionary = _runtime.save_to_path(_save_path)
_expect(bool(saved.get(&"ok", false)), "mid-delivery save writes to an isolated slot")
var before: Dictionary = _runtime.create_snapshot()
var loaded: Dictionary = _runtime.load_from_path(_save_path)
_expect(bool(loaded.get(&"ok", false)), "mid-delivery disk restore: %s" % loaded)
var normalized: Dictionary = _runtime.create_snapshot()
_clock.advance_minutes(5)
var expected: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(normalized), "mid-delivery checkpoint rewinds")
_clock.advance_minutes(5)
_expect(_runtime.create_snapshot() == expected, "mid-delivery replay preserves exact inventory, tasks, positions, and ledger")
_expect(_runtime.restore_snapshot(before), "resume the observed minute after replay verification")
func _completed_deliveries(item: StringName) -> int:
var count := 0
for request: LogisticsRequest in _runtime.logistics_requests.values():
if String(request.request_id).begins_with("supply.") and request.state == LogisticsRequest.State.COMPLETED and request.item_requirements.has(item):
count += 1
return count
func _sales(definition_id: StringName) -> int:
var total := 0
for entry: LedgerEntry in _runtime.economy_service.get_entries():
if entry.category == &"SERVICE_SALE" and entry.source_entity_id == _ids.get(definition_id, &"missing"):
total += entry.amount
return total
func _command(type: StringName, payload: Dictionary) -> bool:
var result: CommandResult = _runtime.submit_command(type, payload)
_expect(result.is_success(), "%s %s: %s" % [type, payload, result.message])
return result.is_success()
func _capture() -> void:
if OS.get_environment("THE_TREE_SUPPLY_CAPTURE") != "1":
return
await process_frame
_main.call("_activate_management_mode", &"product")
var panel := _main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel
for tab: StringName in [&"purchase", &"kitchen", &"delivery"]:
panel.select_tab(tab)
await process_frame
await RenderingServer.frame_post_draw
var path := ProjectSettings.globalize_path("res://artifacts/supply-auto-chain/%s.png" % tab)
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
root.get_texture().get_image().save_png(path)
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition:
_failures += 1
push_error("FAIL: " + message)
func _finish() -> void:
SaveService.delete_save(_save_path)
print("Normal auto purchase chain: %d checks, %d failures" % [_checks, _failures])
quit(1 if _failures > 0 else 0)
func _start_preview() -> void:
if _failures > 0:
_finish()
return
SaveService.delete_save(_save_path)
root.title = "The Tree · 仕入れと厨房の試遊(保存なし)"
var load_menu := _main.find_child("SaveLoadMenu", true, false) as MenuButton
if load_menu != null:
load_menu.disabled = true
load_menu.tooltip_text = "この試遊は保存・読込を行いません。"
for node: Node in _main.find_children("*", "Button", true, false):
var button := node as Button
for connection: Dictionary in button.pressed.get_connections():
var callback: Callable = connection[&"callable"]
if callback.get_method() in [&"_save_manual", &"_load_manual"]:
button.disabled = true
button.tooltip_text = "この試遊は保存しません。"
if OS.get_environment("THE_TREE_BUSINESS_PREVIEW") == "1":
root.title = "The Tree · 料金と営業の試遊(保存なし)"
_main.call("_activate_management_mode", &"business")
var business := _main.find_child("BusinessManagementPanel", true, false)
business.call("select_facility", _ids[&"cafeteria"])
_main.call("_set_status", "料金と営業状態を変更できます。Spaceで再開すると、客の行動に反映されます(保存なし)。", true, true)
else:
_main.call("_activate_management_mode", &"product")
var panel := _main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel
panel.select_tab(&"kitchen")
_main.call("_set_status", "通常予算から育てた宿の試遊です。Spaceで再開し、商品タブで仕入れ・調理・配送を確認できます(保存なし)。", true, true)
_clock.set_paused(true)
print("SUPPLY_PREVIEW_READY checks=%d failures=0" % _checks)
@@ -0,0 +1 @@
uid://b35pphufmkii8
+365
View File
@@ -0,0 +1,365 @@
extends SceneTree
## Real command/ledger boundaries; failure injections are disclosed separately.
class RejectingPaymentEconomy:
extends EconomyService
func transfer(_from: StringName, _to: StringName, _amount: int,
_minute: int = -1, _category: StringName = DEFAULT_CATEGORY,
_source: StringName = &"", _key: StringName = &"", _memo: StringName = &"",
_related: StringName = &"", _notify := true) -> CommandResult:
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "Payment refusal fixture")
class RejectingNotificationEconomy:
extends EconomyService
func commit_transaction_notification(_transaction: StringName) -> bool:
return false
class ProbedInventory:
extends InventoryService
var transaction_boundary: Dictionary = {}
func add_stack(owner: StringName, item: StringName, quantity: int,
quality: int = DEFAULT_QUALITY, expiry: int = -1, created: int = -1,
weight: int = DEFAULT_UNIT_WEIGHT, stack_id: StringName = &"") -> Dictionary:
transaction_boundary = create_snapshot()
return super.add_stack(owner, item, quantity, quality, expiry, created, weight, stack_id)
class PartialFailInventory:
extends ProbedInventory
func add_stack(owner: StringName, item: StringName, quantity: int,
quality: int = DEFAULT_QUALITY, expiry: int = -1, created: int = -1,
weight: int = DEFAULT_UNIT_WEIGHT, stack_id: StringName = &"") -> Dictionary:
super.add_stack(owner, item, quantity, quality, expiry, created, weight, stack_id)
return {&"ok": false, &"code": &"INJECTED_AFTER_STOCK"}
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _warehouse: StringName
var _built: Dictionary
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_runtime = root.get_node("SimulationRuntime")
_clock = root.get_node("GameClock")
_state = root.get_node("GameState")
_clock.set_process(false)
_clock.reset(1, 6, 0)
_state.reset()
_expect(_runtime.initialize(_clock, _state, 420_501)[&"valid"], "canonical Runtime initializes")
_clock.set_paused(true)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_expect(_runtime.submit_command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(-8, 0, 1)}).is_success(), "ordinary paid warehouse build")
_clock.advance_minutes(180)
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"warehouse": _warehouse = facility.instance_id
_expect(not _warehouse.is_empty(), "ordinary construction yields a warehouse")
if _warehouse.is_empty(): _finish(); return
_built = _runtime.create_snapshot()
_test_receipt_codec()
_test_default_manual_and_paid_order()
_test_invalid_input_and_corrupt_save()
_test_atomic_failures_and_same_minute_retry()
_test_hold_disable_and_removal()
_test_actual_budget_power_reservation_and_terminal()
_test_multiple_warehouse_order()
_test_legacy13_and_composed_rollback()
_finish()
func _plan(enabled: bool, target: int, key: StringName = &"") -> CommandResult:
return _runtime.submit_command(&"set_supply_auto_purchase", {&"warehouse_id": _warehouse,
&"item_id": SupplyCatalog.RAW_FOOD, &"enabled": enabled, &"target_stock": target}, &"player", _warehouse, 75, -1, key)
func _stock() -> int:
var facility: FacilityInstance = _runtime.facility_service.get_instance(_warehouse)
return _runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD)
func _test_receipt_codec() -> void:
var memo := SupplyAutoPurchasePlans.receipt_memo(SupplyCatalog.RAW_FOOD, 6, 3, 18)
var decoded := SupplyAutoPurchasePlans.decode_receipt_memo(memo)
_expect(decoded == {&"item_id": String(SupplyCatalog.RAW_FOOD), &"quantity": 6, &"unit_price": 3, &"total_cost": 18}, "canonical versioned actual quote codec is lossless")
decoded[&"quantity"] = 3
_expect(SupplyAutoPurchasePlans.decode_receipt_memo(memo)[&"quantity"] == 6, "decoded receipt is independently owned")
var punctuation: StringName = &"item:part.日本語"
_expect(SupplyAutoPurchasePlans.decode_receipt_memo(SupplyAutoPurchasePlans.receipt_memo(punctuation, 6, 3, 18))[&"item_id"] == String(punctuation), "length encoding handles punctuation and Unicode without ambiguity")
var prefix: String = SupplyAutoPurchasePlans.RECEIPT_PREFIX + "19:ingredient/raw_food:"
for malformed: String in ["", "supply_purchase", prefix + "3:6:18:extra", prefix + "6:03:18", prefix + "6:+3:18", prefix + "6:3.0:18", prefix + "6:3e0:18", prefix + "6:-3:18", prefix + "6:0:0", prefix + "6:3:019", prefix + "6:3:19", prefix + "10001:3:30003", prefix + "1:9223372036854775808:9223372036854775808", prefix + "2:9223372036854775807:9223372036854775807", "supply_purchase.auto.v2:19:ingredient/raw_food:6:3:18", "supply_purchase.auto.v1:019:ingredient/raw_food:6:3:18", "supply_purchase.auto.v1:18:ingredient/raw_food:6:3:18", "supply_purchase.auto.v1:9223372036854775808:ingredient/raw_food:6:3:18", "supply_purchase.auto.v1:1: :6:3:18"]:
_expect(SupplyAutoPurchasePlans.decode_receipt_memo(StringName(malformed)).is_empty(), "malformed/noncanonical/overflow quote memo is rejected: " + malformed)
var max_memo: StringName = &"supply_purchase.auto.v1:1:x:1:9223372036854775807:9223372036854775807"
_expect(SupplyAutoPurchasePlans.decode_receipt_memo(max_memo).get(&"unit_price", 0) == 9223372036854775807, "signed64 canonical maximum parses without floating conversion or overflow")
func _test_default_manual_and_paid_order() -> void:
_expect(_runtime.auto_purchase_plans.get_plans().is_empty(), "new game has no inferred automatic intent")
var before: Dictionary = _runtime.create_snapshot()
_clock.advance_minutes(1)
_expect(_stock() == 0 and _runtime.economy_service.create_snapshot() == before[&"systems"][&"runtime"][&"economy"], "default disabled tick spends and stocks nothing")
var manual: CommandResult = _runtime.submit_command(&"purchase_supplies", {&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 4})
_expect(manual.is_success() and int(manual.details.get(&"total_cost", -1)) == 12 and _stock() == 4, "manual contract still charges4 food exactly12G")
var transaction_id := StringName(manual.details.get(&"transaction_id", &""))
var entries: Array = _runtime.economy_service.get_transaction_entries(transaction_id)
_expect(entries.size() == 1 and entries[0].category == &"OPERATING_EXPENSE" and entries[0].memo_key == &"supply_purchase", "manual category/memo remain unchanged")
_expect(_runtime.economy_service.get_transaction_id_for_idempotency_key(StringName("supply.purchase.%d" % manual.command_id)) == transaction_id, "manual ledger key still uses accepted commandID")
var configured: CommandResult = _plan(true, 10, &"auto.test.intent")
_expect(configured.is_success() and _stock() == 4, "player intent does not immediately stock or pay")
var armed: Dictionary = _runtime.create_snapshot()
var cash: int = _runtime.economy_service.get_balance(&"PLAYER_CASH")
var notifications: Array[Dictionary] = []
_runtime.economy_service.transaction_posted.connect(func(id: StringName, ledger_rows: Array) -> void:
var rows: Array[Dictionary] = []
for entry: LedgerEntry in ledger_rows: rows.append(entry.to_dictionary())
notifications.append({&"transaction_id": id, &"entries": rows}))
_clock.advance_minutes(1)
var paid: Dictionary = _runtime.create_snapshot()
var row: Dictionary = _runtime.auto_purchase_plans.get_plan(_warehouse, SupplyCatalog.RAW_FOOD)
_expect(_stock() == 10 and _runtime.economy_service.get_balance(&"PLAYER_CASH") == cash - 18, "ordinary tick buys the exact deficit6 at existing3G")
_expect(_state.get_resource(&"gold") == cash - 18 and row[&"last_result"] == &"PURCHASED" and row[&"last_attempt_minute"] == _clock.absolute_minute, "cash mirror and saved receipt record the real purchase")
var receipt := SupplyAutoPurchasePlans.receipt_from_ledger(_runtime.economy_service, _warehouse, SupplyCatalog.RAW_FOOD, _clock.absolute_minute, StringName(row[&"last_transaction_id"]))
_expect(receipt == {&"item_id": String(SupplyCatalog.RAW_FOOD), &"quantity": 6, &"unit_price": 3, &"total_cost": 18}, "independent actual ledger memo anchors item/quantity/historic price/total")
_expect(notifications.size() == 1 and notifications[0][&"transaction_id"] == StringName(row[&"last_transaction_id"]) and notifications[0][&"entries"].size() == 1 and notifications[0][&"entries"][0][&"memo_key"] == String(SupplyAutoPurchasePlans.receipt_memo(SupplyCatalog.RAW_FOOD, 6, 3, 18)), "normal successful automatic purchase delivers one actual quoted ledger notification")
_expect((_runtime.economy_service.get("_pending_transaction_notifications") as Dictionary).is_empty(), "normal automatic notification is committed without pending residue")
_expect(paid[&"systems"][&"runtime"][&"next_command_id"] == armed[&"systems"][&"runtime"][&"next_command_id"] and var_to_bytes(paid[&"systems"][&"runtime"][&"command_queue"]) == var_to_bytes(armed[&"systems"][&"runtime"][&"command_queue"]), "automatic order allocates no fake player command or queue history")
_expect(var_to_bytes(paid[&"systems"][&"runtime"][&"rng_streams"]) == var_to_bytes(armed[&"systems"][&"runtime"][&"rng_streams"]), "automatic purchase makes no RNG draw")
_expect(_runtime.supply_management.advance_auto_purchases(_runtime, _clock.absolute_minute).is_empty() and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "same-minute reentry is exact no-op")
_expect(notifications.size() == 1, "same-minute automatic reentry does not duplicate actual notification")
_expect(_runtime.restore_snapshot(armed), "whole pre-order snapshot restores")
_clock.advance_minutes(1)
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "saved pre-order branch replays all typed state/order/events/IDs exactly")
_expect(_runtime.restore_snapshot(paid) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "paid order/current14 receipt roundtrip is exact")
var duplicate_before: Dictionary = _runtime.create_snapshot()
var duplicate: CommandResult = _plan(true, 10, &"auto.test.intent")
_expect(not duplicate.is_success() and duplicate.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY and not duplicate.is_replay and _stock() == 10, "already accepted intent key is rejected by the canonical command queue")
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(duplicate_before), "duplicate intent refusal changes no plan, inventory, ledger, IDs, queue or Runtime state")
var owned: Dictionary = _runtime.auto_purchase_plans.get_plan(_warehouse, SupplyCatalog.RAW_FOOD)
owned[&"enabled"] = false
_expect(_runtime.auto_purchase_plans.get_plan(_warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"], "returned plan is owned")
func _test_invalid_input_and_corrupt_save() -> void:
_expect(_runtime.restore_snapshot(_built), "reset to paid real warehouse fixture")
var before: Dictionary = _runtime.create_snapshot()
for value: Variant in [true, -1, 101, 1.5, "4", NAN, INF]:
var result: CommandResult = _runtime.supply_management.set_auto_purchase(_runtime,
SimulationCommand.new(999, _clock.absolute_minute, _clock.absolute_minute, 75,
&"set_supply_auto_purchase", &"player", _warehouse,
{&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": value}))
_expect(not result.is_success() and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(before), "invalidtarget rejects atomically: %s" % value)
for patch: Dictionary in [
{&"warehouse_id": &"missing", &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 1},
{&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.BASIC_MEAL, &"enabled": true, &"target_stock": 1},
{&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": "true", &"target_stock": 1},
{&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 0},
{&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 1, &"unit_price": 0}]:
var result: CommandResult = _runtime.supply_management.set_auto_purchase(_runtime, SimulationCommand.new(999,0,0,75,&"set_supply_auto_purchase",&"player",_warehouse,patch))
_expect(not result.is_success() and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(before), "forged/wrongintent changes no state")
_expect(_plan(true, 6).is_success(), "configure a canonical saved plan")
var canonical: Dictionary = _runtime.create_snapshot()
for mutation: String in ["missing", "version", "duplicate", "unknown", "target", "bool", "clock", "receipt", "price", "extra"]:
var bad: Dictionary = canonical.duplicate(true)
var data: Dictionary = bad[&"systems"][&"runtime"]
var plans: Dictionary = data[&"auto_purchase_plans"]
var row: Dictionary = plans[&"plans"][0]
match mutation:
"missing": data.erase(&"auto_purchase_plans")
"version": plans[&"version"] = 2
"duplicate": (plans[&"plans"] as Array).append(row.duplicate(true))
"unknown": row[&"warehouse_id"] = "missing"
"target": row[&"target_stock"] = 101
"bool": row[&"enabled"] = 1
"clock": row[&"last_attempt_minute"] = _clock.absolute_minute + 1
"receipt": row[&"last_transaction_id"] = "txn.foreign"
"price": row[&"last_unit_price"] = 0.0
"extra": row[&"unit_price"] = 0
_expect(not _runtime.restore_snapshot(bad) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(canonical), "corrupt14 plan rejected without mutation: " + mutation)
func _test_atomic_failures_and_same_minute_retry() -> void:
for kind: String in ["payment", "notification", "partial_stock"]:
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "configure independent failure fixture: " + kind)
var economy_before: Dictionary = _runtime.economy_service.create_snapshot()
var inventory_before: Dictionary = _runtime.inventory_service.create_snapshot()
var probe: ProbedInventory = PartialFailInventory.new() if kind == "partial_stock" else ProbedInventory.new()
_expect(probe.restore_snapshot(inventory_before), "Inventory probe starts from full actual snapshot: " + kind)
_runtime.inventory_service = probe
if kind != "partial_stock":
var rejecting: EconomyService = RejectingPaymentEconomy.new() if kind == "payment" else RejectingNotificationEconomy.new()
_expect(rejecting.restore_snapshot(economy_before), "injected Economy starts from actual snapshot")
_runtime.economy_service = rejecting
_runtime.call("_connect_economy_signals")
var notifications: Array[StringName] = []
_runtime.economy_service.transaction_posted.connect(func(id: StringName, _entries: Array) -> void: notifications.append(id))
var pending_before: Dictionary = (_runtime.economy_service.get("_pending_transaction_notifications") as Dictionary).duplicate(true)
_clock.advance_minutes(1)
_expect(_stock() == 0 and var_to_bytes(_runtime.economy_service.create_snapshot()) == var_to_bytes(economy_before), "failed order keeps actual cash and all ledger bytes unchanged: " + kind)
_expect(not probe.transaction_boundary.is_empty() and var_to_bytes(_runtime.inventory_service.create_snapshot()) == var_to_bytes(probe.transaction_boundary), "transaction-boundary rollback restores full Inventory IDs/reservations/history/order, allowing the preceding ordinary clock advancement: " + kind)
_expect(probe.transaction_boundary.get(&"current_minute", -1) == _clock.absolute_minute and _clock.absolute_minute > int(inventory_before[&"current_minute"]), "Inventory probe observes the actual next-minute transaction boundary: " + kind)
_expect(notifications.is_empty() and var_to_bytes(_runtime.economy_service.get("_pending_transaction_notifications")) == var_to_bytes(pending_before), "failed automatic transaction emits no notification and leaves the observed pending set exact: " + kind)
var failed: Dictionary = _runtime.create_snapshot()
var row: Dictionary = _runtime.auto_purchase_plans.get_plan(_warehouse, SupplyCatalog.RAW_FOOD)
_expect(row[&"last_attempt_minute"] == _clock.absolute_minute and row[&"last_result"] != &"PURCHASED", "actual failure stays recorded in same minute: " + kind)
_expect(_runtime.restore_snapshot(failed), "recorded failure restores into regular services: " + kind)
_expect(_runtime.supply_management.advance_auto_purchases(_runtime, _clock.absolute_minute).is_empty() and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(failed), "same-minute regular retry cannot hide the actual failure: " + kind)
_clock.advance_minutes(1)
_expect(_stock() == 6 and _runtime.economy_service.get_balance(&"PLAYER_CASH") == int(_built[&"game_state"][&"resources"][&"gold"]) - 18, "next ordinary tick retries exactly once: " + kind)
func _test_hold_disable_and_removal() -> void:
_expect(_runtime.restore_snapshot(_built), "hold fixture restores")
_expect(_plan(true, 6).is_success(), "enable before normal closure")
_expect(_runtime.submit_command(&"set_facility_policy", {&"facility_id": _warehouse, &"patch": {&"business_open": false}}).is_success(), "normal closure precedes auto tick")
var cash: int = _state.get_resource(&"gold")
_clock.advance_minutes(1)
_expect(_stock() == 0 and _state.get_resource(&"gold") == cash, "closed warehouse does not stock/pay")
_expect(_plan(false, 0).is_success(), "setting disable works despite closed receiver")
_expect(_runtime.submit_command(&"set_facility_policy", {&"facility_id": _warehouse, &"patch": {&"business_open": true}}).is_success(), "normal opening restores logistical eligibility")
_clock.advance_minutes(1)
_expect(_stock() == 0, "disabled plan stays off after reopening")
_expect(_plan(true, 6).is_success(), "enable again")
_clock.advance_minutes(1)
var paid: Dictionary = _runtime.create_snapshot()
var removal: CommandResult = _runtime.submit_command(&"demolish_facility", {&"instance_id": _warehouse})
_expect(not removal.is_success() and _runtime.auto_purchase_plans.create_snapshot() == paid[&"systems"][&"runtime"][&"auto_purchase_plans"], "stock-bearing failed removal preserves plan")
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "future closure fixture resets")
cash = _state.get_resource(&"gold")
_expect(_runtime.submit_command(&"set_facility_policy", {&"facility_id": _warehouse, &"patch": {&"business_open": false}, &"effective_at": _clock.absolute_minute + 1}).is_success(), "normal future closure is scheduled for next tick")
_clock.advance_minutes(1)
_expect(_stock() == 0 and _state.get_resource(&"gold") == cash and not _runtime.facility_service.get_instance(_warehouse).operator_open, "due closure blocks auto purchase before FacilityCompletion without early policy mutation")
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "empty warehouse with explicit intent exists")
_expect(_runtime.submit_command(&"demolish_facility", {&"instance_id": _warehouse}).is_success() and _runtime.auto_purchase_plans.get_plans().is_empty(), "successful empty warehouse removal prunes its intent")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "removed warehouse history/ledger remains restorable")
func _test_actual_budget_power_reservation_and_terminal() -> void:
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "configure a paid budget boundary")
var cash: int = _runtime.economy_service.get_balance(&"PLAYER_CASH")
_expect(_runtime.economy_service.transfer(&"PLAYER_CASH", &"CONSTRUCTION_EXPENSE", cash - 17, _clock.absolute_minute, &"CONSTRUCTION", &"", &"auto.acceptance.budget").is_success(), "disclosed ledger cash fixture leaves17G")
_clock.advance_minutes(1)
var row: Dictionary = _runtime.supply_management.auto_purchase_report(_runtime, _warehouse, SupplyCatalog.RAW_FOOD)
_expect(_stock() == 0 and _state.get_resource(&"gold") == 17 and row[&"reason"] == &"INSUFFICIENT_FUNDS", "full deficit18G never silently becomes partial free or affordable17G order")
_expect(row[&"last_attempt_minute"] == -1, "ineligible quote does not fabricate an executed order")
_expect(_runtime.economy_service.transfer(&"CONSTRUCTION_EXPENSE", &"PLAYER_CASH", 1, _clock.absolute_minute, &"CONSTRUCTION", &"", &"auto.acceptance.budget.refund").is_success(), "same actual ledger can return1G")
_clock.advance_minutes(1)
_expect(_stock() == 6 and _state.get_resource(&"gold") == 0, "eligible full order charges exactly available18G")
var paid: Dictionary = _runtime.create_snapshot()
var manual_key: StringName = &"auto.acceptance.manual.replacement"
var manual: CommandResult = _runtime.submit_command(&"purchase_supplies", {&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 1}, &"player", _warehouse, 75, -1, manual_key)
_expect(not manual.is_success(), "manual insufficient cash remains rejected")
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "reserved-stock fixture resets")
_expect(_runtime.submit_command(&"purchase_supplies", {&"warehouse_id": _warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 6}).is_success(), "reserved stock starts from an actual paid order")
var facility: FacilityInstance = _runtime.facility_service.get_instance(_warehouse)
var reservation: Dictionary = _runtime.inventory_service.reserve(facility.local_inventory_id, SupplyCatalog.RAW_FOOD, 6, &"disclosed.reserve.fixture", &"TEST", _clock.absolute_minute)
_expect(reservation.get(&"ok", false), "disclosed Inventory reservation retains physical stock")
cash = _state.get_resource(&"gold")
_clock.advance_minutes(1)
_expect(_stock() == 6 and _runtime.inventory_service.available_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD) == 0 and _state.get_resource(&"gold") == cash, "reserved physical units are not mistaken for missing stock or double-purchased")
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "power boundary resets")
_state.set_resource(&"mana", 0) # Disclosed resource boundary, not natural Mana generation.
cash = _state.get_resource(&"gold")
_clock.advance_minutes(1)
_expect(_stock() == 0 and _state.get_resource(&"gold") == cash, "Mana0 stops automatic paid order")
_expect(_runtime.restore_snapshot(_built) and _plan(true, 6).is_success(), "paused real-time boundary resets")
var paused: Dictionary = _runtime.create_snapshot()
_expect(_clock.process_real_delta(60.0) == 0 and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paused), "paused real clock causes no automatic purchase or ID/timer change")
var collision_key: StringName = SupplyAutoPurchasePlans.order_key(_warehouse, SupplyCatalog.RAW_FOOD, _clock.absolute_minute)
_expect(_runtime.economy_service.transfer(&"PLAYER_CASH", &"CONSTRUCTION_EXPENSE", 1, _clock.absolute_minute, &"CONSTRUCTION", _warehouse, collision_key).is_success(), "disclosed foreign ledger-key fixture uses a valid ordinary transfer")
cash = _state.get_resource(&"gold")
var collisions: Array = _runtime.supply_management.advance_auto_purchases(_runtime, _clock.absolute_minute)
_expect(collisions.size() == 1 and _stock() == 0 and _state.get_resource(&"gold") == cash and _runtime.auto_purchase_plans.get_plan(_warehouse, SupplyCatalog.RAW_FOOD)[&"last_result"] == &"REPLAY_MISMATCH", "foreign ledger namespace cannot be treated as a free stocked purchase")
var collision: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(collision) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(collision), "foreign-key failure receipt remains restorable without making it purchase income")
_expect(_runtime.restore_snapshot(paid), "restore an actual paid receipt for corruption tests")
var corrupt: Dictionary = paid.duplicate(true)
corrupt[&"systems"][&"runtime"][&"auto_purchase_plans"][&"plans"][0][&"last_transaction_id"] = String(_runtime.economy_service.get_entries()[0].transaction_id)
_expect(not _runtime.restore_snapshot(corrupt) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "an existing unrelated actual transaction cannot replace the automatic receipt")
for field: StringName in [&"last_quantity", &"last_unit_price", &"last_total_cost"]:
corrupt = paid.duplicate(true)
corrupt[&"systems"][&"runtime"][&"auto_purchase_plans"][&"plans"][0][field] += 1
_expect(not _runtime.restore_snapshot(corrupt) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "saved auto quantity/price/total quote cannot disagree with payment: %s" % field)
for pair: Vector2i in [Vector2i(3, 6), Vector2i(2, 9), Vector2i(1, 18), Vector2i(9, 2)]:
corrupt = paid.duplicate(true)
var changed_row: Dictionary = corrupt[&"systems"][&"runtime"][&"auto_purchase_plans"][&"plans"][0]
changed_row[&"last_quantity"] = pair.x
changed_row[&"last_unit_price"] = pair.y
_expect(not _runtime.restore_snapshot(corrupt) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "coordinated factorization retaining actual18G cannot rewrite paid6x3G quote: %s" % pair)
var receipt_id: String = paid[&"systems"][&"runtime"][&"auto_purchase_plans"][&"plans"][0][&"last_transaction_id"]
for malformed: StringName in [&"supply_purchase", SupplyAutoPurchasePlans.receipt_memo(SupplyCatalog.BASIC_MEAL, 6, 3, 18), SupplyAutoPurchasePlans.receipt_memo(SupplyCatalog.RAW_FOOD, 3, 6, 18), &"supply_purchase.auto.v1:19:ingredient/raw_food:6:3:18:extra"]:
corrupt = paid.duplicate(true)
for entry: Dictionary in corrupt[&"systems"][&"runtime"][&"economy"][&"entries"]:
if entry[&"transaction_id"] == receipt_id: entry[&"memo_key"] = String(malformed)
_expect(not _runtime.restore_snapshot(corrupt) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(paid), "ledger quote missing/wrong item/re-factored/noncanonical is rejected against independent plan atomically: %s" % malformed)
# Operations can only be enabled at fresh Day1 06:00 (360), before building.
# Keep the normal paid construction and intent after that canonical start.
_clock.reset(1, 6, 0)
_state.reset()
_expect(_runtime.initialize(_clock, _state, 420_501)[&"valid"], "fresh canonical bankruptcy fixture initializes")
_clock.set_paused(true)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
var started: Dictionary = _runtime.enable_production_operations()
_expect(started[&"ok"] and _clock.absolute_minute == 360, "actual bankruptcy boundary enables ordinary daily operation at fresh Day1 06:00")
_expect(_runtime.submit_command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(-8, 0, 1)}).is_success(), "terminal fixture uses ordinary paid warehouse construction")
_clock.advance_minutes(180)
_warehouse = &""
for endpoint: FacilityInstance in _runtime.facility_service.get_instances():
if endpoint.definition_id == &"warehouse": _warehouse = endpoint.instance_id
_expect(not _warehouse.is_empty() and _plan(true, 6).is_success(), "terminal fixture configures explicit player intent after ordinary warehouse completion")
_clock.advance_minutes(4320 - _clock.absolute_minute)
_expect(_runtime.operations_coordinator.get_day_summaries().size() == 3 and _runtime.operations_coordinator.get_terminal_state() == &"BANKRUPT", "warehouse-only actual costs reach existing three-deficit terminal rule")
var terminal: Dictionary = _runtime.create_snapshot()
_expect(_runtime.supply_management.advance_auto_purchases(_runtime, _clock.absolute_minute + 1).is_empty() and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(terminal), "BANKRUPT blocks every further internal purchase without fake commands")
_expect(_runtime.restore_snapshot(terminal), "terminal intent/ledger/current14 save still restores")
func _test_multiple_warehouse_order() -> void:
_expect(_runtime.restore_snapshot(_built), "multiple actual warehouses fixture starts with one paid unit")
_expect(_runtime.submit_command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(2,0,1)}).is_success(), "ordinary second warehouse construction is paid")
_clock.advance_minutes(180)
var ids: Array[StringName] = []
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"warehouse": ids.append(facility.instance_id)
ids.sort_custom(func(a: StringName, b: StringName) -> bool: return String(a) < String(b))
_expect(ids.size() == 2, "two actual warehouse endpoints exist")
if ids.size() != 2: return
for index: int in [1, 0]:
_expect(_runtime.submit_command(&"set_supply_auto_purchase", {&"warehouse_id": ids[index], &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 1}).is_success(), "reverse player setting order does not define payment priority")
var cash: int = _state.get_resource(&"gold")
_expect(_runtime.economy_service.transfer(&"PLAYER_CASH", &"CONSTRUCTION_EXPENSE", cash - 3, _clock.absolute_minute, &"CONSTRUCTION", &"", &"auto.acceptance.multi_budget").is_success(), "disclosed shared cash fixture funds exactly one existing3G unit")
var before: Dictionary = _runtime.create_snapshot()
_clock.advance_minutes(1)
var after: Dictionary = _runtime.create_snapshot()
_expect(_runtime.inventory_service.total_quantity(_runtime.facility_service.get_instance(ids[0]).local_inventory_id, SupplyCatalog.RAW_FOOD) == 1 and _runtime.inventory_service.total_quantity(_runtime.facility_service.get_instance(ids[1]).local_inventory_id, SupplyCatalog.RAW_FOOD) == 0, "explicit lexicographic warehouse/item ordering deterministically spends shared cash once")
_expect(_runtime.restore_snapshot(before), "multiple-plan pre-order snapshot restores")
_clock.advance_minutes(1)
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(after), "restored multiple-plan allocation reproduces every typed field and event in order")
func _test_legacy13_and_composed_rollback() -> void:
_expect(_runtime.restore_snapshot(_built), "legacy fixture resets")
var legacy: Dictionary = _built.duplicate(true)
legacy[&"systems"][&"runtime"][&"runtime_schema_version"] = 13
legacy[&"systems"][&"runtime"].erase(&"auto_purchase_plans")
_expect(_runtime.restore_snapshot(legacy) and _runtime.auto_purchase_plans.get_plans().is_empty(), "valid13 snapshot migrates to disabled14 without purchase inference")
var migrated: Dictionary = _runtime.create_snapshot()
var disguised: Dictionary = legacy.duplicate(true)
disguised[&"systems"][&"runtime"][&"auto_purchase_plans"] = {&"version": 1, &"plans": []}
_expect(not _runtime.restore_snapshot(disguised) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(migrated), "oldversion cannot covertly retain new aggregate")
_clock.set_paused(false)
_expect(_plan(true, 6).is_success(), "actual current14 pending automatic intent queues")
_clock.set_paused(true)
var pending: Dictionary = _runtime.create_snapshot()
var disguised_pending: Dictionary = pending.duplicate(true)
disguised_pending[&"systems"][&"runtime"][&"runtime_schema_version"] = 13
disguised_pending[&"systems"][&"runtime"].erase(&"auto_purchase_plans")
_expect(not _runtime.restore_snapshot(disguised_pending) and var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(pending), "old13 cannot reinterpret a covert queued automatic order as paid feature intent")
_expect(_runtime.restore_snapshot(_built), "reset after pending downgrade refusal")
_expect(_plan(true, 6).is_success(), "intent exists before composed rollback token")
var token: Dictionary = _runtime.call("_capture_operations_external_state")
_expect(not token.is_empty() and token.has(&"auto_purchase_plans"), "Operations rollback includes owned plan state")
_expect(_runtime.auto_purchase_plans.set_plan(_warehouse, SupplyCatalog.RAW_FOOD, false, 0, _runtime.facility_service)[&"ok"], "disclosed boundary mutation fixture changes only owned intent")
_expect(_runtime.call("_restore_operations_external_state", token) and _runtime.call("_operations_external_state_matches", token), "composed rollback restores exact plan and other participants")
_runtime.call("_discard_operations_domain_events")
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition:
_failures += 1
push_error("FAIL: " + message)
func _finish() -> void:
print("SUPPLY_AUTO_PURCHASE checks=%d failures=%d" % [_checks, _failures])
quit(_failures)
@@ -0,0 +1 @@
uid://camsaleipkl22
@@ -0,0 +1,264 @@
extends SceneTree
## Supplemental P05 QA only. Actual paid construction/purchases and porter paths
## are used. Capacity pressure, cargo lot expiry, callback failures and a ledger
## refund are explicit boundary fixtures, not normal-business success evidence.
var runtime: Variant
var clock: Variant
var state: Variant
var warehouse: StringName
var second: StringName
var kitchen: StringName
var checks := 0
var failures := 0
var output := ""
var observations: Array[Dictionary] = []
const PORTER := &"staff_gina"
const RETURN_REQUEST := &"supply.boundary_return"
func _initialize() -> void:
call_deferred("_run")
func expect(ok: bool, label: String) -> void:
checks += 1
if ok:
print(" ✓ ", label)
else:
failures += 1
push_error(label)
func _run() -> void:
output = OS.get_environment("THE_TREE_AUTO_BOUNDARY_OUTPUT")
var user_dir := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_USER_DIR")
var res_dir := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_RES_DIR")
var profile := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_PROFILE")
var external_evidence: bool = not output.is_empty()
var valid := false
if external_evidence:
valid = output.is_absolute_path() and DirAccess.dir_exists_absolute(output) and not FileAccess.file_exists(output.path_join("boundaries.json")) and user_dir.is_absolute_path() and OS.get_user_data_dir() == user_dir and res_dir.is_absolute_path() and ProjectSettings.globalize_path("res://").simplify_path().trim_suffix("/") == res_dir.simplify_path().trim_suffix("/") and not profile.is_empty() and bool(ProjectSettings.get_setting("application/config/use_custom_user_dir", false)) and ProjectSettings.get_setting("application/config/custom_user_dir_name", "") == profile
else:
# Standard regression needs no external output/guards. Use only a new
# process-specific tests child, never a player slot or res:// source file.
var relative := "user://tests/supply_auto_boundaries_%d_%d" % [OS.get_process_id(), Time.get_ticks_usec()]
output = ProjectSettings.globalize_path(relative).simplify_path()
var allowed_parent := OS.get_user_data_dir().path_join("tests").simplify_path()
valid = user_dir.is_empty() and res_dir.is_empty() and profile.is_empty() and output.get_base_dir() == allowed_parent and not DirAccess.dir_exists_absolute(output)
if valid: valid = DirAccess.make_dir_recursive_absolute(output) == OK
profile = ProjectSettings.get_setting("application/config/custom_user_dir_name", "")
if not valid:
push_error("P05_AUTO_BOUNDARIES requires exact fresh actual profile/resource/output guards")
quit(91)
return
runtime = root.get_node("SimulationRuntime")
clock = root.get_node("GameClock")
state = root.get_node("GameState")
clock.set_process(false)
_storage_and_incoming()
_local_power_and_suspension()
_operations_deferred()
_operations_quarantine()
_unpaid_expenses()
_returned_cargo()
var file := FileAccess.open(output.path_join("boundaries.json"), FileAccess.WRITE)
expect(file != null, "fresh boundary summary opens")
if file != null:
file.store_string(JSON.stringify({"checks": checks, "failures": failures, "actual_user_data_dir": OS.get_user_data_dir(), "actual_resource_dir": ProjectSettings.globalize_path("res://"), "actual_profile": profile, "output": output, "external_evidence": external_evidence, "observations": observations, "normal_business_acceptance": false, "p05_full_acceptance": false}, "\t", true, true) + "\n")
file.close()
print("Supply auto purchase boundaries: %d checks, %d failures" % [checks, failures])
print("P05_AUTO_BOUNDARY_OUTPUT ", output)
quit(1 if failures > 0 else 0)
func fresh(rows: Array = [[&"warehouse", Vector3i(-8, 0, 1)]], operations := false) -> bool:
warehouse = &""
second = &""
kitchen = &""
clock.reset(1, 6, 0)
state.reset()
expect(runtime.initialize(clock, state, 420_505)[&"valid"], "canonical14 Runtime resets")
clock.set_paused(true)
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
if operations:
expect(runtime.enable_production_operations()[&"ok"], "actual Operations enable at original Day1 06:00 before construction")
for row: Array in rows:
expect(runtime.submit_command(&"build_facility", {&"definition_id": row[0], &"origin": row[1]}).is_success(), "ordinary paid build: %s" % row[0])
clock.advance_minutes(180)
for facility: FacilityInstance in runtime.facility_service.get_instances():
if facility.definition_id == &"warehouse":
if warehouse.is_empty(): warehouse = facility.instance_id
else: second = facility.instance_id
if facility.definition_id == &"kitchen": kitchen = facility.instance_id
expect(not warehouse.is_empty(), "real warehouse completed")
return not warehouse.is_empty()
func inv(id: StringName) -> StringName:
return runtime.facility_service.get_instance(id).local_inventory_id
func stock(id: StringName = &"") -> int:
if id.is_empty(): id = warehouse
return runtime.inventory_service.total_quantity(inv(id), SupplyCatalog.RAW_FOOD)
func plan(target: int, minute: int = -1) -> CommandResult:
return runtime.submit_command(&"set_supply_auto_purchase", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": target}, &"player", warehouse, 75, minute)
func record(label: String, before: Dictionary, report: Dictionary) -> void:
var after: Dictionary = runtime.create_snapshot()
for row: Array in [["before", before], ["after", after]]:
var path: String = output.path_join(label + "-" + String(row[0]) + ".var")
expect(not FileAccess.file_exists(path), "fresh actual typed boundary snapshot: " + label + "/" + String(row[0]))
var file := FileAccess.open(path, FileAccess.WRITE)
expect(file != null, "typed boundary evidence opens")
if file != null:
file.store_buffer(var_to_bytes(row[1]))
file.close()
observations.append({"boundary": label, "report": report, "before_digest": SimulationDigest.compute(before), "after_digest": SimulationDigest.compute(after), "whole_bytes_equal": var_to_bytes(before) == var_to_bytes(after), "checks_so_far": checks, "failures_so_far": failures})
func _storage_and_incoming() -> void:
if not fresh([[&"warehouse", Vector3i(-8, 0, 1)], [&"warehouse", Vector3i(2, 0, 1)]]): return
expect(not second.is_empty(), "actual second warehouse is inbound source")
if second.is_empty(): return
# Disclosed capacity-pressure seam: actual supported other item, not invented
# commodity/capacity or ordinary free-stock success evidence.
expect(runtime.inventory_service.add_stack(inv(warehouse), SupplyCatalog.BASIC_MEAL, 95, 100, clock.absolute_minute + 720, clock.absolute_minute, 1)[&"ok"], "disclosed95 other-item pressure occupies canonical100 capacity")
expect(runtime.submit_command(&"purchase_supplies", {&"warehouse_id": second, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 6}).is_success(), "inbound6 are actually paid at the source")
expect(runtime.submit_command(&"create_logistics_request", {&"request_id": &"supply.boundary_inbound", &"source_inventory_id": inv(second), &"destination_inventory_id": inv(warehouse), &"item_requirements": {SupplyCatalog.RAW_FOOD: 6}}).is_success(), "actual reserved inbound6 request targets the receiver")
expect(plan(10).is_success(), "player arms10 actual enabled auto target")
var before: Dictionary = runtime.create_snapshot()
var report: Dictionary = runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)
expect(report[&"enabled"] and report[&"incoming_quantity"] == 6 and report[&"deficit"] == 4 and report[&"reason"] == &"STORAGE_FULL", "other95 plus actual inbound6 blocks remaining auto4 with STORAGE_FULL")
expect(runtime.supply_management.advance_auto_purchases(runtime, clock.absolute_minute).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "STORAGE_FULL enabled auto changes no whole typed state or payment")
record("storage-full-with-inbound", before, report)
var reserved: Dictionary = runtime.inventory_service.reserve(inv(warehouse), SupplyCatalog.BASIC_MEAL, 95, &"boundary.pressure.release", &"TEST", clock.absolute_minute)
expect(reserved.get(&"ok", false), "disclosed pressure release uses actual reservation API")
var ids: Array[StringName] = []
for id: Variant in reserved.get(&"reservation_ids", []): ids.append(StringName(id))
expect(runtime.inventory_service.consume_reservations(ids, &"boundary.pressure.consume", clock.absolute_minute).get(&"ok", false), "pressure releases only its disclosed95 cooked meals")
before = runtime.create_snapshot()
var cash: int = state.get_resource(&"gold")
clock.advance_minutes(1)
expect(stock() == 4 and state.get_resource(&"gold") == cash - 12, "released capacity buys actual deficit4 once, retaining inbound6 deduction")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "enabled auto plus real reserved inbound14 saves strictly")
record("inbound-deducted-purchase", before, runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD))
func _local_power_and_suspension() -> void:
for kind: String in ["local-power", "suspension"]:
if not fresh(): return
expect(plan(6).is_success(), "arm enabled auto before " + kind)
var conditions: Dictionary = {&"power_available": false} if kind == "local-power" else {&"suspension_reason": &"RAIDED"}
expect(runtime.facility_service.set_operational_status(warehouse, conditions, clock.absolute_minute).is_success(), "disclosed public operational boundary: " + kind)
var facility: FacilityInstance = runtime.facility_service.get_instance(warehouse)
expect(state.get_resource(&"mana") > 0 and not facility.allows_logistics(), "global Mana remains available while warehouse local condition blocks " + kind)
var before: Dictionary = runtime.create_snapshot()
var report: Dictionary = runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)
expect(report[&"enabled"] and report[&"state"] == &"BLOCKED" and report[&"reason"] == &"FACILITY_CLOSED", "owned auto report exposes local blocked receiver: " + kind)
expect(runtime.supply_management.advance_auto_purchases(runtime, clock.absolute_minute).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "local stopped enabled auto spends/stocks/notifies nothing: " + kind)
record(kind, before, report)
func _operations_deferred() -> void:
if not fresh([[&"warehouse", Vector3i(-8, 0, 1)]], true): return
var due: int = clock.absolute_minute + 30
expect(plan(6, due).is_success(), "actual auto intent is queued for failing Operations boundary")
expect(runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", func(_payload: Dictionary, phase: StringName, _token: Variant) -> Dictionary: return {&"ok": false, &"reason": &"DISCLOSED_BOUNDARY_PREPARE_FAILURE", &"phase": phase}), "disclosed actual prepare fault seam registered")
clock.advance_minutes(30)
var operations: Dictionary = runtime.get_operations_report()
expect(operations[&"tick_blocked"] and operations[&"runtime_failure_latched"] and operations[&"deferred_runtime_minute"] == due and runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"], "actual deferred suffix is blocked with actual enabled auto intent")
var before: Dictionary = runtime.create_snapshot()
expect(stock() == 0 and runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)[&"reason"] == &"OPERATIONS_BLOCKED", "deferred boundary never purchases before its skipped suffix")
expect(runtime.supply_management.advance_auto_purchases(runtime, due).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "deferred enabled auto is whole typed no-op")
record("operations-deferred", before, operations)
expect(runtime.restore_snapshot(before), "actual deferred enabled-auto14 snapshot strictly restores")
var cash: int = state.get_resource(&"gold")
var acknowledged: Dictionary = runtime.acknowledge_operations_pause()
expect(acknowledged.get(&"ok", false) and acknowledged.get(&"suffix_replayed", false) and stock() == 6 and state.get_resource(&"gold") == cash - 18, "explicit actual retry runs deferred suffix and paid auto6 exactly once")
var completed: Dictionary = runtime.create_snapshot()
expect(runtime.supply_management.advance_auto_purchases(runtime, due).is_empty() and var_to_bytes(completed) == var_to_bytes(runtime.create_snapshot()), "retried actual suffix cannot purchase twice in the same minute")
func _quarantine_arrival(payload: Dictionary, phase: StringName, token: Variant) -> Dictionary:
if phase == OperationsCoordinator.PHASE_APPLY:
return {&"ok": false, &"reason": &"DISCLOSED_APPLY_FAILURE"}
if phase == OperationsCoordinator.PHASE_ROLLBACK:
return {&"ok": false, &"reason": &"DISCLOSED_ROLLBACK_UNAVAILABLE"}
return runtime.call("_operations_arrival_boundary", payload, phase, token)
func _operations_quarantine() -> void:
if not fresh([[&"warehouse", Vector3i(-8, 0, 1)]], true): return
var due: int = clock.absolute_minute + 30
expect(plan(6, due).is_success(), "actual auto intent is queued for quarantined boundary")
# Public callback contract, no manual tick_blocked/quarantine flag mutation.
runtime.operations_coordinator.configure_callbacks(Callable(runtime, "_operations_daily_boundary"), Callable(self, "_quarantine_arrival"), Callable(runtime, "_operations_morning_boundary"), Callable(runtime, "_operations_midnight_upkeep_boundary"))
clock.advance_minutes(30)
var operations: Dictionary = runtime.get_operations_report()
expect(runtime.operations_coordinator.is_external_quarantined() and operations[&"tick_blocked"] and runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"], "actual external rollback failure quarantines owning Coordinator with enabled auto")
var before: Dictionary = runtime.create_snapshot()
expect(stock() == 0 and runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)[&"reason"] == &"OPERATIONS_BLOCKED", "actual quarantine exposes blocked enabled auto")
expect(runtime.supply_management.advance_auto_purchases(runtime, due).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "quarantined enabled auto is whole typed no-op")
expect(not runtime.acknowledge_operations_pause().get(&"ok", true), "normal acknowledgement cannot clear external uncertainty")
record("operations-quarantine", before, {"operations": operations, "quarantine": runtime.operations_coordinator.get_quarantine_report()})
func _unpaid_expenses() -> void:
if not fresh([[&"warehouse", Vector3i(-8, 0, 1)]], true): return
clock.advance_minutes(1439 - clock.absolute_minute)
var cash: int = state.get_resource(&"gold")
expect(runtime.economy_service.transfer(&"PLAYER_CASH", &"CONSTRUCTION_EXPENSE", cash, clock.absolute_minute, &"CONSTRUCTION", warehouse, &"boundary.disclosed.budget").is_success(), "disclosed real ledger budget fixture spends cash before midnight")
clock.advance_minutes(1)
var debt: int = runtime.upkeep_settlement.get_arrears_total()
expect(debt > 0 and runtime.operations_coordinator.get_arrears_total() == debt and state.get_resource(&"gold") == 0, "actual midnight accrual creates unpaid upkeep, not crafted debt")
expect(runtime.economy_service.transfer(&"CONSTRUCTION_EXPENSE", &"PLAYER_CASH", 18, clock.absolute_minute, &"CONSTRUCTION_REFUND", warehouse, &"boundary.disclosed.refund").is_success(), "disclosed actual ledger refund funds a6-food order")
expect(plan(6).is_success(), "player arms auto while actual debt remains unpaid")
var before: Dictionary = runtime.create_snapshot()
var settlement: Dictionary = runtime.upkeep_settlement.create_snapshot()
clock.advance_minutes(1)
expect(stock() == 6 and state.get_resource(&"gold") == 0 and runtime.upkeep_settlement.get_arrears_total() == debt, "actual18G paid auto preserves unpaid expense instead of erasing or netting debt")
expect(var_to_bytes(settlement) == var_to_bytes(runtime.upkeep_settlement.create_snapshot()), "procurement leaves all unpaid settlement receipts/history/IDs exact")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "actual paid auto plus unpaid expense14 strictly restores")
record("unpaid-expense-procurement", before, {"actual_arrears": debt, "settlement": settlement, "ordinary_sales_fixture": false})
func _returned_cargo() -> void:
if not fresh([[&"warehouse", Vector3i(-8, 0, 1)], [&"kitchen", Vector3i(2, 0, 1)], [&"staff_door", Vector3i(0, 0, 1)]]): return
expect(not kitchen.is_empty(), "real kitchen destination completes")
if kitchen.is_empty(): return
expect(runtime.submit_command(&"hire_staff_candidate", {&"candidate_id": PORTER}).is_success(), "ordinary paid hire provides real porter")
expect(runtime.submit_command(&"set_staff_work_assignment", {&"staff_id": PORTER, &"facility_id": warehouse, &"template_id": &"DAY"}).is_success(), "real recurring shift assigns warehouse porter")
for minute: int in range(40):
if runtime.staff_service.get_staff(PORTER).state == StaffRuntime.State.AVAILABLE: break
clock.advance_minutes(1)
expect(runtime.staff_service.get_staff(PORTER).state == StaffRuntime.State.AVAILABLE, "porter physically checks in")
expect(runtime.submit_command(&"purchase_supplies", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 8}).is_success(), "all cargo8 originate in ordinary paid purchase")
expect(runtime.submit_command(&"create_logistics_request", {&"request_id": RETURN_REQUEST, &"source_inventory_id": inv(warehouse), &"destination_inventory_id": inv(kitchen), &"item_requirements": {SupplyCatalog.RAW_FOOD: 8}}).is_success(), "actual reserved request starts porter delivery")
for minute: int in range(40):
if runtime.logistics_requests[RETURN_REQUEST].state == LogisticsRequest.State.DELIVERING: break
clock.advance_minutes(1)
var request: LogisticsRequest = runtime.logistics_requests[RETURN_REQUEST]
var cargo: StringName = StringName("cargo:%s" % PORTER)
expect(request.state == LogisticsRequest.State.DELIVERING and stock() == 0 and runtime.inventory_service.total_quantity(cargo, SupplyCatalog.RAW_FOOD) == 8, "real employee picks up8 paid stock into owned cargo")
if request.state != LogisticsRequest.State.DELIVERING: return
clock.advance_minutes(2)
# Disclosed expiry-lot seam preserves the exact8 paid units. No extra or free
# stock is inserted; only half the existing lot expires at the next minute.
var inventory: Dictionary = runtime.inventory_service.create_snapshot()
for owner: Dictionary in inventory[&"inventories"]:
if owner[&"owner_inventory_id"] == String(cargo):
var lot: Dictionary = owner[&"stacks"][0]
lot[&"quantity"] = 4
var expiring: Dictionary = lot.duplicate(true)
expiring[&"stack_id"] = "boundary-expiring-paid-lot"
expiring[&"expires_at"] = clock.absolute_minute + 1
(owner[&"stacks"] as Array).append(expiring)
(inventory[&"known_stack_ids"] as Array).append("boundary-expiring-paid-lot")
expect(runtime.inventory_service.restore_snapshot(inventory), "disclosed expiry split preserves8 actual paid units")
clock.advance_minutes(1)
request = runtime.logistics_requests[RETURN_REQUEST]
expect(runtime.supply_logistics.is_returning(RETURN_REQUEST) and runtime.inventory_service.total_quantity(cargo, SupplyCatalog.RAW_FOOD) == 4, "actual bridge retargets unspoiled paid4 as return cargo")
expect(plan(4).is_success(), "player enables auto4 while actual4 cargo are returning")
var before: Dictionary = runtime.create_snapshot()
var report: Dictionary = runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)
expect(report[&"enabled"] and report[&"physical_quantity"] == 0 and report[&"incoming_quantity"] == 4 and report[&"deficit"] == 0 and report[&"state"] == &"TARGET_REACHED", "actual return cargo4 satisfies target despite warehouse physical0")
expect(runtime.supply_management.advance_auto_purchases(runtime, clock.absolute_minute).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "actual incoming return suppresses duplicate whole-state purchase")
record("return-cargo-pending", before, report)
var cash: int = state.get_resource(&"gold")
for minute: int in range(40):
if request.state == LogisticsRequest.State.CANCELLED: break
clock.advance_minutes(1)
expect(request.state == LogisticsRequest.State.CANCELLED and request.state_reason == "CARGO_EXPIRED_RETURNED" and stock() == 4 and state.get_resource(&"gold") == cash, "actual porter return unloads paid4 with no duplicate auto charge")
expect(runtime.inventory_service.total_quantity(cargo, SupplyCatalog.RAW_FOOD) == 0 and runtime.logistics_dispatcher.get_reserved_load(PORTER) == 0 and SupplyLogisticsBridge.validate_crosslinks(runtime.logistics_requests, runtime.staff_service, runtime.inventory_service, runtime.facility_service), "actual return clears cargo/task/reservations/carrier ownership")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "actual completed return plus enabled intent strictly restores14")
record("return-cargo-completed", before, runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD))
@@ -0,0 +1 @@
uid://pyqycsjyyeuo
@@ -0,0 +1,274 @@
extends SceneTree
## Artifact-only acceptance input, not a new standard test or production value.
## The warehouse is normally paid/built, full durability10000, and auto6 is paid.
## A disclosed existing Defense command fixture attacks it through real automatic
## 5-minute rounds. No pre-damage, direct status/suspension, mock outcome or clock
## assignment is used. Threat10000/interval5 are existing test inputs; 201 rounds
## and1010 minutes merely allow real max50 damage/round to exhaust full10000.
## Mana0/recovery uses the public resource seam and actual Runtime projection;
## this is not a claim of natural outage/thief/bankruptcy threat generation.
const SAVE_PATH := "user://p05-enabled-auto-destruction.ttwb"
const RAID := &"p05.auto.destroy.raid"
const SOURCE_PATHS: Array[String] = [
"autoload/simulation_runtime.gd", "scripts/supply/supply_management_service.gd",
"scripts/supply/supply_auto_purchase_plans.gd", "scripts/ui/supply_management_panel.gd",
"scripts/defense/defense_service.gd", "scripts/facility/facility_instance.gd",
"scripts/facility/facility_service.gd", "scripts/mana/mana_power_projection.gd",
"scripts/core/save_service.gd", "scripts/core/simulation_snapshot.gd",
]
var runtime: Variant
var clock: Variant
var state: Variant
var warehouse: StringName = &""
var output := ""
var mode := ""
var save_path := SAVE_PATH
var checks := 0
var failures := 0
var purchase_notifications := 0
var summary: Dictionary = {}
func _initialize() -> void:
call_deferred("_run")
func expect(ok: bool, label: String) -> void:
checks += 1
if ok: print(" ✓ ", label)
else:
failures += 1
push_error("P05_AUTO_DESTRUCTION: " + label)
func _run() -> void:
var args := OS.get_cmdline_user_args()
if args.size() == 1 and args[0] == "--write": mode = "write"
elif args.size() == 1 and args[0] == "--read": mode = "read"
output = OS.get_environment("THE_TREE_AUTO_DESTRUCTION_OUTPUT")
var user_dir := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_USER_DIR")
var res_dir := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_RES_DIR")
var profile := OS.get_environment("THE_TREE_AUTO_PURCHASE_EXPECTED_PROFILE")
var any_external_guard := false
for key: String in ["THE_TREE_AUTO_DESTRUCTION_OUTPUT", "THE_TREE_AUTO_PURCHASE_EXPECTED_USER_DIR", "THE_TREE_AUTO_PURCHASE_EXPECTED_RES_DIR", "THE_TREE_AUTO_PURCHASE_EXPECTED_PROFILE"]:
any_external_guard = any_external_guard or OS.has_environment(key)
var guards := false
if any_external_guard:
guards = not mode.is_empty() and output.is_absolute_path() and DirAccess.dir_exists_absolute(output) and not FileAccess.file_exists(output.path_join(mode + "-summary.json")) and user_dir.is_absolute_path() and OS.get_user_data_dir() == user_dir and res_dir.is_absolute_path() and ProjectSettings.globalize_path("res://").simplify_path().trim_suffix("/") == res_dir.simplify_path().trim_suffix("/") and not profile.is_empty() and bool(ProjectSettings.get_setting("application/config/use_custom_user_dir", false)) and ProjectSettings.get_setting("application/config/custom_user_dir_name", "") == profile
elif args.is_empty():
mode = "standard"
var nonce := "%d_%d" % [OS.get_process_id(), Time.get_ticks_usec()]
var user_test_root := "user://tests/p05_auto_destruction_" + nonce
save_path = user_test_root.path_join("formal.ttwb")
output = ProjectSettings.globalize_path(user_test_root.path_join("proofs"))
guards = not DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(user_test_root)) and DirAccess.make_dir_recursive_absolute(output) == OK
user_dir = OS.get_user_data_dir()
res_dir = ProjectSettings.globalize_path("res://")
profile = String(ProjectSettings.get_setting("application/config/custom_user_dir_name", ""))
if not guards:
push_error("P05_AUTO_DESTRUCTION exact mode/profile/resource/fresh output guards failed")
quit(91)
return
runtime = root.get_node("SimulationRuntime")
clock = root.get_node("GameClock")
state = root.get_node("GameState")
clock.set_process(false)
_reset_fresh_runtime()
summary = {"mode": mode, "actual_user_data_dir": OS.get_user_data_dir(), "actual_resource_dir": ProjectSettings.globalize_path("res://"), "actual_profile": profile, "configured_attack_fixture": true, "natural_threat_generation_acceptance": false, "production_balance_change": false, "whole_P05_acceptance": false, "source_sha256": {}}
for path: String in SOURCE_PATHS:
summary["source_sha256"][path] = FileAccess.get_sha256("res://" + path)
expect(runtime.create_snapshot()[&"systems"][&"runtime"][&"runtime_schema_version"] == 14, "exact current14 snapshot required")
if mode in ["write", "standard"]: _write_phase()
if mode == "standard" and failures == 0:
_reset_fresh_runtime()
_read_phase()
elif mode == "read": _read_phase()
summary["checks"] = checks
summary["failures"] = failures
summary["save_path"] = ProjectSettings.globalize_path(save_path)
summary["two_process_restarted_in_this_mode"] = mode == "read"
var file := FileAccess.open(output.path_join(mode + "-summary.json"), FileAccess.WRITE)
if file == null:
push_error("P05_AUTO_DESTRUCTION cannot open fresh summary")
quit(92)
return
file.store_string(JSON.stringify(summary, "\t", true, true) + "\n")
file.close()
print("P05_AUTO_DESTRUCTION phase=%s checks=%d failures=%d" % [mode, checks, failures])
print("Supply auto purchase destruction: %d checks, %d failures" % [checks, failures])
quit(1 if failures > 0 else 0)
func _reset_fresh_runtime() -> void:
clock.reset(1, 6, 0)
state.reset()
expect(runtime.initialize(clock, state, 420_506)[&"valid"], "canonical14 Runtime initializes")
clock.set_paused(true)
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
purchase_notifications = 0
func _command(kind: StringName, payload: Dictionary) -> CommandResult:
return runtime.submit_command(kind, payload, &"player", warehouse, 75)
func _stock() -> int:
var facility: FacilityInstance = runtime.facility_service.get_instance(warehouse)
return runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD) if facility != null else -1
func _plan() -> Dictionary:
return runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)
func _auto_payment_count() -> int:
var count := 0
for entry: LedgerEntry in runtime.economy_service.get_entries():
if String(entry.memo_key).begins_with(SupplyAutoPurchasePlans.RECEIPT_PREFIX): count += 1
return count
func _observe_payment(_id: StringName, entries: Array) -> void:
for entry: LedgerEntry in entries:
if String(entry.memo_key).begins_with(SupplyAutoPurchasePlans.RECEIPT_PREFIX): purchase_notifications += 1
func _typed(label: String, value: Dictionary) -> void:
var path := output.path_join(label + ".var")
expect(not FileAccess.file_exists(path), "fresh typed proof: " + label)
if FileAccess.file_exists(path): return
var file := FileAccess.open(path, FileAccess.WRITE)
expect(file != null, "typed proof opens: " + label)
if file == null: return
file.store_buffer(var_to_bytes(value))
file.close()
func _write_phase() -> void:
var absolute_save := ProjectSettings.globalize_path(save_path)
expect(not DirAccess.dir_exists_absolute(absolute_save) and not FileAccess.file_exists(absolute_save), "fresh formal save never overwrites an existing package")
if DirAccess.dir_exists_absolute(absolute_save) or FileAccess.file_exists(absolute_save): return
expect(_command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(-8, 0, 1)}).is_success(), "normal paid warehouse construction begins")
clock.advance_minutes(180)
for facility: FacilityInstance in runtime.facility_service.get_instances():
if facility.definition_id == &"warehouse": warehouse = facility.instance_id
expect(not warehouse.is_empty(), "normal warehouse completes without synthetic Facility instance")
if warehouse.is_empty(): return
var original: FacilityInstance = runtime.facility_service.get_instance(warehouse)
expect(original.durability == FacilityInstance.DEFAULT_DURABILITY and original.durability == 10000 and original.suspension_reason.is_empty(), "paid warehouse starts at full10000 integrity without pre-damage")
runtime.economy_service.transaction_posted.connect(_observe_payment)
var cash: int = state.get_resource(&"gold")
expect(_command(&"set_supply_auto_purchase", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 6}).is_success(), "player enables auto6 before actual combat")
clock.advance_minutes(1)
expect(_stock() == 6 and state.get_resource(&"gold") == cash - 18 and _auto_payment_count() == 1 and purchase_notifications == 1, "operating warehouse actually buys6 once for18G before attack")
var pre_combat: Dictionary = runtime.create_snapshot()
var owned_before: Dictionary = _plan()
_typed("before-configured-attack", pre_combat)
var definition := {&"source_id": &"p05.disclosed.configured_attack", &"entry_points": [&"gate.north"], &"objective_ids": [warehouse], &"round_minutes": 5, &"max_duration_minutes": 1010, &"retreat_policy": &"HOLD", &"defender_retreat_threshold": 0, &"terrain_bonus": 0, &"command_bonus": 0, &"surprise_penalty": 0, &"multiple_entry_penalty": 0, &"waves": [{&"wave_id": &"p05.auto.destroy.wave", &"threat_strength": 10000, &"max_rounds": 201, &"retreat_threshold": 0, &"entry_points": [&"gate.north"]}]}
expect(_command(&"create_defense_raid", {&"raid_id": RAID, &"definition": definition}).is_success(), "disclosed configured attack created through actual Runtime command")
expect(_command(&"register_defense_asset", {&"raid_id": RAID, &"asset_id": warehouse, &"kind": &"OBJECTIVE", &"max_integrity": FacilityInstance.DEFAULT_DURABILITY, &"defense": 0, &"options": {&"target_order": 1}}).is_success(), "real full-durability warehouse registered as actual objective")
expect(_command(&"start_defense_raid", {&"raid_id": RAID}).is_success(), "actual raid starts without injected destruction or mock outcome")
var rng_before: Dictionary = runtime.get_rng_stream(&"defense").snapshot()
clock.advance_minutes(4)
expect(runtime.defense_service.get_raid(RAID)[&"current_round"] == 0 and runtime.get_rng_stream(&"defense").snapshot() == rng_before, "deadline-minus1 does not attack or consume RNG")
clock.advance_minutes(1)
for _index: int in range(200):
var ongoing: Dictionary = runtime.defense_service.get_raid(RAID)
if ongoing[&"state"] in DefenseService.TERMINAL_STATES: break
clock.advance_minutes(5)
var raid: Dictionary = runtime.defense_service.get_raid(RAID)
var destroyed: FacilityInstance = runtime.facility_service.get_instance(warehouse)
var damage := 0
var target_matches := true
for round_value: Variant in (raid[&"resolved_rounds"] as Dictionary).values():
var round_row: Dictionary = round_value
var hit: Dictionary = round_row[&"defense_damage"]
damage += int(hit.get(&"damage", 0))
target_matches = target_matches and hit.get(&"target_type", &"") == &"ASSET" and hit.get(&"target_id", &"") == warehouse
expect(raid[&"state"] == DefenseService.RaidState.BREACHED and raid[&"current_round"] == 200 and damage == 10000 and target_matches, "200 real automatic combat rounds destroy full10000 warehouse via actual outcome boundary")
expect(destroyed.durability == 0 and destroyed.state == FacilityInstance.State.BROKEN and destroyed.suspension_reason == &"DESTROYED" and destroyed.operation_state == FacilityInstance.OperationState.SUSPENDED, "Defense terminal projection owns actual persistent DESTROYED suspension")
expect(var_to_bytes(_plan()) == var_to_bytes(owned_before) and _stock() == 6 and _auto_payment_count() == 1 and purchase_notifications == 1, "combat preserves enabled intent and its paid stock/receipt without repeat procurement")
expect(runtime.economy_service.validate_ledger()[&"ok"] and not runtime.get_operations_report()[&"tick_blocked"], "combat settles without ledger or unrelated Operations block")
# A normal post-destruction input makes a real positive deficit; no inventory
# is consumed/replaced and no destroyed/operational status is directly set.
expect(_command(&"set_supply_auto_purchase", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 7}).is_success(), "ordinary player target7 is retained even though receiver is destroyed")
_check_blocked("post-combat", 6)
var checkpoint: Dictionary = runtime.create_snapshot()
_typed("formal-checkpoint14", checkpoint)
var saved: Dictionary = runtime.save_to_path(save_path, {&"purpose": &"p05.actual_defense_destroyed_auto"})
expect(saved.get(&"ok", false), "actual Runtime writes fresh formal current14 package")
var inspected: Dictionary = SaveService.inspect_snapshot(save_path)
expect(inspected.get(&"ok", false) and var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(checkpoint), "read-only formal package contains exact whole typed checkpoint14")
summary["warehouse_id"] = String(warehouse)
summary["actual_rounds"] = raid[&"current_round"]
summary["actual_damage"] = damage
summary["actual_resolved_rounds"] = (raid[&"resolved_rounds"] as Dictionary).size()
summary["before_attack_plan"] = owned_before
summary["saved_plan"] = _plan()
summary["auto_payment_count"] = _auto_payment_count()
summary["automatic_purchase_notifications"] = purchase_notifications
func _check_blocked(label: String, expected_stock: int) -> void:
var facility: FacilityInstance = runtime.facility_service.get_instance(warehouse)
var report: Dictionary = runtime.supply_management.auto_purchase_report(runtime, warehouse, SupplyCatalog.RAW_FOOD)
expect(facility != null and facility.durability == 0 and facility.suspension_reason == &"DESTROYED" and not facility.allows_logistics(), label + ": actual destroyed receiver rejects Logistics")
expect(report[&"enabled"] and report[&"target_stock"] == 7 and report[&"physical_quantity"] == expected_stock and report[&"incoming_quantity"] == 0 and report[&"deficit"] == 1 and report[&"reason"] == &"FACILITY_CLOSED", label + ": positive real shortage exposes destroyed facility block")
var before: Dictionary = runtime.create_snapshot()
expect(runtime.supply_management.advance_auto_purchases(runtime, clock.absolute_minute).is_empty() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), label + ": blocked procurement preserves every typed aggregate/ID/receipt/event/RNG byte")
_typed(label + "-blocked-before", before)
_typed(label + "-blocked-after", runtime.create_snapshot())
func _read_phase() -> void:
var expected_file := FileAccess.open(output.path_join("formal-checkpoint14.var"), FileAccess.READ)
var resumed_context := "new process" if mode == "read" else "fresh Runtime reinitialization"
expect(expected_file != null, resumed_context + ": retains actual whole typed checkpoint")
if expected_file == null: return
var expected_value: Variant = bytes_to_var(expected_file.get_buffer(expected_file.get_length()))
expected_file.close()
expect(expected_value is Dictionary, "prior typed checkpoint is a Dictionary")
if not expected_value is Dictionary: return
var expected: Dictionary = expected_value
var inspected: Dictionary = SaveService.inspect_snapshot(save_path)
expect(inspected.get(&"ok", false) and var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(expected), resumed_context + ": same exact current14 formal package")
var loaded: Dictionary = runtime.load_from_path(save_path)
expect(loaded.get(&"ok", false), resumed_context + ": ordinary Runtime load commits strict14 destroyed checkpoint")
if not loaded.get(&"ok", false): return
for facility: FacilityInstance in runtime.facility_service.get_instances():
if facility.definition_id == &"warehouse": warehouse = facility.instance_id
expect(not warehouse.is_empty(), "loaded original warehouse identity remains present")
if warehouse.is_empty(): return
var normal_loaded: Dictionary = runtime.create_snapshot()
_typed("normal-runtime-load14", normal_loaded)
# SAVE_LOADED publishes a legitimate event. Strict restore of that same
# package separately proves whole-state byte equality, without hiding it.
expect(runtime.restore_snapshot(expected) and var_to_bytes(runtime.create_snapshot()) == var_to_bytes(expected), "strict14 restore reproduces every saved typed byte; normal load event kept separately")
var plan_before: Dictionary = _plan()
var inventory_before: Dictionary = runtime.inventory_service.create_snapshot()
var economy_before: Dictionary = runtime.economy_service.create_snapshot()
runtime.economy_service.transaction_posted.connect(_observe_payment)
_check_blocked("loaded-restored", 6)
var minute_before_power: int = clock.absolute_minute
var positive_mana: int = state.get_resource(&"mana")
expect(positive_mana > 0, "pre-outage reserve is actually positive")
state.set_resource(&"mana", 0) # Explicit reserve boundary, actual power projection.
clock.advance_minutes(1)
var outage_facility: FacilityInstance = runtime.facility_service.get_instance(warehouse)
expect(not outage_facility.power_available and outage_facility.durability == 0 and outage_facility.suspension_reason == &"DESTROYED", "actual Mana gate outage changes power only; destroyed status persists")
var outage: Dictionary = runtime.create_snapshot()
_typed("actual-power-outage14", outage)
expect(runtime.restore_snapshot(outage) and var_to_bytes(runtime.create_snapshot()) == var_to_bytes(outage), "strict14 outage snapshot retains destroyed facility and owned enabled plan")
state.set_resource(&"mana", positive_mana)
clock.advance_minutes(1)
var recovered_power: FacilityInstance = runtime.facility_service.get_instance(warehouse)
expect(recovered_power.power_available and recovered_power.durability == 0 and recovered_power.suspension_reason == &"DESTROYED" and recovered_power.operation_state == FacilityInstance.OperationState.SUSPENDED, "actual power recovery cannot clear persistent destruction or resume operations")
_check_blocked("power-restored", 6)
expect(var_to_bytes(_plan()) == var_to_bytes(plan_before) and _stock() == 6 and _auto_payment_count() == 1 and purchase_notifications == 0, "restored power produces no stock/payment/notification and preserves explicit intent history")
expect(clock.absolute_minute == minute_before_power + 2, "two ordinary elapsed projection ticks are separate from no-op procurement")
# Inventory/Economy current_minute legitimately advances with the two clock
# ticks. Compare complete participant state after normalizing only this field.
var inventory_after: Dictionary = runtime.inventory_service.create_snapshot()
var economy_after: Dictionary = runtime.economy_service.create_snapshot()
inventory_after[&"current_minute"] = inventory_before[&"current_minute"]
economy_after[&"current_minute"] = economy_before[&"current_minute"]
expect(var_to_bytes(inventory_after) == var_to_bytes(inventory_before) and var_to_bytes(economy_after) == var_to_bytes(economy_before), "two public Mana projection ticks preserve all stock IDs/reservations/history and payment bytes except declared clocks")
var final: Dictionary = runtime.create_snapshot()
expect(runtime.restore_snapshot(final) and var_to_bytes(runtime.create_snapshot()) == var_to_bytes(final), "after power recovery whole14 snapshot strictly restores without implicit repair")
_typed("final14", final)
summary["warehouse_id"] = String(warehouse)
summary["same_package_two_processes"] = mode == "read"
summary["owned_plan_preserved"] = var_to_bytes(_plan()) == var_to_bytes(plan_before)
summary["actual_power_available_after_recovery"] = recovered_power.power_available
summary["persistent_suspension"] = String(recovered_power.suspension_reason)
summary["auto_payment_count"] = _auto_payment_count()
summary["automatic_purchase_notifications_after_load"] = purchase_notifications
@@ -0,0 +1 @@
uid://cqccwfjhooq1t
+2 -2
View File
@@ -19,7 +19,7 @@ func _run() -> void:
source[&"clock"].advance_minutes(3)
var canonical: Dictionary = source_runtime.create_snapshot()
var canonical_data: Dictionary = canonical[&"systems"][&"runtime"]
_expect(canonical_data[&"runtime_schema_version"] == 13, "current Runtime emits schema v13")
_expect(canonical_data[&"runtime_schema_version"] == 14, "current Runtime emits schema v14")
_expect(canonical_data[&"kitchen_production"][&"current_minute"] == source[&"clock"].absolute_minute, "production clock follows Runtime clock without existing kitchens")
var legacy := canonical.duplicate(true)
var legacy_data: Dictionary = legacy[&"systems"][&"runtime"]
@@ -35,7 +35,7 @@ func _run() -> void:
_expect(legacy_data.has(&"facility_infrastructure") and legacy_data.has(&"route_projection"), "v6 fixture retains infrastructure introduced in v6")
_expect(first_runtime.restore_snapshot(legacy), "genuine v6 state restores into v9")
var migrated: Dictionary = first_runtime.create_snapshot()
_expect(migrated[&"systems"][&"runtime"][&"runtime_schema_version"] == 13, "v6 migration re-emits current schema")
_expect(migrated[&"systems"][&"runtime"][&"runtime_schema_version"] == 14, "v6 migration re-emits current schema")
var kitchen: Dictionary = migrated[&"systems"][&"runtime"][&"kitchen_production"]
_expect(kitchen[&"plans"].is_empty() and kitchen[&"active_batches"].is_empty(), "legacy migration does not infer kitchen production plans or batches")
_expect(kitchen[&"current_minute"] == source[&"clock"].absolute_minute, "legacy production clock binds to the restored Runtime minute")