Compare commits
25
Commits
c366012446
...
e8b3dbe7a4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8b3dbe7a4 | ||
|
|
83b170a4c7 | ||
|
|
40364345ab | ||
|
|
a8edd3c3a1 | ||
|
|
62d1dcecad | ||
|
|
0754f412a8 | ||
|
|
50ceca0b7d | ||
|
|
d743bd67b6 | ||
|
|
f6f9c9c0d4 | ||
|
|
0f23d34e9f | ||
|
|
28e3ffe146 | ||
|
|
ad2ea79c7d | ||
|
|
8917c190cf | ||
|
|
619b5e07ed | ||
|
|
728e5e612f | ||
|
|
cca5457454 | ||
|
|
0cfaec1626 | ||
|
|
7e6e540837 | ||
|
|
29782a3f0a | ||
|
|
826ef606df | ||
|
|
db5ca62e42 | ||
|
|
a1538a8633 | ||
|
|
f247d19325 | ||
|
|
65534cfd15 | ||
|
|
ae0f998ff4 |
@@ -63,6 +63,12 @@ test-s2:
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_economy_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_ledger_backed_resources.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_network_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_reserve_cycle.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_runtime_mana_reserve.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_recovery_new_pid.gd -- writer
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_mana_recovery_new_pid.gd -- reader
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_runtime_operations_cycle.gd
|
||||
|
||||
test-s3:
|
||||
@@ -95,6 +101,9 @@ test-s6:
|
||||
|
||||
test-mvp:
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_whole_tower_outage.gd -- writer
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_whole_tower_outage.gd -- reader
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_premium_room.gd
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_original_room_zones.gd
|
||||
$(GODOT) --headless --path . --script res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
@@ -231,6 +240,12 @@ test-transport:
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_transport_density_migration.gd
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_transport_boundaries.gd
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_runtime_transport.gd
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_runtime_warp.gd -- writer
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_runtime_warp.gd -- reader
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_route_wait_choice.gd
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_route_wait_choice.gd -- writer
|
||||
$(GODOT) --headless --path . --script res://tests/transport/test_route_wait_choice.gd -- reader
|
||||
$(GODOT) --headless --path . --script res://tests/s2/test_new_pid_pairs.gd
|
||||
|
||||
capture-transport:
|
||||
THE_TREE_TRANSPORT_CAPTURE=1 $(GODOT) --path . --script res://tests/transport/test_runtime_transport.gd
|
||||
|
||||
@@ -29,6 +29,8 @@ const AutoPurchasePlansType := preload("res://scripts/supply/supply_auto_purchas
|
||||
const CLEANING_RUNTIME_SNAPSHOT_VERSION := 12
|
||||
const WEATHER_RUNTIME_SNAPSHOT_VERSION := 11
|
||||
const FacilityCleanliness = preload("res://scripts/environment/facility_cleanliness_service.gd")
|
||||
const ManaReserveCycle = preload("res://scripts/mana/mana_reserve_cycle.gd")
|
||||
const WarpService = preload("res://scripts/transport/warp_service.gd")
|
||||
const CleaningBridge = preload("res://scripts/environment/facility_cleaning_bridge.gd")
|
||||
const CleaningPolicy = preload("res://scripts/environment/facility_cleaning_policy.gd")
|
||||
const UPKEEP_RUNTIME_SNAPSHOT_VERSION := 10
|
||||
@@ -108,6 +110,7 @@ var facility_cleanliness := FacilityCleanliness.new()
|
||||
var cleaning_policy := CleaningPolicy.new()
|
||||
var cleaning_bridge := CleaningBridge.new()
|
||||
var facility_upkeep := FacilityUpkeepAccrual.new()
|
||||
var mana_reserve := ManaReserveCycle.new()
|
||||
var upkeep_settlement := FacilityUpkeepSettlement.new()
|
||||
var economy_service: EconomyService
|
||||
var construction_resource_state: LedgerBackedResourceState
|
||||
@@ -441,7 +444,7 @@ func initialize(clock: SimulationKernel, state: Variant, seed := DEFAULT_WORLD_S
|
||||
)
|
||||
_initialized = true
|
||||
var report := definition_registry.rebuild_from_paths(
|
||||
FacilityCatalog.FACILITY_PATHS + FacilityCatalog.INFRASTRUCTURE_FACILITY_PATHS + FacilityCatalog.RESCUE_FACILITY_PATHS + FacilityCatalog.ORIGINAL_FACILITY_PATHS)
|
||||
FacilityCatalog.FACILITY_PATHS + FacilityCatalog.INFRASTRUCTURE_FACILITY_PATHS + FacilityCatalog.RESCUE_FACILITY_PATHS + FacilityCatalog.ORIGINAL_FACILITY_PATHS + FacilityCatalog.TRANSPORT_FACILITY_PATHS)
|
||||
var catalog_registration_ok := _catalog_definitions_are_registered(guest_service)
|
||||
report[&"content_catalog"] = mvp_content_report.duplicate(true)
|
||||
report[&"progression_initialized"] = initial_progression_ok
|
||||
@@ -539,6 +542,7 @@ func create_snapshot() -> Dictionary:
|
||||
&"path": path_service.create_snapshot(),
|
||||
&"movement_credits": _serialize_movement_credits(),
|
||||
&"mana": mana_network_service.create_snapshot(),
|
||||
&"mana_reserve": mana_reserve.create_snapshot(),
|
||||
&"economy": economy_service.create_snapshot(),
|
||||
&"facility_upkeep": facility_upkeep.create_snapshot(),
|
||||
&"upkeep_settlement": upkeep_settlement.create_snapshot(),
|
||||
@@ -676,6 +680,8 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
runtime_current_keys.append(&"auto_purchase_plans")
|
||||
if runtime_data.has(&"special_peeping_rooms"):
|
||||
runtime_current_keys.append(&"special_peeping_rooms")
|
||||
if runtime_data.has(&"mana_reserve"):
|
||||
runtime_current_keys.append(&"mana_reserve")
|
||||
if not _dictionary_has_exact_canonical_keys(runtime_data, runtime_current_keys) \
|
||||
or typeof(runtime_data.get(&"runtime_schema_version", null)) != TYPE_INT \
|
||||
or typeof(runtime_data.get(&"world_seed", null)) != TYPE_INT \
|
||||
@@ -857,6 +863,23 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
var restored_mana := ManaNetworkService.new()
|
||||
if not restored_mana.restore_snapshot(runtime_data[&"mana"]):
|
||||
return false
|
||||
# A06: saves without the finite reserve keep an explicit stored amount
|
||||
# (0 stays stopped) only within capacity; nothing is silently truncated.
|
||||
var restored_mana_reserve := ManaReserveCycle.new()
|
||||
var saved_game_resources: Variant = snapshot[&"game_state"].get(&"resources", null)
|
||||
if not saved_game_resources is Dictionary:
|
||||
return false
|
||||
var saved_mana: Variant = saved_game_resources.get(&"mana", saved_game_resources.get("mana", null))
|
||||
if runtime_data.has(&"mana_reserve"):
|
||||
# Between boundaries Mana may exceed capacity; the next boundary records
|
||||
# and discards the excess, so only a valid integer amount is required.
|
||||
if not ManaReserveCycle.stored_amount_is_valid(saved_mana) \
|
||||
or not restored_mana_reserve.restore_snapshot(runtime_data[&"mana_reserve"]) \
|
||||
or restored_mana_reserve.get_minute() > restored_minute:
|
||||
return false
|
||||
elif not ManaReserveCycle.amount_is_valid(saved_mana) \
|
||||
or not restored_mana_reserve.start_at(restored_minute):
|
||||
return false
|
||||
var restored_economy := EconomyService.new()
|
||||
if not restored_economy.restore_snapshot(runtime_data[&"economy"]):
|
||||
return false
|
||||
@@ -1235,6 +1258,8 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
if not _validate_elevator_crosslinks(restored_elevators, restored_path, restored_construction,
|
||||
restored_guests, restored_staff, restored_runtime_domain_minute):
|
||||
return false
|
||||
if not WarpService.connectors_match(restored_path, restored_construction.get_facilities()):
|
||||
return false
|
||||
for facility: FacilityInstance in restored_facilities.get_instances():
|
||||
for staff_id: StringName in facility.active_staff_ids:
|
||||
if not restored_elevators.get_actor_trip(_staff_path_actor_id(staff_id)).is_empty() and not CleaningBridge.is_deployed(restored_staff.get_staff(staff_id), facility, restored_staff):
|
||||
@@ -1325,6 +1350,7 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
_movement_credits = (restored_movement_credits as Dictionary).duplicate(true)
|
||||
_connect_path_performance_signal()
|
||||
mana_network_service = restored_mana
|
||||
mana_reserve = restored_mana_reserve
|
||||
weather = restored_weather
|
||||
facility_cleanliness = restored_cleanliness
|
||||
facility_upkeep = restored_upkeep
|
||||
@@ -2054,6 +2080,7 @@ func _build_operations_external_state() -> Dictionary:
|
||||
&"personal_action_points": _personal_action_points,
|
||||
# The remaining aggregates are reachable through Event actions or signals.
|
||||
&"mana": mana_network_service.create_snapshot(),
|
||||
&"mana_reserve": mana_reserve.create_snapshot(),
|
||||
&"logistics_dispatcher": logistics_dispatcher.create_snapshot(),
|
||||
&"logistics_requests": _serialize_logistics_requests(),
|
||||
&"defense": defense_service.create_snapshot(),
|
||||
@@ -2091,7 +2118,7 @@ func _restore_operations_external_state(token: Variant) -> bool:
|
||||
&"excavation", &"spatial_subslots", &"route_projection",
|
||||
&"facility_infrastructure",
|
||||
&"movement_credits", &"domain_events", &"id_generator", &"rng_streams",
|
||||
&"progression", &"hired_staff_candidate_ids", &"mana",
|
||||
&"progression", &"hired_staff_candidate_ids", &"mana", &"mana_reserve",
|
||||
&"logistics_dispatcher", &"defense", &"construction",
|
||||
]
|
||||
for key: StringName in required_dictionary_keys:
|
||||
@@ -2152,6 +2179,7 @@ func _restore_operations_external_state(token: Variant) -> bool:
|
||||
var ids_ok := id_generator.restore_snapshot(saved[&"id_generator"])
|
||||
var progression_ok := progression_service.restore_snapshot(saved[&"progression"])
|
||||
var mana_ok := mana_network_service.restore_snapshot(saved[&"mana"])
|
||||
mana_ok = mana_reserve.restore_snapshot(saved[&"mana_reserve"]) and mana_ok
|
||||
var dispatcher_ok := logistics_dispatcher.restore_snapshot(saved[&"logistics_dispatcher"])
|
||||
var defense_ok := defense_service.restore_snapshot(saved[&"defense"])
|
||||
var construction_ok := construction_service.restore_snapshot(saved[&"construction"])
|
||||
@@ -2258,6 +2286,7 @@ func _operations_external_state_matches(saved: Dictionary) -> bool:
|
||||
or not _strict_variants_equal(id_generator.create_snapshot(), saved[&"id_generator"]) \
|
||||
or not _strict_variants_equal(progression_service.create_snapshot(), saved[&"progression"]) \
|
||||
or not _strict_variants_equal(mana_network_service.create_snapshot(), saved[&"mana"]) \
|
||||
or not _strict_variants_equal(mana_reserve.create_snapshot(), saved[&"mana_reserve"]) \
|
||||
or not _strict_variants_equal(logistics_dispatcher.create_snapshot(), saved[&"logistics_dispatcher"]) \
|
||||
or not _strict_variants_equal(_serialize_logistics_requests(), saved[&"logistics_requests"]) \
|
||||
or not _strict_variants_equal(defense_service.create_snapshot(), saved[&"defense"]) \
|
||||
@@ -5226,6 +5255,8 @@ func _initialize_economy() -> void:
|
||||
upkeep_settlement = FacilityUpkeepSettlement.new()
|
||||
FacilityUpkeepSettlement.prepare_accounts(economy_service)
|
||||
facility_upkeep.start_at(_clock.absolute_minute)
|
||||
mana_reserve = ManaReserveCycle.new()
|
||||
mana_reserve.start_at(_clock.absolute_minute)
|
||||
construction_resource_state = _create_resource_adapter(
|
||||
economy_service,
|
||||
CONSTRUCTION_EXPENSE_ACCOUNT,
|
||||
@@ -7816,7 +7847,8 @@ func _execute_demolish_facility(command: SimulationCommand) -> CommandResult:
|
||||
demolished.command_id = command.command_id
|
||||
var transport_ok := false
|
||||
if demolished.is_success():
|
||||
transport_ok = bool(_synchronize_elevators(_runtime_event_minute()).get(&"ok", false)) and _prune_legacy_stairs_and_movement()
|
||||
transport_ok = bool(_synchronize_elevators(_runtime_event_minute()).get(&"ok", false)) \
|
||||
and bool(_synchronize_warps().get(&"ok", false)) and _prune_legacy_stairs_and_movement()
|
||||
if not transport_ok or not demolished.is_success() \
|
||||
or not _validate_runtime_path_crosslinks(path_service, guest_service, staff_service, _movement_credits, _runtime_event_minute()) \
|
||||
or facility_infrastructure_projector.has_projection(instance_id) \
|
||||
@@ -8331,6 +8363,11 @@ func _apply_completed_facility_internal(instance: Dictionary, absolute_minute: i
|
||||
var transport := _synchronize_elevators(absolute_minute)
|
||||
if not bool(transport.get(&"ok", false)):
|
||||
return {&"ok": false, &"code": &"ELEVATOR_SYNC_FAILED", &"reason": elevators.last_error, &"details": transport}
|
||||
if definition.id in WarpService.DEFINITION_IDS:
|
||||
_register_facility_mana_consumer(instance)
|
||||
var warp_sync := _synchronize_warps()
|
||||
if not bool(warp_sync.get(&"ok", false)):
|
||||
return {&"ok": false, &"code": &"WARP_SYNC_FAILED", &"reason": String(warp_sync.get(&"reason", "")), &"details": warp_sync}
|
||||
if not _prune_legacy_stairs_and_movement():
|
||||
return {&"ok": false, &"code": &"LEGACY_STAIR_PRUNE_FAILED", &"reason": "Stair topology could not be updated."}
|
||||
if not _publish_domain_event(SimulationDomainEvent.new(
|
||||
@@ -8759,6 +8796,10 @@ func get_guest_decision_record(guest_id: StringName) -> Dictionary:
|
||||
|
||||
func _advance_guest_ai_decisions(absolute_minute: int) -> void:
|
||||
for guest: GuestRuntime in guest_service.get_guests():
|
||||
# A rider inside a cabin cannot decide, leave or despawn until the
|
||||
# elevator sets it down at a real landing (P14-03: passengers never vanish).
|
||||
if elevators.get_actor_trip(_guest_path_actor_id(guest.guest_id)).get(&"state", &"") == PathService.ELEVATOR_REQUEST_BOARDED:
|
||||
continue
|
||||
match guest.state:
|
||||
GuestRuntime.State.SPAWNING:
|
||||
guest_service.transition_guest(guest.guest_id, GuestRuntime.State.ENTERING, absolute_minute, &"ENTRY_STARTED")
|
||||
@@ -8948,7 +8989,8 @@ func _advance_guest_path_travel(
|
||||
actor_id, guest.current_cell, absolute_minute
|
||||
)
|
||||
accumulated = mini(accumulated, required_credit + MOVEMENT_CREDIT_SCALE - 1)
|
||||
if accumulated < required_credit:
|
||||
var guest_warp := _warp_step_connector(path[0], path[1], [])
|
||||
if accumulated < required_credit or not _warp_fare_affordable(guest_warp, 1):
|
||||
_record_blocked_actor_wait(
|
||||
actor_id,
|
||||
guest.guest_id,
|
||||
@@ -8972,6 +9014,7 @@ func _advance_guest_path_travel(
|
||||
guest.recovery_kind = previous_recovery_kind
|
||||
_report_path_integration_failure(actor_id, guest.guest_id, &"guest", previous_cell)
|
||||
return
|
||||
_charge_warp_fare(guest_warp, [actor_id], previous_cell, guest.current_cell)
|
||||
var remaining_credit := accumulated - required_credit
|
||||
_movement_credits[actor_id] = {
|
||||
&"credit": remaining_credit,
|
||||
@@ -9074,7 +9117,9 @@ func _advance_staff_task_travel(
|
||||
actor_id, staff.current_cell, absolute_minute
|
||||
)
|
||||
accumulated = mini(accumulated, required_credit + MOVEMENT_CREDIT_SCALE - 1)
|
||||
if accumulated < required_credit:
|
||||
var staff_warp := _warp_step_connector(path[0], path[1], [&"staff"])
|
||||
var warp_riders := 2 if task.task_type == &"rescue_transport" else 1
|
||||
if accumulated < required_credit or not _warp_fare_affordable(staff_warp, warp_riders):
|
||||
_record_blocked_actor_wait(
|
||||
actor_id,
|
||||
staff.staff_id,
|
||||
@@ -9098,6 +9143,9 @@ func _advance_staff_task_travel(
|
||||
return
|
||||
patient.current_cell = staff.current_cell
|
||||
_record_expected_actor_wait(_staff_path_actor_id(patient.staff_id), patient.current_cell, absolute_minute)
|
||||
_charge_warp_fare(staff_warp, [actor_id, _staff_path_actor_id(patient.staff_id)], previous_cell, staff.current_cell)
|
||||
else:
|
||||
_charge_warp_fare(staff_warp, [actor_id], previous_cell, staff.current_cell)
|
||||
var remaining_credit := accumulated - required_credit
|
||||
_movement_credits[actor_id] = {
|
||||
&"credit": remaining_credit,
|
||||
@@ -10713,6 +10761,30 @@ func _reservation_references_are_valid(
|
||||
return false
|
||||
return true
|
||||
|
||||
## HUD view of the A06 reserve: supply, consumption and storage per interval.
|
||||
func get_mana_reserve_report() -> Dictionary:
|
||||
var demand := _mana_reserve_demand()
|
||||
return {
|
||||
&"amount": int(_state.get_resource(&"mana")),
|
||||
&"capacity": ManaReserveCycle.CAPACITY,
|
||||
&"generation": ManaReserveCycle.WORLD_TREE_GENERATION,
|
||||
&"interval_minutes": ManaReserveCycle.INTERVAL_MINUTES,
|
||||
&"demand": demand,
|
||||
&"net": ManaReserveCycle.WORLD_TREE_GENERATION - demand,
|
||||
&"last_flow": mana_reserve.get_last_flow(),
|
||||
}
|
||||
|
||||
|
||||
## A06 upkeep demand: every owned consumer, open or not, except destroyed
|
||||
## facilities. Removed facilities are already unregistered from the network.
|
||||
func _mana_reserve_demand() -> int:
|
||||
var destroyed: Array[StringName] = []
|
||||
for facility: FacilityInstance in facility_service.get_instances():
|
||||
if facility.suspension_reason == &"DESTROYED":
|
||||
destroyed.append(facility.instance_id)
|
||||
return mana_network_service.get_total_consumer_demand(destroyed)
|
||||
|
||||
|
||||
func _advance_mana(absolute_minute: int) -> void:
|
||||
if _operations_tick_blocked:
|
||||
return
|
||||
@@ -10729,12 +10801,32 @@ func _advance_mana(absolute_minute: int) -> void:
|
||||
infrastructure_preflight.get(&"details", {})
|
||||
)
|
||||
return
|
||||
# A06: generation and upkeep demand settle before local distribution and the
|
||||
# whole-tower gate, so the network and projection see the committed reserve.
|
||||
var reserve_flow := mana_reserve.advance_to(
|
||||
absolute_minute, int(_state.get_resource(&"mana")), _mana_reserve_demand()
|
||||
)
|
||||
if not bool(reserve_flow.get(&"ok", false)):
|
||||
_latch_infrastructure_failure(
|
||||
StringName(reserve_flow.get(&"code", &"MANA_RESERVE_FAILED")),
|
||||
absolute_minute,
|
||||
"The finite Mana reserve could not settle this boundary.",
|
||||
{&"mana": int(_state.get_resource(&"mana"))}
|
||||
)
|
||||
return
|
||||
if bool(reserve_flow.get(&"processed", false)):
|
||||
_state.set_resource(&"mana", int(reserve_flow[&"after"]))
|
||||
var report := mana_network_service.advance_one_minute(absolute_minute)
|
||||
# Finite reserve is an additional gate, even between network evaluations and
|
||||
# for zero-demand facilities. The network remains the local supply authority.
|
||||
preload("res://scripts/mana/mana_power_projection.gd").apply(
|
||||
facility_service, mana_network_service, int(_state.get_resource(&"mana")), absolute_minute
|
||||
)
|
||||
var warp_sync := _synchronize_warps()
|
||||
if not bool(warp_sync.get(&"ok", false)):
|
||||
_latch_infrastructure_failure(&"WARP_SYNC_FAILED", absolute_minute,
|
||||
"Warp connectors could not follow the current power state.", warp_sync)
|
||||
return
|
||||
for change: Dictionary in report.get(&"state_changes", []):
|
||||
var consumer_id := StringName(change.get(&"consumer_id", ""))
|
||||
_publish_domain_event(SimulationDomainEvent.new(
|
||||
@@ -12754,7 +12846,8 @@ func advance_staff_commute(staff_id: StringName, target_cell: Vector3i, minute:
|
||||
var required_credit := _step_movement_credit(path_service, path[0], path[1], [&"staff"])
|
||||
var credit := _movement_credit_after_one_minute(actor_id, staff.current_cell, minute)
|
||||
credit = mini(credit, required_credit + MOVEMENT_CREDIT_SCALE - 1)
|
||||
if credit < required_credit:
|
||||
var commute_warp := _warp_step_connector(path[0], path[1], [&"staff"])
|
||||
if credit < required_credit or not _warp_fare_affordable(commute_warp, 1):
|
||||
_record_expected_actor_wait(actor_id, staff.current_cell, minute)
|
||||
_movement_credits[actor_id] = {&"credit": credit, &"updated_at": minute}
|
||||
return {&"ok": true, &"moved": false}
|
||||
@@ -12763,6 +12856,7 @@ func advance_staff_commute(staff_id: StringName, target_cell: Vector3i, minute:
|
||||
_report_path_integration_failure(actor_id, staff_id, &"staff", previous)
|
||||
return {&"ok": false}
|
||||
staff.current_cell = path[1]
|
||||
_charge_warp_fare(commute_warp, [actor_id], previous, staff.current_cell)
|
||||
_movement_credits[actor_id] = {&"credit": credit - required_credit, &"updated_at": minute}
|
||||
_publish_actor_event(&"STAFF_MOVED", &"staff_commute", [staff_id, actor_id], {
|
||||
&"path_actor_id": actor_id, &"from_cell": previous, &"cell": staff.current_cell,
|
||||
@@ -12846,6 +12940,42 @@ func _execute_set_elevator_operation(command: SimulationCommand) -> CommandResul
|
||||
return CommandResult.succeeded(command.command_id, {&"shaft_id": shaft_id, &"operator_open": open_value})
|
||||
|
||||
|
||||
## A07: warp connectors exist for every completed warp pair and are usable only
|
||||
## while both warps are powered and the reserve can pay one fare.
|
||||
func _synchronize_warps() -> Dictionary:
|
||||
var available := {}
|
||||
var reserve_ok := int(_state.get_resource(&"mana")) > 0
|
||||
for warp: Dictionary in WarpService.warps_from_facilities(construction_service.get_facilities()):
|
||||
var state := mana_network_service.get_consumer_state(warp[&"instance_id"])
|
||||
available[warp[&"instance_id"]] = reserve_ok and state in [ManaNetworkService.ConsumerState.ACTIVE, ManaNetworkService.ConsumerState.DEGRADED]
|
||||
return WarpService.synchronize(path_service, construction_service.get_facilities(), available,
|
||||
int(_state.get_resource(&"mana")) >= WarpService.MANA_PER_TRIP)
|
||||
|
||||
|
||||
func _warp_step_connector(from: Vector3i, to: Vector3i, access: Array[StringName]) -> StringName:
|
||||
var connector_id := path_service.get_step_connector(from, to, access)
|
||||
return connector_id if WarpService.is_warp_connector(connector_id) else &""
|
||||
|
||||
|
||||
func _warp_fare_affordable(connector_id: StringName, riders: int) -> bool:
|
||||
return connector_id.is_empty() or int(_state.get_resource(&"mana")) >= WarpService.MANA_PER_TRIP * riders
|
||||
|
||||
|
||||
## Charged once, in the same tick as the committed step; affordability was
|
||||
## checked before the step, so the deduction cannot fail here.
|
||||
func _charge_warp_fare(connector_id: StringName, actor_ids: Array, from: Vector3i, to: Vector3i) -> void:
|
||||
if connector_id.is_empty():
|
||||
return
|
||||
var fare := WarpService.MANA_PER_TRIP * actor_ids.size()
|
||||
_state.apply_resource_deltas({&"mana": -fare})
|
||||
var subjects: Array[StringName] = [connector_id]
|
||||
for actor_id: Variant in actor_ids:
|
||||
subjects.append(StringName(actor_id))
|
||||
_publish_actor_event(&"WARP_TRAVELED", &"warp", subjects, {
|
||||
&"connector_id": connector_id, &"from_cell": from, &"cell": to,
|
||||
&"riders": actor_ids.size(), &"mana": fare})
|
||||
|
||||
|
||||
func _synchronize_elevators(minute: int) -> Dictionary:
|
||||
return elevators.synchronize(ElevatorService.configurations_from_facilities(
|
||||
construction_service.get_facilities(), definition_registry), minute)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[gd_resource type="Resource" script_class="FacilityDefinition" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/data/facility_definition.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
id = &"mana_warp"
|
||||
display_name = "マナワープ"
|
||||
description = "建設済みの表層ワープ同士を瞬時につなぐ客用の転移陣。客が1回3マナで移動する。従業員は裏層の業務用ワープを使う。"
|
||||
zone = 0
|
||||
layer = 0
|
||||
footprint = Vector2i(2, 2)
|
||||
construction_cost = {&"gold": 3000, &"materials": 30}
|
||||
build_minutes = 360
|
||||
weight_per_cell = 3
|
||||
pass_through = true
|
||||
maintenance_gold = 15
|
||||
maintenance_mana = 1
|
||||
power_priority = 80
|
||||
staff_min = 0
|
||||
staff_max = 0
|
||||
capacity = 4
|
||||
queue_capacity = 0
|
||||
base_service_minutes = 1
|
||||
base_quality = 50
|
||||
base_price = 0
|
||||
tags = Array[StringName]([&"guest_access", &"movement", &"schema_facility_proxy", &"warp"])
|
||||
@@ -0,0 +1,27 @@
|
||||
[gd_resource type="Resource" script_class="FacilityDefinition" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/data/facility_definition.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
id = &"service_warp"
|
||||
display_name = "業務用マナワープ"
|
||||
description = "建設済みの業務用ワープ同士を瞬時につなぐ裏層の転移陣。従業員が1回3マナで移動する。"
|
||||
zone = 0
|
||||
layer = 1
|
||||
footprint = Vector2i(2, 2)
|
||||
construction_cost = {&"gold": 3000, &"materials": 30}
|
||||
build_minutes = 360
|
||||
weight_per_cell = 3
|
||||
pass_through = true
|
||||
maintenance_gold = 15
|
||||
maintenance_mana = 1
|
||||
power_priority = 80
|
||||
staff_min = 0
|
||||
staff_max = 0
|
||||
capacity = 4
|
||||
queue_capacity = 0
|
||||
base_service_minutes = 1
|
||||
base_quality = 50
|
||||
base_price = 0
|
||||
tags = Array[StringName]([&"movement", &"schema_facility_proxy", &"staff_only", &"warp"])
|
||||
@@ -1,3 +1,27 @@
|
||||
## 現在の対象(2026-10-20・正式21成果/P14-03はRunnerゲート待ち)
|
||||
|
||||
元30パッケージ・88成果単位で、部分点はありません。正式完了は21成果、完了パッケージは3(P00、P04、P06)です。
|
||||
- 2026-10-19 以降に正式完了したもの: P06-01、P06-03、P14-02、P06-02。いずれも、ユーザーが承認判断を委任した体制で、独立Opusレビューの合格を受けて記録しました。
|
||||
- 承認済みの決定: A06(有限マナ)、A07(客用マナワープ)、A08(裏層の業務用ワープ)。
|
||||
|
||||
**P14-03「混雑と経路選択」**
|
||||
- 正確候補は `83b170a` です。次の証拠がそろっています。
|
||||
- 正式全回帰: 192/192 suite、22,198 checks、失敗0
|
||||
- Root の別PID pair: 5/5、10 PID
|
||||
- 代表性能 r02: 収集と機能検証は合格し、版間の比較が成立
|
||||
- 独立Opusの whole-unit レビュー: PASS
|
||||
- ただし skill 契約では、完了を確定するのは Runner です(試験・独立レビュー・最新mainとの統合・最終試験)。そのため Runner の正式ゲートが終わるまで加算0です。登録案と、既存証拠を結合する検証器は `artifacts/original-completion-2026-10-02/p14-03-runner-gate-r01/` にあります。
|
||||
|
||||
**未達として明記している性能**
|
||||
- 代表負荷での1分 tick は平均約827 ms で、x1 の予算 4 ms を両版とも大きく超えています。
|
||||
- x3 frame、stall、描画、Windows は未測定です。
|
||||
|
||||
**過去の回帰の訂正**: P06-01・P06-03・P14-02・P06-02 当時の回帰は Makefile 経由で、roster にしか載っていない46 suite を含んでいませんでした。その46 suite で見つかった回帰(v15保存の拒否など)は `83b170a` で修正済みで、正式192 suite の実行で全件合格を確認しました。各単位の完了記録には訂正注記を付けています。
|
||||
|
||||
**次の候補**: P28-01「現HEADの性能基準」です。描画とx3はheadlessの結果で代用せず、新たに測定します。P05-02・P22-03(仕様が未承認)とP02-03(Windows実機待ち)は保留ですが、作業全体は止めません。
|
||||
|
||||
以下の「現在」は各記録時点の履歴です。上記と現在の台帳を優先してください。
|
||||
|
||||
## 現在の対象(2026-10-19・P04-01正式完了/開発体制更新)
|
||||
|
||||
P04-01「同時発生の更新規約」は通常候補 `b1810ff4b2809018fe7434be7bdcf6f6411c91f4` で全条件・独立Opusレビュー・統合・最終検証を完了。元30パッケージ/88成果、部分点なしで正式17成果、完了2パッケージ。詳細は[正式受入証拠](../../../artifacts/original-completion-2026-10-02/p04-01-formal-closure-b1810ff4/ACCEPTANCE.json)と `work_packages.json` / `progress_snapshot.json` を参照。
|
||||
|
||||
@@ -33,6 +33,29 @@
|
||||
"decision": "2×1・裏層。建築350G、資材4、工期60分、清掃員最低1人/上限2人、顧客0人・利用料なし、維持費2G/日。清潔0〜100・初期100、75以下で予約、現地8分で25回復。定義済み汚れ量を使い未定義値を補完しない。",
|
||||
"origin": "user_reply",
|
||||
"reference": "../original-completion-2026-09-29/execution/CLEANING_APPROVED_FOUNDATION.md"
|
||||
},
|
||||
{
|
||||
"id": "A06",
|
||||
"title": "マナ有限残量契約(MP-01-r02推薦案)",
|
||||
"decision": "初期100・容量300・世界樹生成20/ゲーム内10分。10分境界ごとにB+G+F−Dを計算し、需要を先に支払い、超過は明示破棄、0で全館停止。需要は保有施設のmaintenance_mana合計(閉店・停止中も計上、撤去・破壊済みは除外)。境界間に返却等で容量を超えた分は次の境界でoverflowとして記録・破棄。炉の投入は自然生成と需要支払後のbase_after=min(C,max(0,B+G−D))で空き100以上のとき受理。現Runtimeのphase順序を保持しMANA内で処理。受入契約なしの旧セーブの残量は0〜300のみ保持し、超過・不正値は拒否。炉の人物適格性・収率の実接続は別承認。",
|
||||
"origin": "user_reply_in_conversation",
|
||||
"approved_at_jst": "2026-10-19",
|
||||
"reference": "execution/mana-proposal/PROPOSAL.md"
|
||||
},
|
||||
{
|
||||
"id": "A07",
|
||||
"title": "マナワープの初期値(推薦案)",
|
||||
"decision": "2x2・表層。建設3000G・資材30、工期360分、維持費15G/日、維持マナ1/10分。建設済みワープは全て相互接続。利用者は客と接客中の従業員。1人1回3マナを出発時に一回だけ差し引く。残量不足・停電・到着先不可のときは経路から除外し、費用と移動を発生させない。途中保存・再開で費用と移動を二重反映しない。",
|
||||
"origin": "user_reply_in_conversation",
|
||||
"approved_at_jst": "2026-10-20"
|
||||
},
|
||||
{
|
||||
"id": "A08",
|
||||
"title": "ワープの客用・業務用分離",
|
||||
"decision": "表層のマナワープは客用とし、従業員の経路からは使わない。従業員用に裏層の業務用マナワープ(2x2・裏層)を別に設け、業務用同士だけを相互接続し従業員のみ利用する。業務用の建設費・工期・維持費・維持マナ・1人3マナの運賃はA07と同値。",
|
||||
"origin": "user_reply_in_conversation「従業員用を裏層に別で作って」",
|
||||
"approved_at_jst": "2026-10-20",
|
||||
"values_note": "業務用の数値はユーザー指定がなくA07と同値を適用。"
|
||||
}
|
||||
],
|
||||
"pending": [
|
||||
|
||||
+138
-2
@@ -3723,8 +3723,8 @@
|
||||
"path": "docs/spec/acceptance/P08-03.json",
|
||||
"scope": "normal placement; native and OS pending"
|
||||
}
|
||||
}
|
||||
,{
|
||||
},
|
||||
{
|
||||
"test": "tests/ui/test_p23_life_observation.gd",
|
||||
"template": "P23 life observation: <checks> checks, <failures> failures",
|
||||
"required": true,
|
||||
@@ -3789,6 +3789,142 @@
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p13-02/CODE1_TASK_SPEC.json",
|
||||
"scope": "new backend suite marker; native execution pending"
|
||||
}
|
||||
},
|
||||
{
|
||||
"test": "tests/mvp/test_whole_tower_outage.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "dd786e8c7448eeb77d51d7effd9100059ae6febf638ca2c20f3a55683009c9ea",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 41,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/s2/test_mana_recovery_new_pid.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "8ef67859db2a31213585fa49dfc687ea69bf215c41c8d1d8cbb3f84d00dd5d17",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 26,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/s2/test_mana_reserve_cycle.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "048d79e067cc85eef1c5c5f3e412198e652a2acc08e6d910e0761c7148a34cb6",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 64,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if failures == 0 else \"FAIL\", checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/s2/test_mana_reserve_new_pid.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "9f42e4f7f5aac403ee4a6091e54ebf3995e25d11a450000e5a8ce870094f7a6b",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 26,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/s2/test_runtime_mana_reserve.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "323750a533f2bbdf95b96b1615996cd3c4155bdadc6dcfb0936eaaa19a04809b",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 25,
|
||||
"text": "\t\tprint(\"PASS: %d checks\" % _checks)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/transport/test_route_wait_choice.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "c4db1db2e60f1a24fbbcb6c2c3706a7ee1534c7191f612e1d8df40d0a39a97bc",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 49,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/transport/test_runtime_warp.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "cfbb69e63aca12d9d198393fd853c76be63df95e8d1e777e7a14194dbd181234",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new suite added with P06/P14 work; exact label/schema only, check count is not an expected future value"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 40,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"test": "tests/s2/test_new_pid_pairs.gd",
|
||||
"template": "PASS: <checks> checks",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
"source_sha256_at_schema_audit": "9742f9ac253067e6ae69a53faed054d71925f316d038ff7c5e480c3a9be91b04",
|
||||
"authority": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-03-route-wait/EVIDENCE_MATRIX.md",
|
||||
"scope": "Claude session 2026-10-20: new-PID pair suite; exact label/schema only"
|
||||
},
|
||||
"source_marker_lines": [
|
||||
{
|
||||
"line": 45,
|
||||
"text": "\tprint(\"%s: %d checks\" % [\"PASS\" if _failures == 0 else \"FAIL\", _checks])"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"unknown_roster_suite_policy": "refuse, explicitly review its source/schema before accepting",
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"unit": "P06-01",
|
||||
"title": "供給と蓄積の契約",
|
||||
"candidate": "f247d19",
|
||||
"base": "c366012",
|
||||
"decision": "A06",
|
||||
"status": "independent_review_pass_awaiting_owner_approval",
|
||||
"acceptance": "初期値や係数の根拠を区別し、旧版移行と残量境界で矛盾がない。",
|
||||
"cross_cutting": {
|
||||
"原本条件とUI操作が一致": "HUDに残量/容量と生成・容量・停止条件を表示(scenes/main/main.gd)。原本は全館停止のみ規定し数値は承認値A06。",
|
||||
"Runtimeの実利用・会計・行動へ接続": "MANA phaseで境界精算→局所配電→全館ゲート。",
|
||||
"途中保存・再起動で再現": "test_mana_reserve_new_pid writer/reader別PIDで残量・receipt・全状態digest一致、旧0セーブ移行。",
|
||||
"異常/境界/中断を検証": "同境界再送、逆行、境界飛ばし、境界間の容量超過→overflow、改ざん・旧版容量超過の原子的拒否、破壊施設除外、Operations巻き戻し。",
|
||||
"関連回帰PASS": "142本中118 PASS。失敗24本は基準c366012でも同一終了コード(UI/audio owner cleanup、resources still in use、保存パッケージ残骸1件)でマナ起因なし。",
|
||||
"要件別ログとソースハッシュを保存": "本ファイルのsource_sha256、regression-f247d19.log。",
|
||||
"既存代表負荷の性能回帰を計測": "perf-r01.json:1日tick 1008→1014ms(+0.6%)、snapshot +1.5%、save差は誤差内。描画・Windowsは対象外。"
|
||||
},
|
||||
"reviews": [
|
||||
{
|
||||
"round": 1,
|
||||
"candidate": "65534cf",
|
||||
"verdict": "FAIL",
|
||||
"blocking": [
|
||||
"境界間のマナ増加で読込拒否/INVALID_FLOW latch",
|
||||
"test_runtime_mana_depletion回帰",
|
||||
"破壊済み施設の需要計上"
|
||||
]
|
||||
},
|
||||
{
|
||||
"round": 2,
|
||||
"candidate": "f247d19",
|
||||
"verdict": "PASS",
|
||||
"blocking": [],
|
||||
"non_blocking": [
|
||||
"change_resourcesで10億超にすると境界でlatch(通常プレイ外)",
|
||||
"reserve分が古い現行セーブは読込後に境界を順に追いつく(方針どおり)",
|
||||
"HUDが境界間に容量超過値を表示",
|
||||
"test_runtime_transportはaudio cleanup 1件のみ失敗(基準線と同種)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"regression_failures_baseline_identical": [
|
||||
"res://tests/mvp/test_amelia_donations.gd",
|
||||
"res://tests/mvp/test_fine_cleaning_effect.gd",
|
||||
"res://tests/s1/test_path_snapshot_transit_guard.gd",
|
||||
"res://tests/supply/test_normal_auto_purchase_chain.gd",
|
||||
"res://tests/supply/test_normal_supply_chain.gd",
|
||||
"res://tests/transport/test_runtime_transport.gd",
|
||||
"res://tests/ui/test_anri_recruitment.gd",
|
||||
"res://tests/ui/test_chapel_gameplay.gd",
|
||||
"res://tests/ui/test_dashboard_coalescing.gd",
|
||||
"res://tests/ui/test_dimension_migration_ui.gd",
|
||||
"res://tests/ui/test_first_operating_day.gd",
|
||||
"res://tests/ui/test_guest_decision_log.gd",
|
||||
"res://tests/ui/test_guest_observation.gd",
|
||||
"res://tests/ui/test_layer_skin.gd",
|
||||
"res://tests/ui/test_original_build_pointer.gd",
|
||||
"res://tests/ui/test_rank_pointer.gd",
|
||||
"res://tests/ui/test_staff_management.gd",
|
||||
"res://tests/ui/test_staff_rescue.gd",
|
||||
"res://tests/ui/test_staff_rest.gd",
|
||||
"res://tests/ui/test_stair_transport_details.gd",
|
||||
"res://tests/ui/test_supply_auto_purchase_ui.gd",
|
||||
"res://tests/ui/test_upkeep_inspector.gd",
|
||||
"res://tests/ui/test_world_tree_rank.gd",
|
||||
"tests/ui/test_cleaning_gameplay.gd"
|
||||
],
|
||||
"source_sha256": {
|
||||
"scripts/mana/mana_reserve_cycle.gd": "7d372d6e944765de5799803a4f5f60babf129741e6de30fe2452f133a4524d9a",
|
||||
"scripts/mana/mana_network_service.gd": "3f28a9b291cc00efd1495a9adf11a37988ce13e8c8ace01a950fd0075b6d7950",
|
||||
"autoload/simulation_runtime.gd": "22dae2e188ce4e099aad5cfcdcd2103c5ec979858740a7dbba46444153e066fa",
|
||||
"scenes/main/main.gd": "bf45d29b3ea49147651097b783e1d6422a024a22f5422bb38aa1517677ab66ba",
|
||||
"tests/helpers/mana_outage.gd": "8df68f67fd023fb3fed4c465acf8227094d0e9403efc1f56ae009685629563e1",
|
||||
"tests/s2/test_mana_reserve_cycle.gd": "048d79e067cc85eef1c5c5f3e412198e652a2acc08e6d910e0761c7148a34cb6",
|
||||
"tests/s2/test_runtime_mana_reserve.gd": "323750a533f2bbdf95b96b1615996cd3c4155bdadc6dcfb0936eaaa19a04809b",
|
||||
"tests/s2/test_mana_reserve_new_pid.gd": "9f42e4f7f5aac403ee4a6091e54ebf3995e25d11a450000e5a8ce870094f7a6b"
|
||||
},
|
||||
"not_claimed": [
|
||||
"炉の人物投入の実接続",
|
||||
"未定義13施設の需要",
|
||||
"D05詳細",
|
||||
"全館の実プレイ・Windows実機",
|
||||
"P06全体の完了"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# P06-01:A06 マナ有限残量契約の実装(2026-10-19)
|
||||
|
||||
ユーザーが MP-01-r02 推薦案を採用しました(decisions.json A06)。内容は初期100、容量300、世界樹の生成20/ゲーム内10分です。炉は、自然生成と需要の支払いを済ませた後に判定します。
|
||||
|
||||
## 製品への反映
|
||||
|
||||
- `scripts/mana/mana_reserve_cycle.gd`:承認値と境界処理です。10分境界ごとに `available=B+G+F`、`consumed=min(available,D)`、`after=min(C,available−consumed)` を計算します。容量を超えた分は overflow として記録し、破棄します。イベント費用の返却などで境界の間に容量を超えた場合も、次の境界で同じく処理します。同じ境界は二度処理しません。境界を飛ばした場合は、各境界を順番に処理します。保存データは検証が通ってから反映し、不正なら何も変えません。炉の受理判定は `furnace_offer_accepted`(base_after 後の空きが100以上)です。
|
||||
- `scripts/mana/mana_network_service.gd`:`get_total_consumer_demand(excluded_ids)` を追加しました。登録済みの consumer の需要を合計します。無効化・停止中の consumer も含みます。Runtime の `_mana_reserve_demand()` が、破壊済み(DESTROYED)施設を除外して呼び出します。撤去済みの施設はネットワークから登録解除されます。
|
||||
- `autoload/simulation_runtime.gd`:MANA phase の最初で残量を確定してから、局所配電と全館ゲートを評価します。phase の順序は変えていません。新しいセーブは `mana_reserve` を持ちます。Operations の巻き戻し対象にも含めました。
|
||||
- 現行セーブは整数の残量を受け付けます。境界の間に容量を超えた値も読み込めます。
|
||||
- 旧セーブの移行:`mana_reserve` が無いセーブは、残量が0〜300の整数であれば、その値を保持して開始します。明示的な0は停止のまま保持します。300超・負値・非整数は拒否し、現在の状態を保持します。
|
||||
|
||||
## 試験
|
||||
|
||||
- 新規:`tests/s2/test_mana_reserve_cycle.gd`(提案書の比較表の推薦列の値、炉判定の3例、改ざん拒否)、`tests/s2/test_runtime_mana_reserve.gd`(Runtimeでの境界処理、保存往復、旧0の移行、容量超過の拒否、枯渇と復帰)。
|
||||
- 既存の停電試験は「マナを0にすれば停電が続く」前提でした。A06では世界樹が次の境界で復帰させるため、停電を継続させる試験だけ `tests/helpers/mana_outage.gd` に置き換えました。これは配電を受けない無効 consumer で需要を足す方法です。
|
||||
|
||||
## 未了
|
||||
|
||||
- 炉の人物適格性・収率の実接続、未定義13施設の需要、D05 の詳細は別承認です。
|
||||
- 全館の実プレイ、性能、Windows での受入は未実施です。原88の正式完了数は変わりません。
|
||||
|
||||
## 独立レビュー r01 の指摘と修正
|
||||
|
||||
候補 65534cf への判定は FAIL でした。修正した内容は次のとおりです。
|
||||
|
||||
1. 境界の間にマナが増えた場合の不整合を直しました。以前は、読込を拒否するか、毎分 INVALID_FLOW を起こして全館が停止していました。現在は、次の境界で overflow として記録・破棄します。
|
||||
2. `test_runtime_mana_depletion` は停電が続く前提でした。`ManaOutage` に置き換え、旧セーブも `mana_reserve` を外して作るようにしました。また、Makefile の test-mvp に追加しました。
|
||||
3. 需要に破壊済みの施設が含まれていました。提案書の「撤去・破壊済みは除外」に合わせて除外し、A06 の記録文も同じ内容に訂正しました。
|
||||
|
||||
## 未検証として残す点
|
||||
|
||||
- 世界樹の生成を0にする経路が無いため、提案書の比較表にある「生成0→0→通常」は適用外です。
|
||||
- 炉の receipt と配電投影の保存は、炉の実接続が別承認なので対象外です。
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"probe": "perf_probe.gd",
|
||||
"baseline_source": "c366012 (autoload/simulation_runtime.gd, scripts/mana/mana_network_service.gd swapped in place)",
|
||||
"candidate_source": "working tree after ae0f998 + HUD change",
|
||||
"runs": [
|
||||
{
|
||||
"side": "BASE",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 18.752,
|
||||
"save_ms_max": 20.915,
|
||||
"snapshot_ms_avg": 0.38877,
|
||||
"tick_day_ms_avg": 1005.434,
|
||||
"tick_day_ms_max": 1022.47,
|
||||
"trials": 5
|
||||
},
|
||||
{
|
||||
"side": "CAND",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 19.2478,
|
||||
"save_ms_max": 21.626,
|
||||
"snapshot_ms_avg": 0.39343,
|
||||
"tick_day_ms_avg": 1013.9146,
|
||||
"tick_day_ms_max": 1037.407,
|
||||
"trials": 5
|
||||
},
|
||||
{
|
||||
"side": "BASE",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 19.9272,
|
||||
"save_ms_max": 22.122,
|
||||
"snapshot_ms_avg": 0.39169,
|
||||
"tick_day_ms_avg": 1010.7658,
|
||||
"tick_day_ms_max": 1021.417,
|
||||
"trials": 5
|
||||
},
|
||||
{
|
||||
"side": "CAND",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 16.6394,
|
||||
"save_ms_max": 20.276,
|
||||
"snapshot_ms_avg": 0.39521,
|
||||
"tick_day_ms_avg": 1016.2724,
|
||||
"tick_day_ms_max": 1036.077,
|
||||
"trials": 5
|
||||
},
|
||||
{
|
||||
"side": "BASE",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 18.8624,
|
||||
"save_ms_max": 21.534,
|
||||
"snapshot_ms_avg": 0.38799,
|
||||
"tick_day_ms_avg": 1007.718,
|
||||
"tick_day_ms_max": 1034.979,
|
||||
"trials": 5
|
||||
},
|
||||
{
|
||||
"side": "CAND",
|
||||
"minutes": 1440,
|
||||
"save_ms_avg": 15.2772,
|
||||
"save_ms_max": 17.564,
|
||||
"snapshot_ms_avg": 0.40309,
|
||||
"tick_day_ms_avg": 1012.192,
|
||||
"tick_day_ms_max": 1053.282,
|
||||
"trials": 5
|
||||
}
|
||||
],
|
||||
"base_mean": {
|
||||
"tick_day_ms_avg": 1007.973,
|
||||
"snapshot_ms_avg": 0.389,
|
||||
"save_ms_avg": 19.181
|
||||
},
|
||||
"cand_mean": {
|
||||
"tick_day_ms_avg": 1014.126,
|
||||
"snapshot_ms_avg": 0.397,
|
||||
"save_ms_avg": 17.055
|
||||
},
|
||||
"scope": "Ordinary new-game Runtime only, 1440 minutes x5 trials x3 runs, snapshot x20, disk save. Not rendering, Operations-heavy fixtures, or Windows."
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
extends SceneTree
|
||||
|
||||
## P06-01 A06 performance regression probe. Same script runs on the baseline
|
||||
## and candidate checkouts; prints one JSON line. Measures ordinary Runtime
|
||||
## minute ticks, full snapshots and disk saves; not rendering or Windows.
|
||||
const TRIALS := 5
|
||||
const MINUTES := 1440
|
||||
const SNAPSHOTS := 20
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var tick_ms: Array = []
|
||||
var snapshot_ms: Array = []
|
||||
var save_ms: Array = []
|
||||
for trial: int in TRIALS:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0)
|
||||
state.reset()
|
||||
runtime.initialize(clock, state, 5505 + trial)
|
||||
clock.set_paused(true)
|
||||
var start := Time.get_ticks_usec()
|
||||
clock.advance_minutes(MINUTES)
|
||||
tick_ms.append((Time.get_ticks_usec() - start) / 1000.0)
|
||||
start = Time.get_ticks_usec()
|
||||
var snapshot: Dictionary
|
||||
for i: int in SNAPSHOTS:
|
||||
snapshot = runtime.create_snapshot()
|
||||
snapshot_ms.append((Time.get_ticks_usec() - start) / 1000.0 / SNAPSHOTS)
|
||||
var path := "user://saves/p06_01_perf_probe_%d" % trial
|
||||
start = Time.get_ticks_usec()
|
||||
var saved: Dictionary = SaveService.save_snapshot(path, snapshot)
|
||||
save_ms.append((Time.get_ticks_usec() - start) / 1000.0)
|
||||
SaveService.delete_save(path)
|
||||
if not saved[&"ok"]:
|
||||
push_error("save failed")
|
||||
runtime.free(); clock.free(); state.free()
|
||||
print("P06_01_PERF=" + JSON.stringify({
|
||||
"trials": TRIALS, "minutes": MINUTES,
|
||||
"tick_day_ms_avg": _avg(tick_ms), "tick_day_ms_max": tick_ms.max(),
|
||||
"snapshot_ms_avg": _avg(snapshot_ms), "save_ms_avg": _avg(save_ms), "save_ms_max": save_ms.max(),
|
||||
}))
|
||||
quit(0)
|
||||
|
||||
|
||||
func _avg(values: Array) -> float:
|
||||
var total := 0.0
|
||||
for value: float in values:
|
||||
total += value
|
||||
return total / values.size()
|
||||
@@ -0,0 +1 @@
|
||||
uid://fstsv5yarw5v
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
PASS res://tests/run_tests.gd
|
||||
PASS res://tests/s0/test_definition_registry.gd
|
||||
PASS res://tests/s0/test_command_transaction.gd
|
||||
PASS res://tests/s0/test_determinism.gd
|
||||
PASS res://tests/s0/test_stable_id_generator.gd
|
||||
PASS res://tests/s0/test_domain_events.gd
|
||||
PASS res://tests/s0/test_runtime_integration.gd
|
||||
PASS res://tests/s1/test_dimension_batch_save.gd
|
||||
PASS res://tests/s1/test_facility_resize_preview.gd
|
||||
PASS res://tests/s1/test_weighted_resize_runtime.gd
|
||||
PASS res://tests/s1/test_original_facility_dimensions.gd
|
||||
PASS res://tests/s1/test_world_service.gd
|
||||
PASS res://tests/s1/test_path_service.gd
|
||||
FAIL(1) res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
ERROR: refusal creates no new temporary or backup package
|
||||
PASS res://tests/s1/test_path_congestion.gd
|
||||
PASS res://tests/s1/test_construction_service.gd
|
||||
PASS res://tests/s1/test_excavation_service.gd
|
||||
PASS res://tests/s1/test_spatial_subslot_service.gd
|
||||
PASS res://tests/s1/test_route_subslot_path_adapter.gd
|
||||
PASS res://tests/s1/test_facility_infrastructure_projector.gd
|
||||
PASS res://tests/s1/test_runtime_infrastructure_integration.gd
|
||||
PASS res://tests/s1/test_runtime_world_construction.gd
|
||||
PASS tests/environment/test_cleaning_access.gd
|
||||
FAIL(97) tests/ui/test_cleaning_gameplay.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 64508, &"owner_instance_id": 131617261207, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131617261207] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS tests/environment/test_runtime_cleanliness.gd
|
||||
PASS res://tests/environment/test_facility_cleanliness.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_accrual.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_settlement.gd
|
||||
PASS res://tests/s2/test_facility_service.gd
|
||||
PASS res://tests/s2/test_service_start_slots.gd
|
||||
PASS res://tests/s2/test_inventory_logistics.gd
|
||||
PASS res://tests/s2/test_economy_service.gd
|
||||
PASS res://tests/s2/test_ledger_backed_resources.gd
|
||||
PASS res://tests/s2/test_mana_network_service.gd
|
||||
PASS res://tests/s2/test_mana_reserve_cycle.gd
|
||||
PASS res://tests/s2/test_runtime_mana_reserve.gd
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_runtime_operations_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_staff_assignment.gd
|
||||
PASS res://tests/s3/test_guest_service.gd
|
||||
PASS res://tests/s3/test_staff_service.gd
|
||||
PASS res://tests/s3/test_staff_payroll_operations.gd
|
||||
PASS res://tests/s3/test_runtime_actor_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_actor_hardening.gd
|
||||
PASS res://tests/s3/test_runtime_path_traffic.gd
|
||||
PASS res://tests/s2/test_kitchen_production.gd
|
||||
PASS res://tests/supply/test_supply_save_migration.gd
|
||||
PASS res://tests/supply/test_supply_logistics_bridge.gd
|
||||
PASS res://tests/supply/test_supply_management_service.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase.gd
|
||||
FAIL(1) res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
FAIL(1) res://tests/supply/test_normal_supply_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/s2/test_facility_pricing.gd
|
||||
PASS res://tests/business/test_business_operations.gd
|
||||
PASS res://tests/business/test_business_closure.gd
|
||||
PASS res://tests/s4/test_relationship_event_service.gd
|
||||
PASS res://tests/s4/test_placement_risk_preview_service.gd
|
||||
PASS res://tests/s4/test_runtime_relationship_events.gd
|
||||
PASS res://tests/s5/test_authority_service.gd
|
||||
PASS res://tests/s5/test_defense_service.gd
|
||||
PASS res://tests/s5/test_runtime_authority_cycle.gd
|
||||
PASS res://tests/s5/test_runtime_defense_cycle.gd
|
||||
PASS res://tests/s6/test_save_operations.gd
|
||||
PASS res://tests/s6/test_save_preflight.gd
|
||||
PASS res://tests/s6/test_save_recovery_preservation.gd
|
||||
PASS res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
PASS res://tests/s6/test_performance_budget.gd
|
||||
PASS res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
PASS res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
PASS res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
PASS res://tests/mvp/test_premium_room.gd
|
||||
PASS res://tests/mvp/test_original_room_zones.gd
|
||||
PASS res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
PASS res://tests/mvp/test_upkeep_legacy_midnight.gd
|
||||
PASS res://tests/mvp/test_runtime_upkeep.gd
|
||||
PASS res://tests/mvp/test_upkeep_construction_boundaries.gd
|
||||
PASS res://tests/mvp/test_operations_coordinator.gd
|
||||
PASS res://tests/mvp/test_mvp_content_catalog.gd
|
||||
PASS res://tests/mvp/test_progression_service.gd
|
||||
PASS res://tests/mvp/test_runtime_content_progression.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_integration.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_acceptance.gd
|
||||
PASS res://tests/mvp/test_chapel_boundaries.gd
|
||||
FAIL(96) res://tests/ui/test_chapel_gameplay.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 69163, &"owner_instance_id": 179667207680, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [179667207680] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_chapel_service.gd
|
||||
FAIL(96) res://tests/ui/test_dashboard_coalescing.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69190, &"owner_instance_id": 131315271307, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131315271307] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_anri_floor_pricing.gd
|
||||
FAIL(97) res://tests/mvp/test_fine_cleaning_effect.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 69231, &"owner_instance_id": 132087023256, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132087023256] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/mvp/test_amelia_donations.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 69255, &"owner_instance_id": 180036306434, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180036306434] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_anri_recruitment.gd
|
||||
ERROR: Anri recruitment ordinary owner cleanup failed: { &"owned_engine_pid": 69276, &"owner_instance_id": 134754600590, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134754600590] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_layer_skin.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 1 resources still in use at exit (run with --verbose for details).
|
||||
FAIL(96) res://tests/ui/test_world_tree_rank.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69339, &"owner_instance_id": 134721046155, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134721046155] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_rank_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69359, &"owner_instance_id": 134771377803, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134771377803] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_layer_presentation.gd
|
||||
FAIL(96) res://tests/ui/test_original_build_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69392, &"owner_instance_id": 134704268951, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134704268951] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_dimension_migration_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69456, &"owner_instance_id": 164450272915, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [164450272915] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_upkeep_inspector.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69483, &"owner_instance_id": 131399157387, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131399157387] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rest.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69491, &"owner_instance_id": 131399157387, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131399157387] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rescue.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69507, &"owner_instance_id": 131583706763, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131583706763] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_observation.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69534, &"owner_instance_id": 131533375116, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131533375116] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_decision_log.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69549, &"owner_instance_id": 277310605252, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [277310605252] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_room_presentation.gd
|
||||
PASS res://tests/ui/test_pixel_tower.gd
|
||||
PASS res://tests/ui/test_main_dashboard.gd
|
||||
FAIL(96) res://tests/ui/test_staff_management.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 2 resources still in use at exit (run with --verbose for details).
|
||||
PASS res://tests/ui/test_supply_management.gd
|
||||
FAIL(96) res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69672, &"owner_instance_id": 134989481613, &"before": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134989481613] }, &"after": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_business_management.gd
|
||||
PASS res://tests/ui/test_elevator_management.gd
|
||||
FAIL(96) res://tests/ui/test_first_operating_day.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 69732, &"owner_instance_id": 134687491727, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134687491727] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/staff/test_dormitory_dimension_migration.gd
|
||||
PASS res://tests/staff/test_staff_dormitory.gd
|
||||
PASS res://tests/staff/test_dormitory_shift_cycle.gd
|
||||
PASS res://tests/staff/test_shift_rush.gd
|
||||
PASS res://tests/staff/test_staff_rest.gd
|
||||
PASS res://tests/staff/test_rescue_elevator.gd
|
||||
PASS res://tests/staff/test_staff_rescue.gd
|
||||
PASS res://tests/staff/test_task_work_clock.gd
|
||||
PASS res://tests/world/test_path_vertical_policy.gd
|
||||
PASS res://tests/world/test_path_transit_density.gd
|
||||
PASS res://tests/world/test_stair_infrastructure.gd
|
||||
PASS res://tests/world/test_legacy_stairs.gd
|
||||
PASS res://tests/transport/test_runtime_stairs.gd
|
||||
FAIL(96) res://tests/ui/test_stair_transport_details.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 70072, &"owner_instance_id": 132154132118, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132154132118] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/world/test_elevator_topology.gd
|
||||
PASS res://tests/transport/test_elevator_service.gd
|
||||
PASS res://tests/staff/test_staff_commute.gd
|
||||
PASS res://tests/transport/test_transport_save.gd
|
||||
PASS res://tests/transport/test_transport_density_migration.gd
|
||||
PASS res://tests/transport/test_transport_boundaries.gd
|
||||
FAIL(1) res://tests/transport/test_runtime_transport.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/performance/test_facility_reachability.gd
|
||||
PASS res://tests/performance/test_snapshot_ownership.gd
|
||||
PASS res://tests/performance/test_world_snapshot_cache.gd
|
||||
PASS res://tests/performance/test_lazy_digest.gd
|
||||
PASS res://tests/performance/test_path_membership.gd
|
||||
PASS res://tests/performance/test_system_optimization.gd
|
||||
PASS res://tests/performance/test_digest_performance.gd
|
||||
PASS res://tests/performance/test_perf_mvp_001.gd
|
||||
DONE
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"unit": "P06-02",
|
||||
"title": "全館停止の統合",
|
||||
"candidate": "f6f9c9c",
|
||||
"followup_commit_test_only": "SERVICE_SALEイベント0の検査を追加(writer 45/reader 7 PASS)",
|
||||
"status": "completed_delegated_approval",
|
||||
"acceptance": "停電中の無条件売上・生産がなく、乗客と搬送対象が消えない。",
|
||||
"evidence": "DOMAIN_MATRIX.md(7領域の共通停止判定と制御試験)+test_whole_tower_outage(需要23>生成20の自然枯渇、60分停止、別PID一致)",
|
||||
"cross_cutting": {
|
||||
"原本条件とUI操作が一致": "原本9.1全館停止。HUDの停止表示はP06-01/P06-03で受入済み。",
|
||||
"Runtimeの実利用・会計・行動へ接続": "通常建設・自然営業・実雇用の塔で自然枯渇。",
|
||||
"途中保存・再起動で再現": "停電中保存→別PID60分継続で台帳・全状態digest一致。",
|
||||
"異常/境界/中断を検証": "reviewer probeで利用中・乗車中の瞬間に自然枯渇させる8条件と120分観測、10分境界越えの持続を確認。",
|
||||
"関連回帰PASS": "149本中124 PASS、失敗24本は基準と同一。test_cleaning_gameplay/test_runtime_transportは終了処理のcleanupのみ失敗でゲーム検査は全PASS。",
|
||||
"要件別ログとソースハッシュを保存": "本ファイル・regression-f6f9c9c.log。",
|
||||
"既存代表負荷の性能回帰を計測": "本単位の製品コード変更なし(試験と記録のみ)。直前のP14-02計測を継承。"
|
||||
},
|
||||
"reviews": [
|
||||
{
|
||||
"round": 1,
|
||||
"candidate": "f6f9c9c",
|
||||
"verdict": "PASS",
|
||||
"blocking": [],
|
||||
"non_blocking": [
|
||||
"統合試験のrevenue検査は即時掃き出しで常に0、cash<=beforeは賃金で弱い、乗車trip検査は該当0件で空振り、meals/raw_food 0とワープ利用0(生産停止は厨房制御試験が担保)→SERVICE_SALE 0検査を追加",
|
||||
"TIME_BOUNDARIESがMANAより先のため、残量0に精算される当該分の到着判定は精算前残量で行われ得る(probeで2名到着、売上なしで退館)。境界端として記録",
|
||||
"停止籠の待ち客・緊急降車客は30分stuck後PATH_RECOVERY→LEAVINGで上階でdespawnする既存仕組み。停電が消すのではないが非主張として明記",
|
||||
"清掃・運営サイクル試験は持続helper未使用(取消は終端状態で結論不変)",
|
||||
"EV/ワープはFacilityInstanceでないため全施設停止の検査に含まれない(判定コードと制御試験で確認)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"not_claimed": [
|
||||
"清掃対象施設の電力判定、医務室のマナ消費者登録、直接spawn_guestの電力判定(監査で根拠未確定、追加しない)",
|
||||
"上階で停止した客のstuck退館の挙動変更",
|
||||
"Windows実機"
|
||||
],
|
||||
"regression_failures_baseline_identical": [
|
||||
"res://tests/mvp/test_amelia_donations.gd",
|
||||
"res://tests/mvp/test_fine_cleaning_effect.gd",
|
||||
"res://tests/s1/test_path_snapshot_transit_guard.gd",
|
||||
"res://tests/supply/test_normal_auto_purchase_chain.gd",
|
||||
"res://tests/supply/test_normal_supply_chain.gd",
|
||||
"res://tests/transport/test_runtime_transport.gd",
|
||||
"res://tests/ui/test_anri_recruitment.gd",
|
||||
"res://tests/ui/test_chapel_gameplay.gd",
|
||||
"res://tests/ui/test_dashboard_coalescing.gd",
|
||||
"res://tests/ui/test_dimension_migration_ui.gd",
|
||||
"res://tests/ui/test_first_operating_day.gd",
|
||||
"res://tests/ui/test_guest_decision_log.gd",
|
||||
"res://tests/ui/test_guest_observation.gd",
|
||||
"res://tests/ui/test_layer_skin.gd",
|
||||
"res://tests/ui/test_original_build_pointer.gd",
|
||||
"res://tests/ui/test_rank_pointer.gd",
|
||||
"res://tests/ui/test_staff_management.gd",
|
||||
"res://tests/ui/test_staff_rescue.gd",
|
||||
"res://tests/ui/test_staff_rest.gd",
|
||||
"res://tests/ui/test_stair_transport_details.gd",
|
||||
"res://tests/ui/test_supply_auto_purchase_ui.gd",
|
||||
"res://tests/ui/test_upkeep_inspector.gd",
|
||||
"res://tests/ui/test_world_tree_rank.gd",
|
||||
"tests/ui/test_cleaning_gameplay.gd"
|
||||
],
|
||||
"source_sha256": {
|
||||
"tests/mvp/test_whole_tower_outage.gd": "dd786e8c7448eeb77d51d7effd9100059ae6febf638ca2c20f3a55683009c9ea",
|
||||
"scripts/mana/mana_power_projection.gd": "2026467960e857349bcb18fb97b7753189be5f2de2be91a17e73e90146eba69e",
|
||||
"scripts/mana/mana_reserve_cycle.gd": "7d372d6e944765de5799803a4f5f60babf129741e6de30fe2452f133a4524d9a",
|
||||
"scripts/transport/warp_service.gd": "7602c6b8a9672a703bfa98a1424c2cf68cf50184f87f72349d564af6a5ca9d41",
|
||||
"autoload/simulation_runtime.gd": "68d03872a591955740346a894fa4139cea75ed264428d61ade181532ea398b41",
|
||||
"docs/plans/original-completion-2026-10-02/execution/p06-02-whole-stop/DOMAIN_MATRIX.md": "47e735f72ddef95b1aa15e09ed0c7ad608c49a00c79a5d5afea7bc5ab9f2f282"
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# P06-02 全館停止の統合:領域別の証拠(候補 f6f9c9c)
|
||||
|
||||
受入条件は「停電中に無条件の売上・生産がなく、乗客と搬送対象が消えない」です。停止判定はどの領域も共通で、同じ連鎖を通ります。A06 の有限残量が0になる → `mana_power_projection`(`reserve > 0` かつ局所配電)→ 各施設の `power_available` → 各領域の停止。ワープ(A07/A08)は同じ残量と各ワープの給電状態を毎分 connector に反映します。
|
||||
|
||||
| 領域 | 停止の判定 | 証拠 |
|
||||
|---|---|---|
|
||||
| 利用中サービス | 施設が NO_POWER になると、実行中のサービスを PAUSED にする。時間を進めず、課金しない | `test_runtime_operations_cycle`(停電中は一時停止、復帰後に一回だけ課金)、`test_premium_room_guest_flow`(宿泊、持続停電、ディスク保存)、`test_whole_tower_outage`(自然停電の60分間、収入・財布・売上が不変) |
|
||||
| EV | 給電が無いと籠は非稼働。乗車中の客は有効な階で緊急降車させ、安全な階が無ければ保持する | `test_runtime_transport`(緊急降車、降車不能時の保持を持続停電で確認)、`test_whole_tower_outage`(停電中の新規乗車0、残るのは保持中の乗車だけ) |
|
||||
| ワープ | 残量3未満、停電、片側の未給電、到着先の封鎖のいずれかで経路から除外。費用も移動も発生しない | `test_runtime_warp`(各条件と、別PIDでの二重反映なし)、`test_whole_tower_outage`(停電中のワープ移動0) |
|
||||
| 物流 | 両端の施設が非稼働なら引渡しを止める。荷は保持する | `test_supply_logistics_bridge::_test_reserve_outage`(持続停電30分、荷を保持、復旧後に一回だけ配達) |
|
||||
| 生産(厨房) | NO_POWER の間は批(batch)を保持し、投入も生成もしない | `test_supply_logistics_bridge::_test_kitchen_reserve_outage`(持続停電、批の同一性と予約を保持、生成0) |
|
||||
| 救護 | 医務室が OPERATING でなければ治療しない。救助の2人組は一緒に降ろす | `test_rescue_elevator`(全館停止で2人とも有効な階へ、停止中は治療されない、保存の完全一致、持続停電) |
|
||||
| 清掃 | 清掃員室が非稼働なら作業を取り消す。回復はさせない | `test_cleaning_gameplay`(停電で清掃を止め、回復させない。保存も復元される) |
|
||||
| 入館 | 残量0の間、到着の受入数を0にする | `test_runtime_mana_depletion`(停電中は受入数0)、`test_whole_tower_outage`(自然停電の60分間、新規到着0) |
|
||||
|
||||
## 自然停電の統合試験
|
||||
|
||||
`test_whole_tower_outage` は通常の操作だけで塔を作ります。客用EV・受付・客室・食堂+厨房・ワープ2基・按摩室2室・倉庫・警備員詰所で、自然営業を有効にしています。維持需要は23で、世界樹の生成20を上回るため、残量が自然に0へ落ちます。
|
||||
|
||||
停電中の60分間、次を確認しました。
|
||||
- 全施設が停止する
|
||||
- 売上・収入・残っている客の財布が動かない
|
||||
- 食事と食材が増えも減りもしない
|
||||
- 新規の到着、ワープ移動、EV への乗車がいずれも0
|
||||
- 客は塔に残るか、通常の退店で出ていくだけ
|
||||
|
||||
さらに、停電中に保存し、別PIDで60分続けた結果が writer と完全に一致しました。
|
||||
|
||||
## 適用範囲と既知の制約
|
||||
|
||||
- 統合試験の塔には在庫・物流・救護・清掃の作業がありません。これらの領域は、上の表の制御試験(持続停電の helper を使用)で確認しています。
|
||||
- 清掃と救護には別の論点が残っています。清掃は対象施設の電力を停止判定に含めるか、医務室は電力消費者として登録すべきか、という点です。どちらも P06-02 監査で「根拠未確定」とされたままで、新しい判定は追加していません。
|
||||
- 直接 `spawn_guest` コマンドで客を出す経路には、電力の判定がありません。通常の到着は残量で止まります。監査の方針(判定を追加しない)に従っています。
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
PASS res://tests/run_tests.gd
|
||||
PASS res://tests/s0/test_definition_registry.gd
|
||||
PASS res://tests/s0/test_command_transaction.gd
|
||||
PASS res://tests/s0/test_determinism.gd
|
||||
PASS res://tests/s0/test_stable_id_generator.gd
|
||||
PASS res://tests/s0/test_domain_events.gd
|
||||
PASS res://tests/s0/test_runtime_integration.gd
|
||||
PASS res://tests/s1/test_dimension_batch_save.gd
|
||||
PASS res://tests/s1/test_facility_resize_preview.gd
|
||||
PASS res://tests/s1/test_weighted_resize_runtime.gd
|
||||
PASS res://tests/s1/test_original_facility_dimensions.gd
|
||||
PASS res://tests/s1/test_world_service.gd
|
||||
PASS res://tests/s1/test_path_service.gd
|
||||
FAIL(1) res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
ERROR: refusal creates no new temporary or backup package
|
||||
PASS res://tests/s1/test_path_congestion.gd
|
||||
PASS res://tests/s1/test_construction_service.gd
|
||||
PASS res://tests/s1/test_excavation_service.gd
|
||||
PASS res://tests/s1/test_spatial_subslot_service.gd
|
||||
PASS res://tests/s1/test_route_subslot_path_adapter.gd
|
||||
PASS res://tests/s1/test_facility_infrastructure_projector.gd
|
||||
PASS res://tests/s1/test_runtime_infrastructure_integration.gd
|
||||
PASS res://tests/s1/test_runtime_world_construction.gd
|
||||
PASS tests/environment/test_cleaning_access.gd
|
||||
FAIL(97) tests/ui/test_cleaning_gameplay.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 45540, &"owner_instance_id": 132691003032, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132691003032] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS tests/environment/test_runtime_cleanliness.gd
|
||||
PASS res://tests/environment/test_facility_cleanliness.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_accrual.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_settlement.gd
|
||||
PASS res://tests/s2/test_facility_service.gd
|
||||
PASS res://tests/s2/test_service_start_slots.gd
|
||||
PASS res://tests/s2/test_inventory_logistics.gd
|
||||
PASS res://tests/s2/test_economy_service.gd
|
||||
PASS res://tests/s2/test_ledger_backed_resources.gd
|
||||
PASS res://tests/s2/test_mana_network_service.gd
|
||||
PASS res://tests/s2/test_mana_reserve_cycle.gd
|
||||
PASS res://tests/s2/test_runtime_mana_reserve.gd
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_runtime_operations_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_staff_assignment.gd
|
||||
PASS res://tests/s3/test_guest_service.gd
|
||||
PASS res://tests/s3/test_staff_service.gd
|
||||
PASS res://tests/s3/test_staff_payroll_operations.gd
|
||||
PASS res://tests/s3/test_runtime_actor_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_actor_hardening.gd
|
||||
PASS res://tests/s3/test_runtime_path_traffic.gd
|
||||
PASS res://tests/s2/test_kitchen_production.gd
|
||||
PASS res://tests/supply/test_supply_save_migration.gd
|
||||
PASS res://tests/supply/test_supply_logistics_bridge.gd
|
||||
PASS res://tests/supply/test_supply_management_service.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase.gd
|
||||
FAIL(1) res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
FAIL(1) res://tests/supply/test_normal_supply_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/s2/test_facility_pricing.gd
|
||||
PASS res://tests/business/test_business_operations.gd
|
||||
PASS res://tests/business/test_business_closure.gd
|
||||
PASS res://tests/s4/test_relationship_event_service.gd
|
||||
PASS res://tests/s4/test_placement_risk_preview_service.gd
|
||||
PASS res://tests/s4/test_runtime_relationship_events.gd
|
||||
PASS res://tests/s5/test_authority_service.gd
|
||||
PASS res://tests/s5/test_defense_service.gd
|
||||
PASS res://tests/s5/test_runtime_authority_cycle.gd
|
||||
PASS res://tests/s5/test_runtime_defense_cycle.gd
|
||||
PASS res://tests/s6/test_save_operations.gd
|
||||
PASS res://tests/s6/test_save_preflight.gd
|
||||
PASS res://tests/s6/test_save_recovery_preservation.gd
|
||||
PASS res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
PASS res://tests/s6/test_performance_budget.gd
|
||||
PASS res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
PASS res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
PASS res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
PASS res://tests/mvp/test_whole_tower_outage.gd -- writer
|
||||
PASS res://tests/mvp/test_whole_tower_outage.gd -- reader
|
||||
PASS res://tests/mvp/test_premium_room.gd
|
||||
PASS res://tests/mvp/test_original_room_zones.gd
|
||||
PASS res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
PASS res://tests/mvp/test_upkeep_legacy_midnight.gd
|
||||
PASS res://tests/mvp/test_runtime_upkeep.gd
|
||||
PASS res://tests/mvp/test_upkeep_construction_boundaries.gd
|
||||
PASS res://tests/mvp/test_operations_coordinator.gd
|
||||
PASS res://tests/mvp/test_mvp_content_catalog.gd
|
||||
PASS res://tests/mvp/test_progression_service.gd
|
||||
PASS res://tests/mvp/test_runtime_content_progression.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_integration.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_acceptance.gd
|
||||
PASS res://tests/mvp/test_chapel_boundaries.gd
|
||||
FAIL(96) res://tests/ui/test_chapel_gameplay.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 50424, &"owner_instance_id": 180740949505, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180740949505] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_chapel_service.gd
|
||||
FAIL(96) res://tests/ui/test_dashboard_coalescing.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50457, &"owner_instance_id": 132389013132, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132389013132] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_anri_floor_pricing.gd
|
||||
FAIL(97) res://tests/mvp/test_fine_cleaning_effect.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 50497, &"owner_instance_id": 133160765081, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [133160765081] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/mvp/test_amelia_donations.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 50522, &"owner_instance_id": 181110048259, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [181110048259] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_anri_recruitment.gd
|
||||
ERROR: Anri recruitment ordinary owner cleanup failed: { &"owned_engine_pid": 50550, &"owner_instance_id": 135828342415, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135828342415] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_layer_skin.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 1 resources still in use at exit (run with --verbose for details).
|
||||
FAIL(96) res://tests/ui/test_world_tree_rank.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50598, &"owner_instance_id": 135794787980, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135794787980] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_rank_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50614, &"owner_instance_id": 135845119628, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135845119628] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_layer_presentation.gd
|
||||
FAIL(96) res://tests/ui/test_original_build_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50654, &"owner_instance_id": 135778010776, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135778010776] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_dimension_migration_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50712, &"owner_instance_id": 165524014740, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [165524014740] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_upkeep_inspector.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50737, &"owner_instance_id": 132472899212, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132472899212] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rest.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50751, &"owner_instance_id": 132472899212, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132472899212] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rescue.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50777, &"owner_instance_id": 132657448588, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132657448588] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_observation.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50798, &"owner_instance_id": 132607116941, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132607116941] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_decision_log.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50807, &"owner_instance_id": 278384347081, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [278384347081] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_room_presentation.gd
|
||||
PASS res://tests/ui/test_pixel_tower.gd
|
||||
PASS res://tests/ui/test_main_dashboard.gd
|
||||
FAIL(96) res://tests/ui/test_staff_management.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 2 resources still in use at exit (run with --verbose for details).
|
||||
PASS res://tests/ui/test_supply_management.gd
|
||||
FAIL(96) res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50934, &"owner_instance_id": 136063223438, &"before": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [136063223438] }, &"after": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_business_management.gd
|
||||
PASS res://tests/ui/test_elevator_management.gd
|
||||
FAIL(96) res://tests/ui/test_first_operating_day.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 50998, &"owner_instance_id": 135761233552, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135761233552] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/staff/test_dormitory_dimension_migration.gd
|
||||
PASS res://tests/staff/test_staff_dormitory.gd
|
||||
PASS res://tests/staff/test_dormitory_shift_cycle.gd
|
||||
PASS res://tests/staff/test_shift_rush.gd
|
||||
PASS res://tests/staff/test_staff_rest.gd
|
||||
PASS res://tests/staff/test_rescue_elevator.gd
|
||||
PASS res://tests/staff/test_staff_rescue.gd
|
||||
PASS res://tests/staff/test_task_work_clock.gd
|
||||
PASS res://tests/world/test_path_vertical_policy.gd
|
||||
PASS res://tests/world/test_path_transit_density.gd
|
||||
PASS res://tests/world/test_stair_infrastructure.gd
|
||||
PASS res://tests/world/test_legacy_stairs.gd
|
||||
PASS res://tests/transport/test_runtime_stairs.gd
|
||||
FAIL(96) res://tests/ui/test_stair_transport_details.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 51379, &"owner_instance_id": 133227873943, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [133227873943] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/world/test_elevator_topology.gd
|
||||
PASS res://tests/transport/test_elevator_service.gd
|
||||
PASS res://tests/staff/test_staff_commute.gd
|
||||
PASS res://tests/transport/test_transport_save.gd
|
||||
PASS res://tests/transport/test_transport_density_migration.gd
|
||||
PASS res://tests/transport/test_transport_boundaries.gd
|
||||
FAIL(1) res://tests/transport/test_runtime_transport.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- writer
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- reader
|
||||
PASS res://tests/performance/test_facility_reachability.gd
|
||||
PASS res://tests/performance/test_snapshot_ownership.gd
|
||||
PASS res://tests/performance/test_world_snapshot_cache.gd
|
||||
PASS res://tests/performance/test_lazy_digest.gd
|
||||
PASS res://tests/performance/test_path_membership.gd
|
||||
PASS res://tests/performance/test_system_optimization.gd
|
||||
PASS res://tests/performance/test_digest_performance.gd
|
||||
PASS res://tests/performance/test_perf_mvp_001.gd
|
||||
DONE
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"unit": "P06-03",
|
||||
"title": "復帰と表示",
|
||||
"candidate": "826ef60",
|
||||
"depends_on": "P06-01 f247d19 (A06)",
|
||||
"status": "independent_review_pass_awaiting_owner_approval",
|
||||
"acceptance": "破壊・摘発の休止を停電復旧で解除しない。途中保存後も復帰結果が一致する。",
|
||||
"cross_cutting": {
|
||||
"原本条件とUI操作が一致": "原本9.1の枯渇時全館停止。HUDにマナ残量/容量・停止表示、ツールチップに供給+20/10分・消費・収支・蓄積・直近の不足/破棄、休止は解除されない旨を表示。",
|
||||
"Runtimeの実利用・会計・行動へ接続": "get_mana_reserve_report()はA06の実Runtime値(_mana_reserve_demand、last_flow)を返す。復帰後の搬送・調理・EV・救護・接客の再開と一回請求は既存試験(supply_logistics_bridge、rescue_elevator、runtime_transport、runtime_operations_cycle、premium_room_guest_flow)で、持続停電中に検証。",
|
||||
"途中保存・再起動で再現": "test_mana_recovery_new_pid:停電中の保存を別PIDで読み、同じ復帰後に全状態digestと施設状態が一致。",
|
||||
"異常/境界/中断を検証": "DESTROYED/RAIDEDが復帰後も保持。reviewerが600分停電+4320分経過でも保持を確認。digestは1分差・未復帰で不一致となり比較が自明でないことを確認。runtime無しでもHUDは固定文言で動作。",
|
||||
"関連回帰PASS": "144本中120 PASS。失敗24本はc366012基準と同一の既知失敗(UI/audio cleanup等)。",
|
||||
"要件別ログとソースハッシュを保存": "本ファイルのsource_sha256、regression-826ef60.log。",
|
||||
"既存代表負荷の性能回帰を計測": "Runtime tickの変更なし。追加はsnapshot公開ごとのHUD報告1回:2.84µs/回(1分tick 794µsの0.36%、施設1件)。施設数に比例。描画・Windowsは対象外。"
|
||||
},
|
||||
"reviews": [
|
||||
{
|
||||
"round": 1,
|
||||
"candidate": "826ef60",
|
||||
"verdict": "PASS",
|
||||
"blocking": [],
|
||||
"non_blocking": [
|
||||
"HUD供給に炉受入量(未接続)を含まない",
|
||||
"収支の負号がASCII「-」、消費は全角「−」",
|
||||
"新PID試験はreceptionがINACTIVEに留まる理由を検証しない(運行再開は既存試験に依存)",
|
||||
"dashboard試験は部分一致のみ",
|
||||
"ログ・ハッシュ・性能証跡は受入記録で別途用意(本ファイル)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"regression_failures_baseline_identical": [
|
||||
"res://tests/mvp/test_amelia_donations.gd",
|
||||
"res://tests/mvp/test_fine_cleaning_effect.gd",
|
||||
"res://tests/s1/test_path_snapshot_transit_guard.gd",
|
||||
"res://tests/supply/test_normal_auto_purchase_chain.gd",
|
||||
"res://tests/supply/test_normal_supply_chain.gd",
|
||||
"res://tests/transport/test_runtime_transport.gd",
|
||||
"res://tests/ui/test_anri_recruitment.gd",
|
||||
"res://tests/ui/test_chapel_gameplay.gd",
|
||||
"res://tests/ui/test_dashboard_coalescing.gd",
|
||||
"res://tests/ui/test_dimension_migration_ui.gd",
|
||||
"res://tests/ui/test_first_operating_day.gd",
|
||||
"res://tests/ui/test_guest_decision_log.gd",
|
||||
"res://tests/ui/test_guest_observation.gd",
|
||||
"res://tests/ui/test_layer_skin.gd",
|
||||
"res://tests/ui/test_original_build_pointer.gd",
|
||||
"res://tests/ui/test_rank_pointer.gd",
|
||||
"res://tests/ui/test_staff_management.gd",
|
||||
"res://tests/ui/test_staff_rescue.gd",
|
||||
"res://tests/ui/test_staff_rest.gd",
|
||||
"res://tests/ui/test_stair_transport_details.gd",
|
||||
"res://tests/ui/test_supply_auto_purchase_ui.gd",
|
||||
"res://tests/ui/test_upkeep_inspector.gd",
|
||||
"res://tests/ui/test_world_tree_rank.gd",
|
||||
"tests/ui/test_cleaning_gameplay.gd"
|
||||
],
|
||||
"source_sha256": {
|
||||
"autoload/simulation_runtime.gd": "5a23418002cad5bde1f1db847b49b8c953c7b9d16a61c749415e2a41162902c1",
|
||||
"scenes/main/main.gd": "64142faf89ced6a8aedfedbf5642d830628d3ce003a3670507ba8b82bead2fa2",
|
||||
"scripts/mana/mana_power_projection.gd": "2026467960e857349bcb18fb97b7753189be5f2de2be91a17e73e90146eba69e",
|
||||
"scripts/facility/facility_instance.gd": "0c6aa493f3299611e05dd26aa429dda8cf7b5c479e4d09632036fb862f79a324",
|
||||
"scripts/facility/facility_service.gd": "ceb4a3e9224587423f273cdb2b56e6843ab176ac1fc4fe79dd498b5ed5c33b77",
|
||||
"tests/s2/test_mana_recovery_new_pid.gd": "8ef67859db2a31213585fa49dfc687ea69bf215c41c8d1d8cbb3f84d00dd5d17",
|
||||
"tests/ui/test_main_dashboard.gd": "793e23077fc7e2d9c6112c8847c4ee0fd92d5c00037bf753bd05d9888b3ffcfe",
|
||||
"tests/helpers/mana_outage.gd": "8df68f67fd023fb3fed4c465acf8227094d0e9403efc1f56ae009685629563e1"
|
||||
},
|
||||
"not_claimed": [
|
||||
"P06-02(ワープ未実装)",
|
||||
"P06全体",
|
||||
"炉の実接続",
|
||||
"Windows実機・全館実プレイ"
|
||||
]
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
PASS res://tests/run_tests.gd
|
||||
PASS res://tests/s0/test_definition_registry.gd
|
||||
PASS res://tests/s0/test_command_transaction.gd
|
||||
PASS res://tests/s0/test_determinism.gd
|
||||
PASS res://tests/s0/test_stable_id_generator.gd
|
||||
PASS res://tests/s0/test_domain_events.gd
|
||||
PASS res://tests/s0/test_runtime_integration.gd
|
||||
PASS res://tests/s1/test_dimension_batch_save.gd
|
||||
PASS res://tests/s1/test_facility_resize_preview.gd
|
||||
PASS res://tests/s1/test_weighted_resize_runtime.gd
|
||||
PASS res://tests/s1/test_original_facility_dimensions.gd
|
||||
PASS res://tests/s1/test_world_service.gd
|
||||
PASS res://tests/s1/test_path_service.gd
|
||||
FAIL(1) res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
ERROR: refusal creates no new temporary or backup package
|
||||
PASS res://tests/s1/test_path_congestion.gd
|
||||
PASS res://tests/s1/test_construction_service.gd
|
||||
PASS res://tests/s1/test_excavation_service.gd
|
||||
PASS res://tests/s1/test_spatial_subslot_service.gd
|
||||
PASS res://tests/s1/test_route_subslot_path_adapter.gd
|
||||
PASS res://tests/s1/test_facility_infrastructure_projector.gd
|
||||
PASS res://tests/s1/test_runtime_infrastructure_integration.gd
|
||||
PASS res://tests/s1/test_runtime_world_construction.gd
|
||||
PASS tests/environment/test_cleaning_access.gd
|
||||
FAIL(97) tests/ui/test_cleaning_gameplay.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 16049, &"owner_instance_id": 131617261207, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131617261207] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS tests/environment/test_runtime_cleanliness.gd
|
||||
PASS res://tests/environment/test_facility_cleanliness.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_accrual.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_settlement.gd
|
||||
PASS res://tests/s2/test_facility_service.gd
|
||||
PASS res://tests/s2/test_service_start_slots.gd
|
||||
PASS res://tests/s2/test_inventory_logistics.gd
|
||||
PASS res://tests/s2/test_economy_service.gd
|
||||
PASS res://tests/s2/test_ledger_backed_resources.gd
|
||||
PASS res://tests/s2/test_mana_network_service.gd
|
||||
PASS res://tests/s2/test_mana_reserve_cycle.gd
|
||||
PASS res://tests/s2/test_runtime_mana_reserve.gd
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_runtime_operations_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_staff_assignment.gd
|
||||
PASS res://tests/s3/test_guest_service.gd
|
||||
PASS res://tests/s3/test_staff_service.gd
|
||||
PASS res://tests/s3/test_staff_payroll_operations.gd
|
||||
PASS res://tests/s3/test_runtime_actor_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_actor_hardening.gd
|
||||
PASS res://tests/s3/test_runtime_path_traffic.gd
|
||||
PASS res://tests/s2/test_kitchen_production.gd
|
||||
PASS res://tests/supply/test_supply_save_migration.gd
|
||||
PASS res://tests/supply/test_supply_logistics_bridge.gd
|
||||
PASS res://tests/supply/test_supply_management_service.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase.gd
|
||||
FAIL(1) res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
FAIL(1) res://tests/supply/test_normal_supply_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/s2/test_facility_pricing.gd
|
||||
PASS res://tests/business/test_business_operations.gd
|
||||
PASS res://tests/business/test_business_closure.gd
|
||||
PASS res://tests/s4/test_relationship_event_service.gd
|
||||
PASS res://tests/s4/test_placement_risk_preview_service.gd
|
||||
PASS res://tests/s4/test_runtime_relationship_events.gd
|
||||
PASS res://tests/s5/test_authority_service.gd
|
||||
PASS res://tests/s5/test_defense_service.gd
|
||||
PASS res://tests/s5/test_runtime_authority_cycle.gd
|
||||
PASS res://tests/s5/test_runtime_defense_cycle.gd
|
||||
PASS res://tests/s6/test_save_operations.gd
|
||||
PASS res://tests/s6/test_save_preflight.gd
|
||||
PASS res://tests/s6/test_save_recovery_preservation.gd
|
||||
PASS res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
PASS res://tests/s6/test_performance_budget.gd
|
||||
PASS res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
PASS res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
PASS res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
PASS res://tests/mvp/test_premium_room.gd
|
||||
PASS res://tests/mvp/test_original_room_zones.gd
|
||||
PASS res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
PASS res://tests/mvp/test_upkeep_legacy_midnight.gd
|
||||
PASS res://tests/mvp/test_runtime_upkeep.gd
|
||||
PASS res://tests/mvp/test_upkeep_construction_boundaries.gd
|
||||
PASS res://tests/mvp/test_operations_coordinator.gd
|
||||
PASS res://tests/mvp/test_mvp_content_catalog.gd
|
||||
PASS res://tests/mvp/test_progression_service.gd
|
||||
PASS res://tests/mvp/test_runtime_content_progression.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_integration.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_acceptance.gd
|
||||
PASS res://tests/mvp/test_chapel_boundaries.gd
|
||||
FAIL(96) res://tests/ui/test_chapel_gameplay.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 20818, &"owner_instance_id": 179667207680, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [179667207680] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_chapel_service.gd
|
||||
FAIL(96) res://tests/ui/test_dashboard_coalescing.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20847, &"owner_instance_id": 131315271307, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131315271307] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_anri_floor_pricing.gd
|
||||
FAIL(97) res://tests/mvp/test_fine_cleaning_effect.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 20887, &"owner_instance_id": 132087023256, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132087023256] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/mvp/test_amelia_donations.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 20914, &"owner_instance_id": 180036306434, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180036306434] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_anri_recruitment.gd
|
||||
ERROR: Anri recruitment ordinary owner cleanup failed: { &"owned_engine_pid": 20933, &"owner_instance_id": 134754600590, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134754600590] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_layer_skin.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 1 resources still in use at exit (run with --verbose for details).
|
||||
FAIL(96) res://tests/ui/test_world_tree_rank.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20985, &"owner_instance_id": 134721046155, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134721046155] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_rank_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21004, &"owner_instance_id": 134771377803, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134771377803] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_layer_presentation.gd
|
||||
FAIL(96) res://tests/ui/test_original_build_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21034, &"owner_instance_id": 134704268951, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134704268951] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_dimension_migration_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21103, &"owner_instance_id": 164450272915, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [164450272915] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_upkeep_inspector.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21110, &"owner_instance_id": 131399157387, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131399157387] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rest.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21137, &"owner_instance_id": 131399157387, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131399157387] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rescue.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21154, &"owner_instance_id": 131583706763, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131583706763] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_observation.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21181, &"owner_instance_id": 131533375116, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [131533375116] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_decision_log.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21189, &"owner_instance_id": 277310605252, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [277310605252] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_room_presentation.gd
|
||||
PASS res://tests/ui/test_pixel_tower.gd
|
||||
PASS res://tests/ui/test_main_dashboard.gd
|
||||
FAIL(96) res://tests/ui/test_staff_management.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 2 resources still in use at exit (run with --verbose for details).
|
||||
PASS res://tests/ui/test_supply_management.gd
|
||||
FAIL(96) res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21314, &"owner_instance_id": 134989481613, &"before": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134989481613] }, &"after": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_business_management.gd
|
||||
PASS res://tests/ui/test_elevator_management.gd
|
||||
FAIL(96) res://tests/ui/test_first_operating_day.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21381, &"owner_instance_id": 134687491727, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [134687491727] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/staff/test_dormitory_dimension_migration.gd
|
||||
PASS res://tests/staff/test_staff_dormitory.gd
|
||||
PASS res://tests/staff/test_dormitory_shift_cycle.gd
|
||||
PASS res://tests/staff/test_shift_rush.gd
|
||||
PASS res://tests/staff/test_staff_rest.gd
|
||||
PASS res://tests/staff/test_rescue_elevator.gd
|
||||
PASS res://tests/staff/test_staff_rescue.gd
|
||||
PASS res://tests/staff/test_task_work_clock.gd
|
||||
PASS res://tests/world/test_path_vertical_policy.gd
|
||||
PASS res://tests/world/test_path_transit_density.gd
|
||||
PASS res://tests/world/test_stair_infrastructure.gd
|
||||
PASS res://tests/world/test_legacy_stairs.gd
|
||||
PASS res://tests/transport/test_runtime_stairs.gd
|
||||
FAIL(96) res://tests/ui/test_stair_transport_details.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 21756, &"owner_instance_id": 132154132118, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132154132118] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/world/test_elevator_topology.gd
|
||||
PASS res://tests/transport/test_elevator_service.gd
|
||||
PASS res://tests/staff/test_staff_commute.gd
|
||||
PASS res://tests/transport/test_transport_save.gd
|
||||
PASS res://tests/transport/test_transport_density_migration.gd
|
||||
PASS res://tests/transport/test_transport_boundaries.gd
|
||||
FAIL(1) res://tests/transport/test_runtime_transport.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/performance/test_facility_reachability.gd
|
||||
PASS res://tests/performance/test_snapshot_ownership.gd
|
||||
PASS res://tests/performance/test_world_snapshot_cache.gd
|
||||
PASS res://tests/performance/test_lazy_digest.gd
|
||||
PASS res://tests/performance/test_path_membership.gd
|
||||
PASS res://tests/performance/test_system_optimization.gd
|
||||
PASS res://tests/performance/test_digest_performance.gd
|
||||
PASS res://tests/performance/test_perf_mvp_001.gd
|
||||
DONE
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
extends SceneTree
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
func _run() -> void:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0); state.reset(); runtime.initialize(clock, state, 7707); clock.set_paused(true)
|
||||
runtime.submit_command(&"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(-6, 0, 0)})
|
||||
clock.advance_minutes(180)
|
||||
var n := 2000
|
||||
var start := Time.get_ticks_usec()
|
||||
for i in n: runtime.get_mana_reserve_report()
|
||||
var per := (Time.get_ticks_usec() - start) / float(n)
|
||||
start = Time.get_ticks_usec()
|
||||
for i in 200: clock.advance_minutes(1)
|
||||
var tick := (Time.get_ticks_usec() - start) / 200.0
|
||||
print("P06_03_REPORT_COST=" + JSON.stringify({"report_us_avg": per, "tick_us_avg": tick, "ratio_percent": per / tick * 100.0, "facilities": runtime.facility_service.get_instances().size()}))
|
||||
runtime.free(); clock.free(); state.free(); quit(0)
|
||||
+1
@@ -0,0 +1 @@
|
||||
uid://ut4tipgj23pf
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"unit": "P14-02",
|
||||
"title": "ワープの通常接続",
|
||||
"candidate": "8917c19",
|
||||
"decision": "A07+A08",
|
||||
"status": "independent_review_pass_awaiting_owner_approval",
|
||||
"acceptance": "途中保存、マナ不足、到着不能時に移動や費用が二重反映されない。",
|
||||
"closed_gaps": {
|
||||
"通常建設できる2x2 FRONTのワープ定義": "mana_warp.tres(3000G・資材30・360分・15G/日・維持マナ1)、通常ビルドパレットに追加。",
|
||||
"通常経路選択から対象・出口・実位置へ接続": "建設済みワープ全組を経路connectorで連結し、客・従業員の通常経路探索が1歩で移動。自然営業の客が実際にワープで移動(test_runtime_warp)。",
|
||||
"費用確定規約と二重計上拒否": "移動確定と同じtickで1人3マナを1回だけ差し引き、WARP_TRAVELEDを記録。投影失敗時は課金なし。救助ペアは2人分。",
|
||||
"マナ不足/機能停止/到着不能の原状保持": "残量3未満・停電・片側未評価/無電力・到着セル封鎖で経路から除外し、費用・移動なし。",
|
||||
"途中保存・別PID再開で二重反映なし": "課金直後の保存を別PIDで読み、120分継続のマナ・移動履歴・全状態digestが一致。",
|
||||
"通常UI・影響回帰・性能・独立レビュー": "パレット(25件)・コード描画、147本中122 PASS(失敗24本は基準と同一)、性能計測、独立レビュー2回PASS。",
|
||||
"A08 客用/業務用の分離": "表層mana_warpは客専用(access \"!staff\")、裏層service_warpは従業員専用で同種同士のみ接続。値はA07と同じ。"
|
||||
},
|
||||
"reviews": [
|
||||
{
|
||||
"round": 1,
|
||||
"candidate": "728e5e6",
|
||||
"verdict": "PASS",
|
||||
"blocking": [],
|
||||
"non_blocking": [
|
||||
"access_id=\"\"のため通勤・休憩・作業中の従業員もワープを使う(A07文言「客と接客中の従業員」より広い。既存の客用昇降機と同じ扱い)。owner確認事項。",
|
||||
"同一分内で残量が3未満に落ちると後続はその分だけブロック待ち(課金・移動なし)",
|
||||
"connectors_matchはoperational_stateを検証しない(次分に自己修復)",
|
||||
"到着先ワープの収容数4は未反映",
|
||||
"UIは接続状態・運賃・停止理由を表示しない/パレットからの実建設UI操作試験なし/描画未目視",
|
||||
"4基(6リンク)で1日tickが約74%増。ただし同配置の客用昇降機4基はさらに重く、ワープ固有ではない。リンク数はN(N-1)/2",
|
||||
"15G/日維持費・救助2人課金・通勤ワープ・同一分複数乗客の専用試験なし(コード確認のみ)"
|
||||
],
|
||||
"note": "r1で従業員も表層ワープを使う点を指摘→owner判断A08で分離"
|
||||
},
|
||||
{
|
||||
"round": 2,
|
||||
"candidate": "8917c19",
|
||||
"verdict": "PASS",
|
||||
"blocking": [],
|
||||
"non_blocking": [
|
||||
"path_service.gd:2260-2261 _route_exists_with_overridesが\"!staff\"を旧比較で読み、停電中の客ワープ経路の理由コードがCONNECTOR_UNAVAILABLEでなくGOAL_UNREACHABLEになる(移動・費用に影響なし)",
|
||||
"path_service.gd:2193-2194 _has_permitted_layer_crossingも同様(現ワープは層をまたがないため無害)",
|
||||
"A08以前のaccess \"\"ワープ接続を含むセーブは原子的に読込拒否(配布済みセーブにワープなし)",
|
||||
"描画未目視"
|
||||
]
|
||||
}
|
||||
],
|
||||
"regression_failures_baseline_identical": [
|
||||
"res://tests/mvp/test_amelia_donations.gd",
|
||||
"res://tests/mvp/test_fine_cleaning_effect.gd",
|
||||
"res://tests/s1/test_path_snapshot_transit_guard.gd",
|
||||
"res://tests/supply/test_normal_auto_purchase_chain.gd",
|
||||
"res://tests/supply/test_normal_supply_chain.gd",
|
||||
"res://tests/transport/test_runtime_transport.gd",
|
||||
"res://tests/ui/test_anri_recruitment.gd",
|
||||
"res://tests/ui/test_chapel_gameplay.gd",
|
||||
"res://tests/ui/test_dashboard_coalescing.gd",
|
||||
"res://tests/ui/test_dimension_migration_ui.gd",
|
||||
"res://tests/ui/test_first_operating_day.gd",
|
||||
"res://tests/ui/test_guest_decision_log.gd",
|
||||
"res://tests/ui/test_guest_observation.gd",
|
||||
"res://tests/ui/test_layer_skin.gd",
|
||||
"res://tests/ui/test_original_build_pointer.gd",
|
||||
"res://tests/ui/test_rank_pointer.gd",
|
||||
"res://tests/ui/test_staff_management.gd",
|
||||
"res://tests/ui/test_staff_rescue.gd",
|
||||
"res://tests/ui/test_staff_rest.gd",
|
||||
"res://tests/ui/test_stair_transport_details.gd",
|
||||
"res://tests/ui/test_supply_auto_purchase_ui.gd",
|
||||
"res://tests/ui/test_upkeep_inspector.gd",
|
||||
"res://tests/ui/test_world_tree_rank.gd",
|
||||
"tests/ui/test_cleaning_gameplay.gd"
|
||||
],
|
||||
"source_sha256": {
|
||||
"data/facilities/infrastructure/mana_warp.tres": "42f1156e04a4454d8525bf3dd77b214a39e11bb7c5e749af1413fe77cdb1e456",
|
||||
"scripts/transport/warp_service.gd": "7602c6b8a9672a703bfa98a1424c2cf68cf50184f87f72349d564af6a5ca9d41",
|
||||
"scripts/world/facility_infrastructure_projector.gd": "7903b7aa6a46b50150beeb13dd8675ce34168c044b1b818f0dc6e2f7e1872d51",
|
||||
"scripts/world/path_service.gd": "fd0f4b202291eda6ab06442d14373f9c2457627550958fa79008d2f828b21521",
|
||||
"scripts/data/facility_catalog.gd": "08a09405a4f07a6b5fd21ba41eff347692f376c50306ccf6ae18e74ed95adacf",
|
||||
"autoload/simulation_runtime.gd": "68d03872a591955740346a894fa4139cea75ed264428d61ade181532ea398b41",
|
||||
"scenes/main/main.gd": "e09c361603da85b7a8b1e299a62b0f9b27f2b20b373b511e6450fe281e16614e",
|
||||
"scripts/ui/pixel_tower_art.gd": "f766b47167db8bd5906b1467fd6f8587af8e9268304c7ba209a9eb157847165e",
|
||||
"tests/transport/test_runtime_warp.gd": "ce263dd680dfa753f7435b85a733e3909740e98ec75e9e3b72750ed11b664f85",
|
||||
"tests/ui/test_main_dashboard.gd": "305227c1077a92ef3ee522b561a68b0685767740b46b51ff789c800931647ef1",
|
||||
"data/facilities/infrastructure/service_warp.tres": "712ae26bf2d9d18c9da6344ee18ef716250e051693bcace586b85f35a794dba2"
|
||||
},
|
||||
"not_claimed": [
|
||||
"Windows実機",
|
||||
"完成素材・描画の目視",
|
||||
"P06-02全体(別単位)",
|
||||
"P14全体"
|
||||
],
|
||||
"followups": [
|
||||
"\"!\"接頭辞accessを_route_exists_with_overridesと_has_permitted_layer_crossingにも適用する"
|
||||
]
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
PASS res://tests/run_tests.gd
|
||||
PASS res://tests/s0/test_definition_registry.gd
|
||||
PASS res://tests/s0/test_command_transaction.gd
|
||||
PASS res://tests/s0/test_determinism.gd
|
||||
PASS res://tests/s0/test_stable_id_generator.gd
|
||||
PASS res://tests/s0/test_domain_events.gd
|
||||
PASS res://tests/s0/test_runtime_integration.gd
|
||||
PASS res://tests/s1/test_dimension_batch_save.gd
|
||||
PASS res://tests/s1/test_facility_resize_preview.gd
|
||||
PASS res://tests/s1/test_weighted_resize_runtime.gd
|
||||
PASS res://tests/s1/test_original_facility_dimensions.gd
|
||||
PASS res://tests/s1/test_world_service.gd
|
||||
PASS res://tests/s1/test_path_service.gd
|
||||
FAIL(1) res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
ERROR: refusal creates no new temporary or backup package
|
||||
PASS res://tests/s1/test_path_congestion.gd
|
||||
PASS res://tests/s1/test_construction_service.gd
|
||||
PASS res://tests/s1/test_excavation_service.gd
|
||||
PASS res://tests/s1/test_spatial_subslot_service.gd
|
||||
PASS res://tests/s1/test_route_subslot_path_adapter.gd
|
||||
PASS res://tests/s1/test_facility_infrastructure_projector.gd
|
||||
PASS res://tests/s1/test_runtime_infrastructure_integration.gd
|
||||
PASS res://tests/s1/test_runtime_world_construction.gd
|
||||
PASS tests/environment/test_cleaning_access.gd
|
||||
FAIL(97) tests/ui/test_cleaning_gameplay.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 15221, &"owner_instance_id": 132691003032, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132691003032] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS tests/environment/test_runtime_cleanliness.gd
|
||||
PASS res://tests/environment/test_facility_cleanliness.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_accrual.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_settlement.gd
|
||||
PASS res://tests/s2/test_facility_service.gd
|
||||
PASS res://tests/s2/test_service_start_slots.gd
|
||||
PASS res://tests/s2/test_inventory_logistics.gd
|
||||
PASS res://tests/s2/test_economy_service.gd
|
||||
PASS res://tests/s2/test_ledger_backed_resources.gd
|
||||
PASS res://tests/s2/test_mana_network_service.gd
|
||||
PASS res://tests/s2/test_mana_reserve_cycle.gd
|
||||
PASS res://tests/s2/test_runtime_mana_reserve.gd
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_runtime_operations_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_staff_assignment.gd
|
||||
PASS res://tests/s3/test_guest_service.gd
|
||||
PASS res://tests/s3/test_staff_service.gd
|
||||
PASS res://tests/s3/test_staff_payroll_operations.gd
|
||||
PASS res://tests/s3/test_runtime_actor_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_actor_hardening.gd
|
||||
PASS res://tests/s3/test_runtime_path_traffic.gd
|
||||
PASS res://tests/s2/test_kitchen_production.gd
|
||||
PASS res://tests/supply/test_supply_save_migration.gd
|
||||
PASS res://tests/supply/test_supply_logistics_bridge.gd
|
||||
PASS res://tests/supply/test_supply_management_service.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase.gd
|
||||
FAIL(1) res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
FAIL(1) res://tests/supply/test_normal_supply_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/s2/test_facility_pricing.gd
|
||||
PASS res://tests/business/test_business_operations.gd
|
||||
PASS res://tests/business/test_business_closure.gd
|
||||
PASS res://tests/s4/test_relationship_event_service.gd
|
||||
PASS res://tests/s4/test_placement_risk_preview_service.gd
|
||||
PASS res://tests/s4/test_runtime_relationship_events.gd
|
||||
PASS res://tests/s5/test_authority_service.gd
|
||||
PASS res://tests/s5/test_defense_service.gd
|
||||
PASS res://tests/s5/test_runtime_authority_cycle.gd
|
||||
PASS res://tests/s5/test_runtime_defense_cycle.gd
|
||||
PASS res://tests/s6/test_save_operations.gd
|
||||
PASS res://tests/s6/test_save_preflight.gd
|
||||
PASS res://tests/s6/test_save_recovery_preservation.gd
|
||||
PASS res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
PASS res://tests/s6/test_performance_budget.gd
|
||||
PASS res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
PASS res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
PASS res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
PASS res://tests/mvp/test_premium_room.gd
|
||||
PASS res://tests/mvp/test_original_room_zones.gd
|
||||
PASS res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
PASS res://tests/mvp/test_upkeep_legacy_midnight.gd
|
||||
PASS res://tests/mvp/test_runtime_upkeep.gd
|
||||
PASS res://tests/mvp/test_upkeep_construction_boundaries.gd
|
||||
PASS res://tests/mvp/test_operations_coordinator.gd
|
||||
PASS res://tests/mvp/test_mvp_content_catalog.gd
|
||||
PASS res://tests/mvp/test_progression_service.gd
|
||||
PASS res://tests/mvp/test_runtime_content_progression.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_integration.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_acceptance.gd
|
||||
PASS res://tests/mvp/test_chapel_boundaries.gd
|
||||
FAIL(96) res://tests/ui/test_chapel_gameplay.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 20026, &"owner_instance_id": 180740949505, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180740949505] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_chapel_service.gd
|
||||
FAIL(96) res://tests/ui/test_dashboard_coalescing.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20063, &"owner_instance_id": 132389013132, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132389013132] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_anri_floor_pricing.gd
|
||||
FAIL(97) res://tests/mvp/test_fine_cleaning_effect.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 20101, &"owner_instance_id": 133160765081, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [133160765081] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/mvp/test_amelia_donations.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 20128, &"owner_instance_id": 181110048259, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [181110048259] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_anri_recruitment.gd
|
||||
ERROR: Anri recruitment ordinary owner cleanup failed: { &"owned_engine_pid": 20148, &"owner_instance_id": 135828342415, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135828342415] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_layer_skin.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 1 resources still in use at exit (run with --verbose for details).
|
||||
FAIL(96) res://tests/ui/test_world_tree_rank.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20200, &"owner_instance_id": 135794787980, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135794787980] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_rank_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20209, &"owner_instance_id": 135845119628, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135845119628] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_layer_presentation.gd
|
||||
FAIL(96) res://tests/ui/test_original_build_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20255, &"owner_instance_id": 135778010776, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135778010776] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_dimension_migration_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20315, &"owner_instance_id": 165524014740, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [165524014740] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_upkeep_inspector.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20330, &"owner_instance_id": 132472899212, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132472899212] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rest.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20361, &"owner_instance_id": 132472899212, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132472899212] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rescue.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20377, &"owner_instance_id": 132657448588, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132657448588] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_observation.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20402, &"owner_instance_id": 132607116941, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132607116941] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_decision_log.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20410, &"owner_instance_id": 278384347081, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [278384347081] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_room_presentation.gd
|
||||
PASS res://tests/ui/test_pixel_tower.gd
|
||||
PASS res://tests/ui/test_main_dashboard.gd
|
||||
FAIL(96) res://tests/ui/test_staff_management.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 2 resources still in use at exit (run with --verbose for details).
|
||||
PASS res://tests/ui/test_supply_management.gd
|
||||
FAIL(96) res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20534, &"owner_instance_id": 136063223438, &"before": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [136063223438] }, &"after": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_business_management.gd
|
||||
PASS res://tests/ui/test_elevator_management.gd
|
||||
FAIL(96) res://tests/ui/test_first_operating_day.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20599, &"owner_instance_id": 135761233552, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135761233552] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/staff/test_dormitory_dimension_migration.gd
|
||||
PASS res://tests/staff/test_staff_dormitory.gd
|
||||
PASS res://tests/staff/test_dormitory_shift_cycle.gd
|
||||
PASS res://tests/staff/test_shift_rush.gd
|
||||
PASS res://tests/staff/test_staff_rest.gd
|
||||
PASS res://tests/staff/test_rescue_elevator.gd
|
||||
PASS res://tests/staff/test_staff_rescue.gd
|
||||
PASS res://tests/staff/test_task_work_clock.gd
|
||||
PASS res://tests/world/test_path_vertical_policy.gd
|
||||
PASS res://tests/world/test_path_transit_density.gd
|
||||
PASS res://tests/world/test_stair_infrastructure.gd
|
||||
PASS res://tests/world/test_legacy_stairs.gd
|
||||
PASS res://tests/transport/test_runtime_stairs.gd
|
||||
FAIL(96) res://tests/ui/test_stair_transport_details.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 20968, &"owner_instance_id": 133227873943, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [133227873943] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/world/test_elevator_topology.gd
|
||||
PASS res://tests/transport/test_elevator_service.gd
|
||||
PASS res://tests/staff/test_staff_commute.gd
|
||||
PASS res://tests/transport/test_transport_save.gd
|
||||
PASS res://tests/transport/test_transport_density_migration.gd
|
||||
PASS res://tests/transport/test_transport_boundaries.gd
|
||||
FAIL(1) res://tests/transport/test_runtime_transport.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- writer
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- reader
|
||||
PASS res://tests/performance/test_facility_reachability.gd
|
||||
PASS res://tests/performance/test_snapshot_ownership.gd
|
||||
PASS res://tests/performance/test_world_snapshot_cache.gd
|
||||
PASS res://tests/performance/test_lazy_digest.gd
|
||||
PASS res://tests/performance/test_path_membership.gd
|
||||
PASS res://tests/performance/test_system_optimization.gd
|
||||
PASS res://tests/performance/test_digest_performance.gd
|
||||
PASS res://tests/performance/test_perf_mvp_001.gd
|
||||
DONE
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
PASS res://tests/run_tests.gd
|
||||
PASS res://tests/s0/test_definition_registry.gd
|
||||
PASS res://tests/s0/test_command_transaction.gd
|
||||
PASS res://tests/s0/test_determinism.gd
|
||||
PASS res://tests/s0/test_stable_id_generator.gd
|
||||
PASS res://tests/s0/test_domain_events.gd
|
||||
PASS res://tests/s0/test_runtime_integration.gd
|
||||
PASS res://tests/s1/test_dimension_batch_save.gd
|
||||
PASS res://tests/s1/test_facility_resize_preview.gd
|
||||
PASS res://tests/s1/test_weighted_resize_runtime.gd
|
||||
PASS res://tests/s1/test_original_facility_dimensions.gd
|
||||
PASS res://tests/s1/test_world_service.gd
|
||||
PASS res://tests/s1/test_path_service.gd
|
||||
FAIL(1) res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
ERROR: refusal creates no new temporary or backup package
|
||||
PASS res://tests/s1/test_path_congestion.gd
|
||||
PASS res://tests/s1/test_construction_service.gd
|
||||
PASS res://tests/s1/test_excavation_service.gd
|
||||
PASS res://tests/s1/test_spatial_subslot_service.gd
|
||||
PASS res://tests/s1/test_route_subslot_path_adapter.gd
|
||||
PASS res://tests/s1/test_facility_infrastructure_projector.gd
|
||||
PASS res://tests/s1/test_runtime_infrastructure_integration.gd
|
||||
PASS res://tests/s1/test_runtime_world_construction.gd
|
||||
PASS tests/environment/test_cleaning_access.gd
|
||||
FAIL(97) tests/ui/test_cleaning_gameplay.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 60299, &"owner_instance_id": 132355458712, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132355458712] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS tests/environment/test_runtime_cleanliness.gd
|
||||
PASS res://tests/environment/test_facility_cleanliness.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_accrual.gd
|
||||
PASS res://tests/s2/test_facility_upkeep_settlement.gd
|
||||
PASS res://tests/s2/test_facility_service.gd
|
||||
PASS res://tests/s2/test_service_start_slots.gd
|
||||
PASS res://tests/s2/test_inventory_logistics.gd
|
||||
PASS res://tests/s2/test_economy_service.gd
|
||||
PASS res://tests/s2/test_ledger_backed_resources.gd
|
||||
PASS res://tests/s2/test_mana_network_service.gd
|
||||
PASS res://tests/s2/test_mana_reserve_cycle.gd
|
||||
PASS res://tests/s2/test_runtime_mana_reserve.gd
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_reserve_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- writer
|
||||
PASS res://tests/s2/test_mana_recovery_new_pid.gd -- reader
|
||||
PASS res://tests/s2/test_runtime_operations_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_staff_assignment.gd
|
||||
PASS res://tests/s3/test_guest_service.gd
|
||||
PASS res://tests/s3/test_staff_service.gd
|
||||
PASS res://tests/s3/test_staff_payroll_operations.gd
|
||||
PASS res://tests/s3/test_runtime_actor_cycle.gd
|
||||
PASS res://tests/s3/test_runtime_actor_hardening.gd
|
||||
PASS res://tests/s3/test_runtime_path_traffic.gd
|
||||
PASS res://tests/s2/test_kitchen_production.gd
|
||||
PASS res://tests/supply/test_supply_save_migration.gd
|
||||
PASS res://tests/supply/test_supply_logistics_bridge.gd
|
||||
PASS res://tests/supply/test_supply_management_service.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase.gd
|
||||
FAIL(1) res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
PASS res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
FAIL(1) res://tests/supply/test_normal_supply_chain.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/s2/test_facility_pricing.gd
|
||||
PASS res://tests/business/test_business_operations.gd
|
||||
PASS res://tests/business/test_business_closure.gd
|
||||
PASS res://tests/s4/test_relationship_event_service.gd
|
||||
PASS res://tests/s4/test_placement_risk_preview_service.gd
|
||||
PASS res://tests/s4/test_runtime_relationship_events.gd
|
||||
PASS res://tests/s5/test_authority_service.gd
|
||||
PASS res://tests/s5/test_defense_service.gd
|
||||
PASS res://tests/s5/test_runtime_authority_cycle.gd
|
||||
PASS res://tests/s5/test_runtime_defense_cycle.gd
|
||||
PASS res://tests/s6/test_save_operations.gd
|
||||
PASS res://tests/s6/test_save_preflight.gd
|
||||
PASS res://tests/s6/test_save_recovery_preservation.gd
|
||||
PASS res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
PASS res://tests/s6/test_performance_budget.gd
|
||||
PASS res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
PASS res://tests/mvp/test_premium_room_guest_flow.gd
|
||||
PASS res://tests/mvp/test_runtime_mana_depletion.gd
|
||||
PASS res://tests/mvp/test_premium_room.gd
|
||||
PASS res://tests/mvp/test_original_room_zones.gd
|
||||
PASS res://tests/mvp/test_upkeep_loss_streak.gd
|
||||
PASS res://tests/mvp/test_upkeep_legacy_midnight.gd
|
||||
PASS res://tests/mvp/test_runtime_upkeep.gd
|
||||
PASS res://tests/mvp/test_upkeep_construction_boundaries.gd
|
||||
PASS res://tests/mvp/test_operations_coordinator.gd
|
||||
PASS res://tests/mvp/test_mvp_content_catalog.gd
|
||||
PASS res://tests/mvp/test_progression_service.gd
|
||||
PASS res://tests/mvp/test_runtime_content_progression.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_integration.gd
|
||||
PASS res://tests/mvp/test_runtime_operations_acceptance.gd
|
||||
PASS res://tests/mvp/test_chapel_boundaries.gd
|
||||
FAIL(96) res://tests/ui/test_chapel_gameplay.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 65058, &"owner_instance_id": 180405405185, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180405405185] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_chapel_service.gd
|
||||
FAIL(96) res://tests/ui/test_dashboard_coalescing.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65080, &"owner_instance_id": 132053468812, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132053468812] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/mvp/test_anri_floor_pricing.gd
|
||||
FAIL(97) res://tests/mvp/test_fine_cleaning_effect.gd
|
||||
ERROR: Cleaning ordinary owner cleanup failed: { &"owned_engine_pid": 65129, &"owner_instance_id": 132825220761, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132825220761] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/mvp/test_amelia_donations.gd
|
||||
ERROR: Chapel ordinary owner cleanup failed: { &"owned_engine_pid": 65145, &"owner_instance_id": 180774503939, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [180774503939] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_anri_recruitment.gd
|
||||
ERROR: Anri recruitment ordinary owner cleanup failed: { &"owned_engine_pid": 65173, &"owner_instance_id": 135492798095, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135492798095] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_layer_skin.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 1 resources still in use at exit (run with --verbose for details).
|
||||
FAIL(96) res://tests/ui/test_world_tree_rank.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65217, &"owner_instance_id": 135459243660, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135459243660] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_rank_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65233, &"owner_instance_id": 135509575308, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135509575308] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_layer_presentation.gd
|
||||
FAIL(96) res://tests/ui/test_original_build_pointer.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65263, &"owner_instance_id": 135442466456, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135442466456] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_dimension_migration_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65325, &"owner_instance_id": 165188470420, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [165188470420] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_upkeep_inspector.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65351, &"owner_instance_id": 132137354892, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132137354892] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rest.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65367, &"owner_instance_id": 132137354892, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132137354892] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_staff_rescue.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65397, &"owner_instance_id": 132321904268, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132321904268] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_observation.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65414, &"owner_instance_id": 132271572621, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132271572621] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
FAIL(96) res://tests/ui/test_guest_decision_log.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65432, &"owner_instance_id": 278048802760, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [278048802760] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_room_presentation.gd
|
||||
PASS res://tests/ui/test_pixel_tower.gd
|
||||
PASS res://tests/ui/test_main_dashboard.gd
|
||||
FAIL(96) res://tests/ui/test_staff_management.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"passed": false, &"reason": "fresh absolute owned control proof required" }
|
||||
ERROR: 2 resources still in use at exit (run with --verbose for details).
|
||||
PASS res://tests/ui/test_supply_management.gd
|
||||
FAIL(96) res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65547, &"owner_instance_id": 135727679118, &"before": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135727679118] }, &"after": { &"clock_process": false, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/ui/test_business_management.gd
|
||||
PASS res://tests/ui/test_elevator_management.gd
|
||||
FAIL(96) res://tests/ui/test_first_operating_day.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65610, &"owner_instance_id": 135425689232, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [135425689232] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/staff/test_dormitory_dimension_migration.gd
|
||||
PASS res://tests/staff/test_staff_dormitory.gd
|
||||
PASS res://tests/staff/test_dormitory_shift_cycle.gd
|
||||
PASS res://tests/staff/test_shift_rush.gd
|
||||
PASS res://tests/staff/test_staff_rest.gd
|
||||
PASS res://tests/staff/test_rescue_elevator.gd
|
||||
PASS res://tests/staff/test_staff_rescue.gd
|
||||
PASS res://tests/staff/test_task_work_clock.gd
|
||||
PASS res://tests/world/test_path_vertical_policy.gd
|
||||
PASS res://tests/world/test_path_transit_density.gd
|
||||
PASS res://tests/world/test_stair_infrastructure.gd
|
||||
PASS res://tests/world/test_legacy_stairs.gd
|
||||
PASS res://tests/transport/test_runtime_stairs.gd
|
||||
FAIL(96) res://tests/ui/test_stair_transport_details.gd
|
||||
ERROR: Ordinary fixture audio cleanup failed: { &"owned_engine_pid": 65968, &"owner_instance_id": 132892329623, &"before": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": false, &"owner_meta_present": true, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [132892329623] }, &"after": { &"clock_process": true, &"clock_physics_process": false, &"clock_process_mode": 0, &"root_gui_disable_input": false, &"auto_accept_quit": true, &"owner_meta_present": false, &"original_auto_accept_quit": true, &"closing": false, &"owner_ids": [] }, &"expected_remaining_owner_ids": [], &"ordinary_owner_release_verified": true, &"clock_process_flags_unchanged": true, &"root_gui_input_flag_unchanged": true, &"audio_teardown_passed": true, &"maximum_wait_us": 500000, &"whole_Runtime_field_deletions": 0, &"gameplay_expectations_changed": false, &"clock_flags_intentionally_modified": false, &"application_close_called": false, &"SceneTree_quit_overridden": false, &"WindowQuit_verified": false, &"formal_acceptance": false, &"passed": false }
|
||||
PASS res://tests/world/test_elevator_topology.gd
|
||||
PASS res://tests/transport/test_elevator_service.gd
|
||||
PASS res://tests/staff/test_staff_commute.gd
|
||||
PASS res://tests/transport/test_transport_save.gd
|
||||
PASS res://tests/transport/test_transport_density_migration.gd
|
||||
PASS res://tests/transport/test_transport_boundaries.gd
|
||||
FAIL(1) res://tests/transport/test_runtime_transport.gd
|
||||
ERROR: ordinary Main final audio cleanup failed
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- writer
|
||||
PASS res://tests/transport/test_runtime_warp.gd -- reader
|
||||
PASS res://tests/performance/test_facility_reachability.gd
|
||||
PASS res://tests/performance/test_snapshot_ownership.gd
|
||||
PASS res://tests/performance/test_world_snapshot_cache.gd
|
||||
PASS res://tests/performance/test_lazy_digest.gd
|
||||
PASS res://tests/performance/test_path_membership.gd
|
||||
PASS res://tests/performance/test_system_optimization.gd
|
||||
PASS res://tests/performance/test_digest_performance.gd
|
||||
PASS res://tests/performance/test_perf_mvp_001.gd
|
||||
DONE
|
||||
@@ -0,0 +1,4 @@
|
||||
P14_02_WARP_PERF={"links_0":0,"links_4":6,"tick_day_ms_avg_warps_0":1015.712,"tick_day_ms_avg_warps_4":1810.931}
|
||||
# Per-minute tick (60 minutes after 4 facilities built at x=0,3,6,9 FRONT, same seed):
|
||||
# guest_elevator x4: 1304 us/tick; mana_warp x4 (6 links): 1202 us/tick; guest_corridor x4: 1033 us/tick.
|
||||
# _synchronize_warps alone: 43 us/call with 4 warps. No-warp day: 1015.7 ms (P06-01 candidate 1014, base 1008).
|
||||
@@ -0,0 +1,33 @@
|
||||
extends SceneTree
|
||||
|
||||
## P14-02 A07 warp tick cost: the same 1440-minute run with 0 or 4 warps
|
||||
## (6 linked pairs) built on ground-supported columns. Not rendering/Windows.
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var out := {}
|
||||
for warps: int in [0, 4]:
|
||||
var samples: Array = []
|
||||
for trial: int in 3:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var rt: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0); state.reset(); rt.initialize(clock, state, 9909 + trial); clock.set_paused(true)
|
||||
rt.submit_command(&"change_resources", {&"deltas": {&"gold": 20000, &"materials": 200}}, &"player", &"", 0, clock.absolute_minute, &"perf.fund")
|
||||
for i: int in warps:
|
||||
var built: CommandResult = rt.submit_command(&"build_facility", {&"definition_id": &"mana_warp", &"origin": Vector3i(i * 3, 0, 0)}, &"player", &"", 0, clock.absolute_minute, StringName("perf.w%d" % i))
|
||||
if not built.is_success():
|
||||
push_error("warp build failed: " + built.message)
|
||||
clock.advance_minutes(370)
|
||||
var start := Time.get_ticks_usec()
|
||||
clock.advance_minutes(1440)
|
||||
samples.append((Time.get_ticks_usec() - start) / 1000.0)
|
||||
out["links_%d" % warps] = rt.path_service.get_connector_ids_with_prefix("warp.connector:").size()
|
||||
rt.free(); clock.free(); state.free()
|
||||
var total := 0.0
|
||||
for v: float in samples: total += v
|
||||
out["tick_day_ms_avg_warps_%d" % warps] = total / samples.size()
|
||||
print("P14_02_WARP_PERF=" + JSON.stringify(out))
|
||||
quit(0)
|
||||
@@ -0,0 +1 @@
|
||||
uid://clvwcbbt34gwr
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# P14-03 混雑と経路選択:証拠
|
||||
|
||||
作業内容は「客用・業務用EV、階段、ワープの待ち時間をA*費用へ反映する」です。受入条件は「ラッシュ、相乗り、50%減速、救護同伴、経路喪失を、実際の位置と人数で検証する」です。
|
||||
|
||||
候補は、実装 `0754f41` に次の修正を加えた正確版です。正確な commit は ACCEPTANCE.json に記録します。
|
||||
- 保存契約 v2
|
||||
- 乗車中の客の退館修正
|
||||
- 試験の強化
|
||||
|
||||
## 製品の変更
|
||||
|
||||
1. **EVの待ち時間を shaft 全体で数える。** 同じ籠で別の階の組を待っている人も、1人あたり1分(ORTHOGONAL_MOVE_COST)として各接続の `dynamic_cost` に公開します。更新するのは、乗車の要求・取消の時と、籠の毎分処理の時です。同じ階の組の行列、満員の籠1回あたり2分、移動階数×階あたり時間は、これまでどおり `estimate_elevator_wait_cost` が数えます。
|
||||
2. **保存契約を v2 にする。** ElevatorService の snapshot version を 1 から 2 に上げます。
|
||||
- v1 は旧版の保存で、昇降機の `dynamic_cost` は常に0でした。v1 は0のときだけ受理し、読込後に待ち時間を導出します。
|
||||
- v2 は、行列から導いた値との完全一致を必須にします。全0を含めて、一致しなければ原子的に拒否します。
|
||||
- 旧版 `50ceca0` が書き出した本物の保存(`tests/fixtures/p14-03/legacy_rush_v1`)で移行を確認しました。
|
||||
3. **乗車中の客は籠の中で判断・退館しない。** 乗車中(BOARDED)の客は、籠が有効な階に降ろすまで判断も退館もしません。試験で再現した欠陥は、乗車中に退館を決めた客が乗車中のまま消えるものでした(1094分)。修正後は1115分に1階で降車し、そのあと退館します。
|
||||
|
||||
## A*費用
|
||||
|
||||
| 移動手段 | 内訳 |
|
||||
|---|---|
|
||||
| EV(客用・業務用) | 基本費用+移動階数×階あたり時間+同じ組の待ち行列+shaft の他の組の待ち人数×1分 |
|
||||
| 階段 | 費用150(移動は半速の2分)+セルの混雑費用 |
|
||||
| ワープ | 費用400+到着先セルの混雑費用。停止中・残量不足のときは除外 |
|
||||
| 通路 | 定員を超えた1人ごとに75。2人以上が重なると移動速度は50% |
|
||||
|
||||
## 証拠(`tests/transport/test_route_wait_choice.gd`。full は119 checks、`-- writer` は39、`-- reader` は7)
|
||||
|
||||
| 観点 | 確認したこと |
|
||||
|---|---|
|
||||
| ラッシュ | 実際の客12人が籠Aで3階行きを待つと、0→1階の経路は籠Bへ迂回します。籠Aの0→1階の接続は12人分の待ちを費用に含みます。登録した従業員が実際に歩き、籠Bに乗り、籠Aには乗らずに目的地へ着きます。取消後は、待ち費用がちょうど消えます。 |
|
||||
| 保存・再開 | v2 の保存は完全に往復します。v2 の待ち費用を0や300に改ざんした保存は、原子的に拒否されます。旧版 v1 の本物の保存は移行し、v2 として往復します。v1 に非0値を入れた改ざんは拒否されます。別PIDで再開すると、同じ従業員が同じ物理経路を歩き、全状態の digest が一致します。 |
|
||||
| 相乗り | 実際の客6人が目的地を持ち、Runtime 自身が乗車を要求します。同時に乗るのは定員4人以内です。全員の乗車が終わり、乗車中に退館を指示された1人を除く5人は、存在したまま1階の乗り場 (4,1,0) にいます。退館を指示された1人も、1階で降車してから退館します。 |
|
||||
| 経路喪失 | 籠Bを撤去すると、経路は籠Aに切り替わります。実際に歩く従業員も籠Aで目的地へ着きます。撤去後の状態も保存・往復します。 |
|
||||
| 業務用EV | 裏層の業務用 shaft は従業員専用です。実際の従業員4人が並ぶと、A*費用が上がります。 |
|
||||
| 階段 | 階段で2階をつなぎ、上の乗り場に客6人がいると、階段経路の費用が上がります。 |
|
||||
| ワープ | 出口に客6人がいると、ワープ経路の費用が上がります。 |
|
||||
| 50%減速 | 実際の客5人での確認は `tests/s3/test_runtime_path_traffic.gd:146-158`(定員を超えると半速になり、2分目までに必ず進む)です。`test_path_congestion` は PathService 単体での混雑費用と経路回避の試験です。 |
|
||||
| 救護同伴 | `test_rescue_elevator` で、2人組が同じ籠を予約し、乗車中の保存・読込が完全に一致し、停止時は2人一緒に降りることを確認します。ワープの2人分課金は P14-02 のレビュー(コード確認)です。 |
|
||||
|
||||
## 主張しないこと
|
||||
|
||||
- 階段(150、2分)とEV(階あたり10分)の費用の釣り合いは変えていません。近くに階段があればEVより優先されます。
|
||||
- ワープ到着先の定員4は、混雑費用以外では扱いません。
|
||||
- 退館中の客は位置に関係なく次の分に消えます(既存の仕組み。P06-02 の非主張)。今回の修正は、乗車中だけに限ったものです。
|
||||
- Windows 実機は、適用ありの未検証として残します。
|
||||
|
||||
## レビュー後の確定(2026-10-20)
|
||||
|
||||
- **代表性能 r02**(`artifacts/original-completion-2026-10-02/p14-03-representative-perf-r01/run-r02`)
|
||||
- 収集と機能検証は合格し、版間の比較も成立しました(両版とも checks>0、failures 0)。
|
||||
- **絶対予算との合格判定は、これとは別にします。** x1 の1分 tick は平均約 827 ms(両版)で、4 ms の予算を超えています。経路、save、load は予算内です。
|
||||
- 版間の差分は42ファイルで、P14 だけではありません。そのため「P14 単独の影響はない」とは断定しません。
|
||||
- **r01 の失敗**: save と load の20件は、probe が保存先を artifacts 配下の絶対パスにしたことによる `UNSAFE_PATH` 拒否でした(save-diag-r01)。製品の欠陥ではなく、計測側の誤りです。
|
||||
- **試験への注入**: ラッシュの12人と業務用EVの4人は、試験から `request_trip` で直接並ばせています。実際に歩くのは迂回する従業員だけです。進行中に経路を失った actor の再計画は、この suite では試験していません。
|
||||
- **新PID pair suite**(`test_new_pid_pairs`): 子プロセスは正式全回帰の外側捕捉の対象外です。正式な別PIDの証拠は Root の pair(10 PID)です。
|
||||
- **レビュー依頼書の表記**: `REVIEW_REQUEST.md` は提出時のまま保全しています。71行の「r02実行中・結果欄は空欄」は提出前の古い記載で、実値は同じ文書の74〜79行にあります。性能の「両版が合格しなければ比較不成立」は収集と機能検証の成立を指すもので、絶対予算の合格を意味しません。
|
||||
- **独立Opus レビュー**: PASS(`p14-03-whole-unit-review-r01/review-output.md`)。正式完了は Runner の final を待ちます。
|
||||
@@ -84,7 +84,7 @@
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"name": "all5_confirmed_decisions",
|
||||
"name": "all8_confirmed_decisions",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
@@ -204,8 +204,8 @@
|
||||
"packages": 30,
|
||||
"remaining_delivery_units": 88,
|
||||
"current_package_state_counts": {
|
||||
"completed": 2,
|
||||
"in_progress": 28
|
||||
"completed": 3,
|
||||
"in_progress": 27
|
||||
},
|
||||
"initial_snapshot_checked": false,
|
||||
"historical_source_bytes_compared": false,
|
||||
@@ -222,7 +222,7 @@
|
||||
"ASSETS_PLATFORM_AUDIT.md": "a3a4b8ca0fb39a9abf88692285d26aa879815e092352ba304ef2758e71da50bd",
|
||||
"ATOMIC_REGISTER.md": "6070143b85b7c3b9b73d7c9768d88d2dae4d20407ad54a563cb6ffd74f2373ae",
|
||||
"ATOMIC_SEMANTIC_REVIEW.md": "c156b1fb57c2b4a900f27a19049510f3e9616e723d5edb3417f50d733de5be48",
|
||||
"IMPLEMENTATION_PLAN.md": "317b0cbe5948a3783ef7c167f4ea32f412c71854119b4ad504feedf0420ea3dd",
|
||||
"IMPLEMENTATION_PLAN.md": "823d559e35d5c1aac9385c5bba1ccc56e41e3c2e436e13fda1168e89c56943fb",
|
||||
"IMPORT_UNICODE_AUDIT.md": "88b2382bfbb3411fece8be7e6004ba21d8bf6a47e9d1f3cf573ea6638f79c12f",
|
||||
"P09_VALUE_CONTRACTS.md": "c5abdc4543e54033b1fcfd0943cba266f664d8c10968965fd36ade40da488a25",
|
||||
"PERFORMANCE_AUDIT.md": "c75e806acf825fc4a61f773a2cce7e71be335a1e25dbb15d98091097e8dc47c1",
|
||||
@@ -238,12 +238,12 @@
|
||||
"build_atomic_requirements.py": "6d6ea9e4216372dbd0f9fed3f885a4481b21a3f539a26f64cf98a6830af984c5",
|
||||
"build_facility_value_contracts.py": "35027b23ce75ce9e4e0cdfc5132fd2eb771ba9f598d97750d7ecf9ecb257f769",
|
||||
"current_evidence_links.json": "db1837bcc8497201134297e312f4b266a297102285307ec3075d4e23632e26c2",
|
||||
"decisions.json": "ad0673051b0fa601e525f05128e0b11e2dddd9d4bae6a3e0170bbf47da35efe9",
|
||||
"decisions.json": "8b7dba984d5b1cc0091e8faf8fddb2ab357dd79f1bd093eba46c23c2f62a068c",
|
||||
"facility_value_contracts.json": "447388ba03a29bfac25f2bcd068665aedbb07ba7641ce86f2db93d44f4c9f326",
|
||||
"facility_value_contracts_validation.json": "bf490a9ca585c48fc318e76c4a069ef244827d828d46e6415399f7710590dd16",
|
||||
"performance_audit.json": "0b630b15a392c3e0366f1362bfe4ddacb968f915f54549f82e4d6fa315384762",
|
||||
"progress_contract.json": "ccbdfce8c8365d5c13f59531955708834cce45acd0e136561c95295dfdb5df77",
|
||||
"progress_snapshot.json": "6906588e1199657bfb58df86b8bd205cffb795410b6df56759a4a2a14fbc953b",
|
||||
"progress_snapshot.json": "111faeb32f4772fedd5ab35b723d5b975b59ab8bd913547f38fe849fc71afd8d",
|
||||
"progress_validator_guard_checks.json": "72ee513fcf1cad85c24bfd0a4b472f425f6acf117928ece0f6b41e29018da627",
|
||||
"progress_validator_guard_checks.r01.json": "14c26adffa5a1938241db4912e50ea54b660641ee6fee2e412d467dce14d0519",
|
||||
"requirement_coverage.json": "03b10e4da367579db502d0671a6e0dda36fd385111c6f1c0084acaff7605f593",
|
||||
@@ -251,8 +251,8 @@
|
||||
"source_atomic_coverage.json": "2ab7b9e6a2536ef248d4f7ccd695bf75a082b47018aca7a5a1b6b706fe1907bc",
|
||||
"validate_atomic_requirements.py": "5b2d0b7d72cb124830fbb0a12d1434e6db698148cc5263d33fd5bd7c2953a1e0",
|
||||
"validate_facility_value_contracts.py": "91640e2a76cd6cc9186f7b85c0bf379b24b6cac0d48962815235ee628e7ae43e",
|
||||
"validate_plan.py": "23678227d9b3c556f1bd903f4b5b3147c4a82ce5085e10055481026803451b80",
|
||||
"validate_plan.py": "4973d3c2797289d266f5c6e7c36f3d2c31931b23a1022eab3d94328267bcc1b4",
|
||||
"validator_update_checks.json": "f997d0f3de2cea29550840b1d5cb23647d64f81329de46758551e2b6c8fecc27",
|
||||
"work_packages.json": "1913634b33ca25bec6d5a0a6a4d3d9258b7fd4c9c79c9ab426f21ac2b11c6e81"
|
||||
"work_packages.json": "e1cd56f1fff8a4787d0e378e8b271a59f45b261baff93227f0832d9854996413"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,13 +151,13 @@
|
||||
"scope": "Base defect reproduction and isolated candidate checks; normal source adoption and merged regression require their own evidence."
|
||||
},
|
||||
"current_package_state_counts": {
|
||||
"completed": 2,
|
||||
"in_progress": 28
|
||||
"completed": 3,
|
||||
"in_progress": 27
|
||||
},
|
||||
"current_delivery_state_counts": {
|
||||
"completed": 17,
|
||||
"in_progress": 23,
|
||||
"planned": 48
|
||||
"completed": 21,
|
||||
"in_progress": 20,
|
||||
"planned": 47
|
||||
},
|
||||
"completed_current_deliveries": [
|
||||
"P00-01",
|
||||
@@ -167,10 +167,14 @@
|
||||
"P04-02",
|
||||
"P04-03",
|
||||
"P05-01",
|
||||
"P06-01",
|
||||
"P06-02",
|
||||
"P06-03",
|
||||
"P08-02",
|
||||
"P08-03",
|
||||
"P13-03",
|
||||
"P14-01",
|
||||
"P14-02",
|
||||
"P19-03",
|
||||
"P22-01",
|
||||
"P23-01",
|
||||
@@ -179,7 +183,8 @@
|
||||
"P26-01"
|
||||
],
|
||||
"whole_packages_newly_completed": [
|
||||
"P04"
|
||||
"P04",
|
||||
"P06"
|
||||
],
|
||||
"remaining_acceptance": [
|
||||
"原子条件の8観点と適用外/独立判定者",
|
||||
@@ -4542,7 +4547,7 @@
|
||||
}
|
||||
},
|
||||
"known_original_principal_profit_difference_unresolved": 0,
|
||||
"next_priority_original_principal_profit": "P04 existingD05 answers pending; current independent nextwholeunit P26-01 actualOpus selected. PriorP06 evidence retained, no activeworker.",
|
||||
"next_priority_original_principal_profit": "Current: P14-03 Runner gate on 83b170a (no rerun; evidence binding). Next unit: P28-01 current-HEAD performance baseline.",
|
||||
"historical_before_original_profit_fix_original_principal_profit_reproduction_checkpoint": {
|
||||
"issued_by": "/root",
|
||||
"linked_at_jst": "2026-10-09T07:29:13.140774+09:00",
|
||||
@@ -5233,23 +5238,23 @@
|
||||
"whole_P06_accepted": false,
|
||||
"whole_goal_accepted": false
|
||||
},
|
||||
"next_priority_p06": "Deferred existingP06 original conditions; no duplicate run. CurrentfocusP26-01.",
|
||||
"next_priority_p06": "P06 package completed 2026-10-20 (P06-01/02/03).",
|
||||
"current_P02_01_formal_completion": {
|
||||
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p02-01-completion-acceptance-r01.json",
|
||||
"bytes": 8539,
|
||||
"sha256": "45845238d8a73b84fd346f2d834ee4aaa12d32b45367ea0952b523db9b2523d9"
|
||||
},
|
||||
"next_single_delivery_unit": {
|
||||
"id": "P04-01",
|
||||
"title": "同時発生の更新規約",
|
||||
"id": "P14-03",
|
||||
"title": "混雑と経路選択",
|
||||
"state": "in_progress",
|
||||
"completed": false,
|
||||
"existing_project": "the-tree-p04-01",
|
||||
"source": "2dde7596a87c43f7bf0bf704b7479f2fa06a5b1c",
|
||||
"actual_runner_state": "repair_pending; verify live before next run",
|
||||
"actual_worker": null,
|
||||
"approval_record": "docs/plans/original-completion-2026-10-02/execution/p04-01-owner-approval-20261018/D05_ACTUAL_HUMAN_APPROVAL.json",
|
||||
"remaining": "Adopt actual approved rules/authority refs/registry/pins; qualified minimal real-boundary probes; exact final full roster and whole-unit independent review; normalcandidate/final. No duplicateP26."
|
||||
"candidate_commit": "83b170a4c7176761b0b18d8e9292017d990e80a9",
|
||||
"current_gate_status": "independent Opus review PASS; awaiting Runner registration/validate/review/integration/final",
|
||||
"runner_gate_proposal": "artifacts/original-completion-2026-10-02/p14-03-runner-gate-r01/REGISTRATION_PROPOSAL.json",
|
||||
"original_completed": 21,
|
||||
"original_total": 88,
|
||||
"next_after": "P28-01"
|
||||
},
|
||||
"current_P14_01_contract_audit": {
|
||||
"audit": "artifacts/original-completion-2026-10-02/p14-01-current-contract-audit-r01/audit.json",
|
||||
@@ -9716,7 +9721,7 @@
|
||||
"execution": "the-tree-p06-01 Runner; recorded Claude decision, one Codex implementer",
|
||||
"current_gate_status": "not implemented or executed; formal completion not accepted"
|
||||
},
|
||||
"next_closure_plan_id": "P06-02",
|
||||
"next_closure_plan_id": "P14-03",
|
||||
"p24_03_formal_closure": "docs/plans/original-completion-2026-10-02/execution/p24-03-formal-closure-r01/closure.json",
|
||||
"new_engine_run_scope": "Supplemental positive metadata diagnostic r01 failed cleanup preparation/log isolation; r02 passed17checks unchanged source and entire default profile. No duplicated full game regression.",
|
||||
"current_closure_targets": {
|
||||
@@ -10781,5 +10786,64 @@
|
||||
"support_review": "No verdict: Antigravity actual429; native Claude resets05:00JST",
|
||||
"resume_at_jst": "2026-10-19T05:01:00+09:00",
|
||||
"production_runner_modified": false
|
||||
},
|
||||
"current_p06_01_a06_completion": {
|
||||
"unit": "P06-01",
|
||||
"candidate": "f247d19",
|
||||
"acceptance": "docs/plans/original-completion-2026-10-02/execution/p06-01-a06/ACCEPTANCE.json",
|
||||
"acceptance_sha256": "6f471e3f32460758b8a617051a507f780d70b33f254a7910dfada87a8c5c3484",
|
||||
"owner_approved_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"original_completed": 18,
|
||||
"original_total": 88,
|
||||
"whole_package_completed": false
|
||||
},
|
||||
"current_p06_03_recovery_completion": {
|
||||
"unit": "P06-03",
|
||||
"candidate": "826ef60",
|
||||
"acceptance": "docs/plans/original-completion-2026-10-02/execution/p06-03-recovery/ACCEPTANCE.json",
|
||||
"acceptance_sha256": "4e3da801cec700ba5b4c87e881de931bc4e2e971d4ab44d38af6e964baabc9a7",
|
||||
"owner_approved_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"original_completed": 19,
|
||||
"original_total": 88,
|
||||
"whole_package_completed": false
|
||||
},
|
||||
"current_p14_02_warp_completion": {
|
||||
"unit": "P14-02",
|
||||
"candidate": "8917c19",
|
||||
"acceptance": "docs/plans/original-completion-2026-10-02/execution/p14-02-warp/ACCEPTANCE.json",
|
||||
"acceptance_sha256": "ac7bfd6fb88cb8ac8c195c8051ebe22ba95a6a6393342de089aae88fdcc73307",
|
||||
"approved_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"approval": "delegated_by_user",
|
||||
"original_completed": 20,
|
||||
"original_total": 88,
|
||||
"whole_package_completed": false
|
||||
},
|
||||
"current_p06_02_whole_stop_completion": {
|
||||
"unit": "P06-02",
|
||||
"candidate": "f6f9c9c",
|
||||
"acceptance": "docs/plans/original-completion-2026-10-02/execution/p06-02-whole-stop/ACCEPTANCE.json",
|
||||
"acceptance_sha256": "d5592fa8f6e3646f7d8a81952cf34f894ca4d1048ab0b2a10d87927cd176b9a2",
|
||||
"approved_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"approval": "delegated_by_user",
|
||||
"original_completed": 21,
|
||||
"original_total": 88,
|
||||
"whole_package_completed": false
|
||||
},
|
||||
"historical_next_before_20261020_sync": {
|
||||
"next_single_delivery_unit": {
|
||||
"id": "P04-01",
|
||||
"title": "同時発生の更新規約",
|
||||
"state": "in_progress",
|
||||
"completed": false,
|
||||
"existing_project": "the-tree-p04-01",
|
||||
"source": "2dde7596a87c43f7bf0bf704b7479f2fa06a5b1c",
|
||||
"actual_runner_state": "repair_pending; verify live before next run",
|
||||
"actual_worker": null,
|
||||
"approval_record": "docs/plans/original-completion-2026-10-02/execution/p04-01-owner-approval-20261018/D05_ACTUAL_HUMAN_APPROVAL.json",
|
||||
"remaining": "Adopt actual approved rules/authority refs/registry/pins; qualified minimal real-boundary probes; exact final full roster and whole-unit independent review; normalcandidate/final. No duplicateP26."
|
||||
},
|
||||
"next_closure_plan_id": "P06-02",
|
||||
"next_priority_original_principal_profit": "P04 existingD05 answers pending; current independent nextwholeunit P26-01 actualOpus selected. PriorP06 evidence retained, no activeworker.",
|
||||
"next_priority_p06": "Deferred existingP06 original conditions; no duplicate run. CurrentfocusP26-01."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ def main():
|
||||
pending = {d["id"] for d in decisions["pending"]}
|
||||
check("all10_pending_decision_groups", pending == {f"D{i:02d}" for i in range(1, 11)})
|
||||
check("known_decision_references", all(set(p["decisions"]) <= pending for p in packages))
|
||||
check("all5_confirmed_decisions", {d["id"] for d in decisions["confirmed"]} == {f"A{i:02d}" for i in range(1, 6)})
|
||||
check("all8_confirmed_decisions", {d["id"] for d in decisions["confirmed"]} == {f"A{i:02d}" for i in range(1, 9)})
|
||||
check("confirmed_decision_references_exist", all((PLAN / d["reference"]).is_file() for d in decisions["confirmed"] if "reference" in d))
|
||||
state_counts = dict(collections.Counter(p["state"] for p in packages))
|
||||
check("valid_package_states", all(p["state"] in states for p in packages))
|
||||
|
||||
@@ -6260,7 +6260,7 @@
|
||||
"D04",
|
||||
"D05"
|
||||
],
|
||||
"state": "in_progress",
|
||||
"state": "completed",
|
||||
"supplement_ids": [],
|
||||
"owner_roles": {
|
||||
"implementation": "実装担当(担当者未割当)",
|
||||
@@ -6269,14 +6269,14 @@
|
||||
},
|
||||
"legacy_state": "in_progress",
|
||||
"state_scope": "package-level; completed means whole package accepted; partial implementation is not completion",
|
||||
"current_summary": "停電、厨房、救護の停止復帰は部分実装。有限マナ供給の契約が残る。 制御したEV乗車中救護の停電保存/再開691/0・10全状態pair・無制限journal各9件を限定照合。安全階での緊急降車・自然停電・全館停止・通常UIは未受入。 最新現1299参照で通常建設からの物流/厨房の制御停電・復旧、2正式保存/別PID全状態一致を460/0で限定受入。利用中サービス0件なので途中販売/中断は未受入。全P06未完。 現版の自然入館客の食事/宿泊の制御停電・復旧・一回請求、既存RAIDED休止時の安全終了と正式保存別PID全状態一致を1906/0で限定受入。再開の不具合仮説は反証、ゲーム変更0。全P06未完。 2026-10-12: 旧限定停電証拠から現Runtime等13sourceが変化しており現受入へ自動適用しない。P06-01の原契約監査をRunner経由で開始。正式完了なし。 MP-01-r02承認用資料は追加許可後に同じSol担当で修正し、提出6b78f8fd・正式検証・別Opus5.5独立review PASS・最新main統合/最終検証65a4fe2bを完了。修正使用済み2を保持、現在の個別上限は撤廃済み。全37需要・53source pin・21候補比較・炉の容量判定・旧0/再送/D05を整理。数値/人物分類/更新順序/保存移行は未承認、製品未反映。P06-01はin_progress、原88正式完了増加0。 2026-10-14: MP-01-r02のcompletedと検証/review/統合/最終証拠をRunnerで再確認。推薦契約の採用選択をユーザーへ提示。採用回答は未受領、製品値は未反映。P08-03進行を止める依存ではない。",
|
||||
"current_summary": "全3成果P06-01/02/03を正式受入済み(A06有限残量、自然枯渇の全館停止、復帰と表示)。P06の完了を全ゲーム/Windows/炉の人物投入/根拠未確定の清掃・医務室・直接spawn判定へ転用しない。",
|
||||
"remaining_deliverables": [
|
||||
{
|
||||
"id": "P06-01",
|
||||
"title": "供給と蓄積の契約",
|
||||
"work": "世界樹、マナ炉、消費施設の生成・蓄積・消費と旧ゼロ値保存の意味を整理する。",
|
||||
"acceptance": "初期値や係数の根拠を区別し、旧版移行と残量境界で矛盾がない。",
|
||||
"state": "in_progress",
|
||||
"state": "completed",
|
||||
"evidence": [
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/multi-agent-dev-handoff/runner-start-r01.json",
|
||||
@@ -6304,22 +6304,35 @@
|
||||
"sha256_at_integration": "21eb3136124ed76f7cc9f063e78eea74a773412dd22976535bc54a02fd23db66",
|
||||
"scope": "Completed owner-requested unapproved proposal artifact; validation, independent review, integration and final all pinned. Not P06-01 game acceptance.",
|
||||
"current_game_acceptance_inferred": false
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-01-a06/ACCEPTANCE.json",
|
||||
"sha256_at_integration": "6f471e3f32460758b8a617051a507f780d70b33f254a7910dfada87a8c5c3484",
|
||||
"scope": "A06実装の候補f247d19:独立レビュー2回目PASS、別PID保存再開、境界/移行試験、142本回帰(失敗24本は基準と同一)、性能計測。owner承認2026-10-20。P06-01のみ。",
|
||||
"linked_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"progress_summary": "停電、厨房、救護の停止復帰は部分実装。有限マナ供給の契約が残る。 制御したEV乗車中救護の停電保存/再開691/0・10全状態pair・無制限journal各9件を限定照合。安全階での緊急降車・自然停電・全館停止・通常UIは未受入。 最新現1299参照で通常建設からの物流/厨房の制御停電・復旧、2正式保存/別PID全状態一致を460/0で限定受入。利用中サービス0件なので途中販売/中断は未受入。全P06未完。 現版の自然入館客の食事/宿泊の制御停電・復旧・一回請求、既存RAIDED休止時の安全終了と正式保存別PID全状態一致を1906/0で限定受入。再開の不具合仮説は反証、ゲーム変更0。全P06未完。 2026-10-12: 旧限定停電証拠から現Runtime等13sourceが変化しており現受入へ自動適用しない。P06-01の原契約監査をRunner経由で開始。正式完了なし。 MP-01-r02承認用資料は追加許可後に同じSol担当で修正し、提出6b78f8fd・正式検証・別Opus5.5独立review PASS・最新main統合/最終検証65a4fe2bを完了。修正2/2保持。全37需要・53source pin・21候補比較・炉の容量判定・旧0/再送/D05を整理。数値/人物分類/更新順序/保存移行は未承認、製品未反映。P06-01はin_progress、原88正式完了増加0。",
|
||||
"progress_summary": "A06(MP-01-r02推薦案:初期100・容量300・世界樹20/10分、需要先払い、overflow記録破棄、0で全館停止、炉は需要支払後判定)を製品へ実装。旧版移行は受入契約なしの0〜300のみ保持し他は原子的に拒否、境界間の容量超過は次境界でoverflow。別PID保存再開・Operations巻き戻し・破壊施設除外を試験。独立レビューr01 FAIL→修正→r02 PASS(f247d19)、owner承認で正式完了。炉の人物投入接続・未定義13施設需要・D05詳細・Windows・全P06は対象外。",
|
||||
"remaining_gaps": [
|
||||
"承認用数値・炉対象分類・需要換算・D05更新順序・保存domain/移行元適用条件のオーナー判断。",
|
||||
"承認後の生成・蓄積・消費接続、旧ゼロ保存の実移行・残量境界・原条件全体の現版証拠。",
|
||||
"原P06-01の必要検証・独立レビュー・統合/最終を含む全条件完了。候補資料の完了を代用しない。"
|
||||
],
|
||||
"updated_at_jst": "2026-10-13T17:09:33.500243+09:00"
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"completion_acceptance": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-01-a06/ACCEPTANCE.json",
|
||||
"sha256": "6f471e3f32460758b8a617051a507f780d70b33f254a7910dfada87a8c5c3484",
|
||||
"candidate": "f247d19",
|
||||
"owner_approval": "user_reply_in_conversation 2026-10-20「承認する」"
|
||||
},
|
||||
"regression_scope_correction_20261020": "2026-10-20訂正:当時の回帰はMakefile経由で、collector rosterのみの46 suiteを含まなかった。その範囲で見つかった回帰(v15 mana_reserve保存拒否、test_required_kitchen旧14比較、defense autonomyのマナ前提、P08 palette数)は83b170aで修正し、正式192/192・22198 checks・失敗0(artifacts/original-completion-2026-10-02/p14-03-formal-regression-83b170a-r01)で全roster合格を確認。当該単位の完了・承認・独立証拠はそのまま保全。"
|
||||
},
|
||||
{
|
||||
"id": "P06-02",
|
||||
"title": "全館停止の統合",
|
||||
"work": "利用中、EV、ワープ、物流、救護、清掃、入館へ同じ停止判定を接続する。",
|
||||
"acceptance": "停電中の無条件売上・生産がなく、乗客と搬送対象が消えない。",
|
||||
"state": "in_progress",
|
||||
"state": "completed",
|
||||
"evidence": [
|
||||
{
|
||||
"path": "artifacts/original-completion-2026-10-02/p06-rescue-power-final-root-review-r01.json",
|
||||
@@ -6341,17 +6354,30 @@
|
||||
"scope": "Current1299 naturally admitted active meal/lodging services with controlled reserve;1906/0,28 whole typed,14 exact cross-PID pairs,2 formal8 members;existing RAIDED safe failure verified. Natural depletion,whole P06 and OS pending",
|
||||
"linked_at_jst": "2026-10-09T10:46:38.784861+09:00",
|
||||
"current_game_acceptance_inferred": false
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-02-whole-stop/ACCEPTANCE.json",
|
||||
"sha256_at_integration": "d5592fa8f6e3646f7d8a81952cf34f894ca4d1048ab0b2a10d87927cd176b9a2",
|
||||
"scope": "候補f6f9c9c:7領域共通停止、自然枯渇の統合試験と別PID一致、独立レビューPASS、委任承認。P06-02のみ。",
|
||||
"linked_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"progress_summary": " 制御したEV乗車中救護の停電保存/再開691/0・10全状態pair・無制限journal各9件を限定照合。安全階での緊急降車・自然停電・全館停止・通常UIは未受入。 現版物流/厨房の制御枯渇と復旧の別PID正式保存を460/0で限定照合。 現版食事/宿泊2種の制御枯渇と復旧、一回請求、既存摘発休止の境界を別PID正式保存で1906/0照合。",
|
||||
"updated_at_jst": "2026-10-09T10:46:38.784861+09:00"
|
||||
"progress_summary": "A06自然枯渇の下で利用中・EV・ワープ・物流・救護・清掃・入館の停止を共通判定で確認。需要23の通常塔が自然に0へ落ち、60分間売上・生産・入館・ワープ・乗車0、客は通常退店以外で消えない。停電中保存の別PID継続が一致。独立レビューPASS。根拠未確定3項目は非主張。",
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"completion_acceptance": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-02-whole-stop/ACCEPTANCE.json",
|
||||
"sha256": "d5592fa8f6e3646f7d8a81952cf34f894ca4d1048ab0b2a10d87927cd176b9a2",
|
||||
"candidate": "f6f9c9c",
|
||||
"owner_approval": "delegated_by_user 2026-10-20"
|
||||
},
|
||||
"regression_scope_correction_20261020": "2026-10-20訂正:当時の回帰はMakefile経由で、collector rosterのみの46 suiteを含まなかった。その範囲で見つかった回帰(v15 mana_reserve保存拒否、test_required_kitchen旧14比較、defense autonomyのマナ前提、P08 palette数)は83b170aで修正し、正式192/192・22198 checks・失敗0(artifacts/original-completion-2026-10-02/p14-03-formal-regression-83b170a-r01)で全roster合格を確認。当該単位の完了・承認・独立証拠はそのまま保全。"
|
||||
},
|
||||
{
|
||||
"id": "P06-03",
|
||||
"title": "復帰と表示",
|
||||
"work": "供給復帰時の予約・在庫・運行を再開し、HUDへ供給、消費、蓄積を表示する。",
|
||||
"acceptance": "破壊・摘発の休止を停電復旧で解除しない。途中保存後も復帰結果が一致する。",
|
||||
"state": "in_progress",
|
||||
"state": "completed",
|
||||
"evidence": [
|
||||
{
|
||||
"path": "artifacts/original-completion-2026-10-02/p06-rescue-power-final-root-review-r01.json",
|
||||
@@ -6373,10 +6399,23 @@
|
||||
"scope": "Current1299 naturally admitted active meal/lodging services with controlled reserve;1906/0,28 whole typed,14 exact cross-PID pairs,2 formal8 members;existing RAIDED safe failure verified. Natural depletion,whole P06 and OS pending",
|
||||
"linked_at_jst": "2026-10-09T10:46:38.784861+09:00",
|
||||
"current_game_acceptance_inferred": false
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-03-recovery/ACCEPTANCE.json",
|
||||
"sha256_at_integration": "4e3da801cec700ba5b4c87e881de931bc4e2e971d4ab44d38af6e964baabc9a7",
|
||||
"scope": "候補826ef60:独立レビューPASS、停電中保存の別PID復帰一致、DESTROYED/RAIDED保持、HUD供給/消費/蓄積表示、144本回帰(失敗24本は基準と同一)、HUD報告コスト計測。owner承認2026-10-20。P06-03のみ。",
|
||||
"linked_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"progress_summary": " 制御したEV乗車中救護の停電保存/再開691/0・10全状態pair・無制限journal各9件を限定照合。安全階での緊急降車・自然停電・全館停止・通常UIは未受入。 現版物流/厨房の制御枯渇と復旧の別PID正式保存を460/0で限定照合。 現版食事/宿泊2種の制御枯渇と復旧、一回請求、既存摘発休止の境界を別PID正式保存で1906/0照合。",
|
||||
"updated_at_jst": "2026-10-09T10:46:38.784861+09:00"
|
||||
"progress_summary": "A06有限残量の下で停電復帰を実装・検証。HUDに供給+20/10分・消費・収支・蓄積・直近不足/破棄を表示。停電中保存を別PIDで復帰させ全状態digest一致、破壊・摘発の休止は復帰で解除されない。予約・在庫・運行・接客の再開は既存試験で持続停電下に確認。独立レビューPASS(826ef60)、owner承認で正式完了。P06-02(ワープ)・P06全体・Windowsは対象外。",
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"completion_acceptance": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p06-03-recovery/ACCEPTANCE.json",
|
||||
"sha256": "4e3da801cec700ba5b4c87e881de931bc4e2e971d4ab44d38af6e964baabc9a7",
|
||||
"candidate": "826ef60",
|
||||
"owner_approval": "user_reply_in_conversation 2026-10-20「承認する」"
|
||||
},
|
||||
"regression_scope_correction_20261020": "2026-10-20訂正:当時の回帰はMakefile経由で、collector rosterのみの46 suiteを含まなかった。その範囲で見つかった回帰(v15 mana_reserve保存拒否、test_required_kitchen旧14比較、defense autonomyのマナ前提、P08 palette数)は83b170aで修正し、正式192/192・22198 checks・失敗0(artifacts/original-completion-2026-10-02/p14-03-formal-regression-83b170a-r01)で全roster合格を確認。当該単位の完了・承認・独立証拠はそのまま保全。"
|
||||
}
|
||||
],
|
||||
"estimate_person_days_historical_2026_09_29": [
|
||||
@@ -6408,7 +6447,7 @@
|
||||
],
|
||||
"cross_cutting_acceptance_scope": "Completion requirements only, not achieved results. Apply each to relevant slices; non-applicable views require a recorded reason.",
|
||||
"initial_revision_state": "in_progress",
|
||||
"updated_at_jst": "2026-10-13T17:09:33.500243+09:00",
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"current_controlled_supply_outage_checkpoint": {
|
||||
"issued_by": "/root",
|
||||
"linked_at_jst": "2026-10-09T10:37:43.610273+09:00",
|
||||
@@ -6710,7 +6749,27 @@
|
||||
"all_schema_feature_acceptance": false,
|
||||
"whole_P06_accepted": false,
|
||||
"whole_goal_accepted": false
|
||||
}
|
||||
},
|
||||
"completion_evidence": [
|
||||
{
|
||||
"delivery_id": "P06-01",
|
||||
"state": "completed",
|
||||
"evidence_reference": "work_packages.json:P06.remaining_deliverables.P06-01.evidence",
|
||||
"evidence_count": 6
|
||||
},
|
||||
{
|
||||
"delivery_id": "P06-02",
|
||||
"state": "completed",
|
||||
"evidence_reference": "work_packages.json:P06.remaining_deliverables.P06-02.evidence",
|
||||
"evidence_count": 4
|
||||
},
|
||||
{
|
||||
"delivery_id": "P06-03",
|
||||
"state": "completed",
|
||||
"evidence_reference": "work_packages.json:P06.remaining_deliverables.P06-03.evidence",
|
||||
"evidence_count": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "P07",
|
||||
@@ -8202,7 +8261,7 @@
|
||||
},
|
||||
"legacy_state": "planned",
|
||||
"state_scope": "package-level; completed means whole package accepted; partial implementation is not completion",
|
||||
"current_summary": "採用済み1299参照は初回独立レビューと同一。初回に確認した失敗時保存保護、正常順序・型・所有権・保存復元・経路、1200Kernel ticksの全状態・イベント一致、同版172回帰14636checks/0を維持する。追加証拠で20,000セル・200客・50職員・150施設、既存施設要求200・物流要求200・イベント50の同一Operations OFF状態を採用前後で復元し、snapshot/manual/autosaveを各5試行、各64checks/0として確認。平均snapshotは75.985→74.624ms、manualは425.955→445.803ms、autosaveは280.757→297.499ms。手動保存平均19.848ms、自動保存平均16.741msの実測増加を保持し、ゼロ回帰とは扱わない。採用後保存最大515.903msは既存5秒予算内。P14-01の性能回帰測定不足B01を解消し、この成果単位のみ正式完了可と判定する。5試行の保存・snapshot測定に限定し、simulation、Operations ON、描画、全P28・全P14・全原子・完成素材・全体完成へ拡張しない。Windowsは適用ありの未検証を保持し、通常OS全受入も未完のままとする。 P14-02/03・全P14・Windows全体受入は未完。",
|
||||
"current_summary": "採用済み1299参照は初回独立レビューと同一。初回に確認した失敗時保存保護、正常順序・型・所有権・保存復元・経路、1200Kernel ticksの全状態・イベント一致、同版172回帰14636checks/0を維持する。追加証拠で20,000セル・200客・50職員・150施設、既存施設要求200・物流要求200・イベント50の同一Operations OFF状態を採用前後で復元し、snapshot/manual/autosaveを各5試行、各64checks/0として確認。平均snapshotは75.985→74.624ms、manualは425.955→445.803ms、autosaveは280.757→297.499ms。手動保存平均19.848ms、自動保存平均16.741msの実測増加を保持し、ゼロ回帰とは扱わない。採用後保存最大515.903msは既存5秒予算内。P14-01の性能回帰測定不足B01を解消し、この成果単位のみ正式完了可と判定する。5試行の保存・snapshot測定に限定し、simulation、Operations ON、描画、全P28・全P14・全原子・完成素材・全体完成へ拡張しない。Windowsは適用ありの未検証を保持し、通常OS全受入も未完のままとする。 P14-02は2026-10-20に正式完了(8917c19、A07/A08)。P14-03は独立レビューPASS・Runnerゲート待ち(加算0)、全P14・Windows全体受入は未完。",
|
||||
"remaining_deliverables": [
|
||||
{
|
||||
"id": "P14-01",
|
||||
@@ -8263,7 +8322,7 @@
|
||||
"title": "ワープの通常接続",
|
||||
"work": "マナワープの建設、対象、待ち時間、消費、停止、実移動を接続する。",
|
||||
"acceptance": "途中保存、マナ不足、到着不能時に移動や費用が二重反映されない。",
|
||||
"state": "in_progress",
|
||||
"state": "completed",
|
||||
"evidence": [
|
||||
{
|
||||
"path": "artifacts/original-completion-2026-10-02/p14-02-current-warp-gap-audit-r01/audit.json",
|
||||
@@ -8271,27 +8330,49 @@
|
||||
"scope": "Authoritative requirement/current-catalog gap audit only; no implementation or acceptance",
|
||||
"linked_at_jst": "2026-10-10T00:39:35.742448+09:00",
|
||||
"current_game_acceptance_inferred": false
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-02-warp/ACCEPTANCE.json",
|
||||
"sha256_at_integration": "ac7bfd6fb88cb8ac8c195c8051ebe22ba95a6a6393342de089aae88fdcc73307",
|
||||
"scope": "候補8917c19:A07/A08ワープ、自然営業の客のワープ移動と1回課金、停電/残量不足/到着封鎖の除外、別PID再開で二重反映なし、147本回帰(失敗24本は基準と同一)、性能計測、独立レビュー2回PASS。完了判断はユーザー委任により実施(2026-10-20)。",
|
||||
"linked_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"progress_summary": "原本86/176/211と現1299の通常施設カタログ37定義を再監査。ワープ定義0、通常建設/移動/費用/停止/途中保存の接続証拠は不足。イベント復旧teleportとマウスwarpは通常マナワープへ転用しない。原本の2x2 FRONT・瞬間移動・高コストを保持し、D04必須値/D05費用時点等は未確定のまま。独立レビュー待ちのP14-01を次の正式完了対象として維持。",
|
||||
"progress_summary": "A07表層マナワープ(客用)とA08裏層業務用ワープ(従業員用)を通常建設・経路・課金・停止・保存へ接続。建設済み同種ワープを全組接続し1歩移動、出発確定時に1人3マナを一回課金。残量不足・停電・到着封鎖で経路除外し費用・移動なし。別PID再開で費用・移動の二重反映なし。独立レビューr1/r2 PASS(8917c19)。Windows・完成素材・描画目視は対象外。",
|
||||
"remaining_gaps": [
|
||||
"通常建設できる2x2 FRONTのワープ施設と原本/承認値の費用・工期等の定義",
|
||||
"通常経路選択から対象と出口の所有/予約/実位置へ接続する処理",
|
||||
"承認済み費用確定規約と二重計上拒否",
|
||||
"マナ不足/機能停止/到着不能の原状保持",
|
||||
"途中保存・別PID再開で費用と移動が二重反映されない証拠",
|
||||
"通常UI、影響回帰、性能、完成素材の適用範囲と独立レビュー",
|
||||
"適用する同一配布版のWindows実機条件"
|
||||
"完成素材と描画の目視確認(非主張)",
|
||||
"同一配布版のWindows実機条件(非主張)"
|
||||
],
|
||||
"updated_at_jst": "2026-10-10T00:39:35.742448+09:00"
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00",
|
||||
"completion_acceptance": {
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p14-02-warp/ACCEPTANCE.json",
|
||||
"sha256": "ac7bfd6fb88cb8ac8c195c8051ebe22ba95a6a6393342de089aae88fdcc73307",
|
||||
"candidate": "8917c19",
|
||||
"owner_approval": "delegated_by_user 2026-10-20「承認するかどうかの判断はそちらでやって」"
|
||||
},
|
||||
"remaining_gaps_at_completion": "2026-10-20に解消済み(execution/p14-02-warp/ACCEPTANCE.json)。完成素材・描画目視・Windows実機は非主張として残す。",
|
||||
"regression_scope_correction_20261020": "2026-10-20訂正:当時の回帰はMakefile経由で、collector rosterのみの46 suiteを含まなかった。その範囲で見つかった回帰(v15 mana_reserve保存拒否、test_required_kitchen旧14比較、defense autonomyのマナ前提、P08 palette数)は83b170aで修正し、正式192/192・22198 checks・失敗0(artifacts/original-completion-2026-10-02/p14-03-formal-regression-83b170a-r01)で全roster合格を確認。当該単位の完了・承認・独立証拠はそのまま保全。"
|
||||
},
|
||||
{
|
||||
"id": "P14-03",
|
||||
"title": "混雑と経路選択",
|
||||
"work": "客用・業務用EV、階段、ワープの待ち時間をA*費用へ反映する。",
|
||||
"acceptance": "ラッシュ、相乗り、50%減速、救護同伴、経路喪失を実位置と人数で検証する。",
|
||||
"state": "planned",
|
||||
"evidence": []
|
||||
"state": "in_progress",
|
||||
"evidence": [
|
||||
{
|
||||
"path": "artifacts/original-completion-2026-10-02/p14-03-whole-unit-review-r01/review-output.md",
|
||||
"scope": "独立Opus whole-unit review PASS(入力SHA 0e53d1c1…)。Runner final ではない。",
|
||||
"linked_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"progress_summary": "正確候補83b170a:shaft全体の待ちをA*費用へ、保存契約v2(v1は導出して移行、v2は導出値不一致を改ざん0含め原子拒否、正常v2は往復)、乗車中の客を籠内で退館させない修正。正式全回帰192/192・22198 checks・失敗0、Root別PID pair 5/5・10PID、代表性能r02(収集と機能検証は合格・比較成立、x1は両版約827msで4ms予算超過)、独立Opus whole-unit review PASS。skill契約によりRunnerの正式ゲート(登録・validate・review・統合final)待ちで加算0。",
|
||||
"remaining_gaps": [
|
||||
"Runner登録とvalidate/review/integration/final(登録案と検証器: artifacts/original-completion-2026-10-02/p14-03-runner-gate-r01/)",
|
||||
"代表負荷で実職員が業務用shaftを使う場面は未実施",
|
||||
"x3 frame / stall / 描画 / Windows は未測定(x1は両版とも予算超過、P28で扱う)"
|
||||
],
|
||||
"updated_at_jst": "2026-10-20T00:00:00+09:00"
|
||||
}
|
||||
],
|
||||
"estimate_person_days_historical_2026_09_29": [
|
||||
|
||||
+27
-1
@@ -47,6 +47,8 @@ const MVP_FACILITY_IDS: Array[StringName] = [
|
||||
&"service_elevator",
|
||||
&"guest_stairs",
|
||||
&"service_stairs",
|
||||
&"mana_warp",
|
||||
&"service_warp",
|
||||
&"staff_door",
|
||||
&"guest_corridor",
|
||||
&"service_corridor",
|
||||
@@ -2583,6 +2585,7 @@ func _on_snapshot_published(_snapshot: Dictionary = {}, _digest: String = "") ->
|
||||
_synchronize_elevator_pending()
|
||||
_queue_dashboard_refresh()
|
||||
_update_pause_and_speed()
|
||||
_refresh_mana_tooltip()
|
||||
var operation: Dictionary = _snapshot.get(&"systems", {}).get(&"runtime", {}).get(&"operations", {}).get(&"coordinator", {})
|
||||
var summaries: Array = operation.get(&"day_summaries", [])
|
||||
if summaries.is_empty():
|
||||
@@ -2793,6 +2796,7 @@ func _phase_name(phase: GameClock.Phase) -> String:
|
||||
return "夜"
|
||||
|
||||
|
||||
const MANA_RESERVE := preload("res://scripts/mana/mana_reserve_cycle.gd")
|
||||
const RESOURCE_TITLES := {&"gold": "所持金", &"mana": "マナ", &"materials": "資材", &"faith": "信仰"}
|
||||
|
||||
func _resource_title(resource_name: StringName) -> String:
|
||||
@@ -2805,8 +2809,9 @@ func _on_resource_changed(resource_name: StringName, value: int) -> void:
|
||||
var label := resource_labels[resource_name] as Label
|
||||
label.text = "%s %d" % [_resource_title(resource_name), value]
|
||||
if resource_name == &"mana":
|
||||
label.text += "/%d" % MANA_RESERVE.CAPACITY
|
||||
label.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
label.tooltip_text = "残量0で全館停止。接客・調理・搬入・エレベーターと新規入館が停止します。\n残量と配電が回復すると再開します。破壊・摘発による休止は解除されません。"
|
||||
_refresh_mana_tooltip(label)
|
||||
if value <= 0:
|
||||
label.text += "・停止"
|
||||
label.add_theme_color_override("font_color", Color("ffb095"))
|
||||
@@ -2814,6 +2819,27 @@ func _on_resource_changed(resource_name: StringName, value: int) -> void:
|
||||
label.add_theme_color_override("font_color", _visual_color(&"text_light", Color("fff2d0")))
|
||||
|
||||
|
||||
func _refresh_mana_tooltip(label: Label = null) -> void:
|
||||
if label == null:
|
||||
label = resource_labels.get(&"mana") as Label
|
||||
if label == null:
|
||||
return
|
||||
var lines: Array[String] = []
|
||||
if _runtime != null and _runtime.has_method(&"get_mana_reserve_report"):
|
||||
var report: Dictionary = _runtime.call(&"get_mana_reserve_report")
|
||||
lines.append("供給 +%d/%d分 消費 −%d/%d分 収支 %+d" % [int(report[&"generation"]), int(report[&"interval_minutes"]), int(report[&"demand"]), int(report[&"interval_minutes"]), int(report[&"net"])])
|
||||
var flow: Dictionary = report[&"last_flow"]
|
||||
var stored := "蓄積 %d/%d" % [int(report[&"amount"]), int(report[&"capacity"])]
|
||||
if not flow.is_empty():
|
||||
stored += "(直近:不足 %d・上限超過で破棄 %d)" % [int(flow[&"shortage"]), int(flow[&"overflow"])]
|
||||
lines.append(stored)
|
||||
else:
|
||||
lines.append("世界樹が%d分ごとに%dマナを生成し、施設の維持マナを先に支払って残りを最大%dまで蓄えます。" % [MANA_RESERVE.INTERVAL_MINUTES, MANA_RESERVE.WORLD_TREE_GENERATION, MANA_RESERVE.CAPACITY])
|
||||
lines.append("残量0で全館停止。接客・調理・搬入・エレベーターと新規入館が停止します。")
|
||||
lines.append("残量と配電が回復すると再開します。破壊・摘発による休止は解除されません。")
|
||||
label.tooltip_text = "\n".join(lines)
|
||||
|
||||
|
||||
func _button(text_value: String, callback: Callable, minimum_width := 82) -> Button:
|
||||
var button := Button.new()
|
||||
button.text = text_value
|
||||
|
||||
@@ -1055,6 +1055,12 @@ static func _snapshot_package_format(snapshot: Dictionary) -> int:
|
||||
# Keep PurchaseQuotePlansV2's exact original allowlist and all format rules.
|
||||
runtime_data = runtime_data.duplicate()
|
||||
runtime_data.erase(&"special_peeping_rooms")
|
||||
if runtime_data.has(&"mana_reserve"):
|
||||
# A06 finite reserve: validate the section, then keep the exact original
|
||||
# v15 purchase allowlist unchanged, as for the peeping-room extension.
|
||||
if not preload("res://scripts/mana/mana_reserve_cycle.gd").new().restore_snapshot(runtime_data[&"mana_reserve"]): return -1
|
||||
runtime_data = runtime_data.duplicate()
|
||||
runtime_data.erase(&"mana_reserve")
|
||||
if version >= 1 and version <= 14:
|
||||
return -1 if PurchaseQuoteV2.has_history(runtime_data) else FORMAT_VERSION
|
||||
if version != 15: return -1
|
||||
|
||||
@@ -66,6 +66,13 @@ const INFRASTRUCTURE_FACILITY_PATHS: Array[String] = [
|
||||
"res://data/facilities/infrastructure/service_stairs.tres",
|
||||
]
|
||||
|
||||
## A07 Mana warp: original 10.7 transport, kept apart from the stair list.
|
||||
const TRANSPORT_FACILITY_IDS: Array[StringName] = [&"mana_warp", &"service_warp"]
|
||||
const TRANSPORT_FACILITY_PATHS: Array[String] = [
|
||||
"res://data/facilities/infrastructure/mana_warp.tres",
|
||||
"res://data/facilities/infrastructure/service_warp.tres",
|
||||
]
|
||||
|
||||
## Optional post-MVP expansion definitions. They live below a subdirectory so
|
||||
## DefinitionRegistry's baseline directory scan remains exactly compatible with
|
||||
## existing saves and the sixteen-facility acceptance fixture.
|
||||
|
||||
@@ -231,6 +231,16 @@ func get_storage_current(node_id: StringName) -> int:
|
||||
return int(_nodes[node_id][&"current"])
|
||||
|
||||
|
||||
## Upkeep demand of every registered consumer, including disabled ones,
|
||||
## except the given consumer ids (for example destroyed facilities).
|
||||
func get_total_consumer_demand(excluded_ids: Array = []) -> int:
|
||||
var total := 0
|
||||
for node_id: StringName in _nodes:
|
||||
if int(_nodes[node_id][&"kind"]) == NodeKind.CONSUMER and not node_id in excluded_ids:
|
||||
total += int(_nodes[node_id][&"demand"])
|
||||
return total
|
||||
|
||||
|
||||
func get_last_report() -> Dictionary:
|
||||
return _last_report.duplicate(true)
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
extends RefCounted
|
||||
|
||||
## Approved finite Mana reserve contract A06 (MP-01-r02 recommended plan).
|
||||
## GameState keeps the stored amount so existing spending stays authoritative;
|
||||
## this owns the processed 10-minute boundary and the last flow, so a boundary
|
||||
## is consumed exactly once and replays after rollback reproduce the same flow.
|
||||
const VERSION := 1
|
||||
const POLICY_ID := &"MP-01-r02"
|
||||
const INITIAL_MANA := 100
|
||||
const CAPACITY := 300
|
||||
const WORLD_TREE_GENERATION := 20
|
||||
const INTERVAL_MINUTES := 10
|
||||
const FURNACE_YIELD := 100
|
||||
const LIMIT := 1000000000
|
||||
const FLOW_KEYS: Array[String] = [
|
||||
"from_minute", "to_minute", "before", "generation", "furnace", "demand",
|
||||
"consumed", "shortage", "overflow", "after",
|
||||
]
|
||||
|
||||
var _minute := -1
|
||||
var _last_flow: Dictionary = {}
|
||||
|
||||
func start_at(minute: Variant) -> bool:
|
||||
if _minute >= 0 or typeof(minute) != TYPE_INT or minute < 0:
|
||||
return false
|
||||
_minute = minute - minute % INTERVAL_MINUTES
|
||||
return true
|
||||
|
||||
func get_minute() -> int:
|
||||
return _minute
|
||||
|
||||
func get_last_flow() -> Dictionary:
|
||||
return _last_flow.duplicate(true)
|
||||
|
||||
## Processes every boundary in (last boundary, minute]. Boundaries already
|
||||
## processed are no-ops, so per-minute callers and same-minute retries are safe.
|
||||
## Mana may rise between boundaries (event refunds, commands); anything above
|
||||
## capacity is discarded and recorded as overflow at the next boundary.
|
||||
func advance_to(minute: Variant, before: Variant, demand: Variant) -> Dictionary:
|
||||
if _minute < 0:
|
||||
return _failure(&"NOT_STARTED")
|
||||
if typeof(minute) != TYPE_INT or not _amount(before) \
|
||||
or typeof(demand) != TYPE_INT or demand < 0 or demand > LIMIT or minute < _minute:
|
||||
return _failure(&"INVALID_FLOW")
|
||||
var boundary: int = minute - minute % INTERVAL_MINUTES
|
||||
if boundary <= _minute:
|
||||
return {&"ok": true, &"processed": false, &"after": before}
|
||||
var amount: int = before
|
||||
var flow := {}
|
||||
var count := 0
|
||||
for to_minute: int in range(_minute + INTERVAL_MINUTES, boundary + 1, INTERVAL_MINUTES):
|
||||
flow = plan(amount, demand, 0, to_minute - INTERVAL_MINUTES, to_minute)
|
||||
amount = flow[&"after"]
|
||||
count += 1
|
||||
_minute = boundary
|
||||
_last_flow = flow
|
||||
return {&"ok": true, &"processed": true, &"boundaries": count, &"after": amount, &"flow": flow.duplicate(true)}
|
||||
|
||||
## Furnace offers are judged after natural generation and upkeep demand are
|
||||
## paid: base_after = min(C, max(0, B + G - D)); accept only with 100 free.
|
||||
static func furnace_offer_accepted(before: int, demand: int) -> bool:
|
||||
var base_after := mini(CAPACITY, maxi(0, before + WORLD_TREE_GENERATION - demand))
|
||||
return CAPACITY - base_after >= FURNACE_YIELD
|
||||
|
||||
static func plan(before: int, demand: int, furnace: int, start: int, end: int) -> Dictionary:
|
||||
var available := before + WORLD_TREE_GENERATION + furnace
|
||||
var consumed := mini(available, demand)
|
||||
var remaining := available - consumed
|
||||
return {&"from_minute": start, &"to_minute": end, &"before": before,
|
||||
&"generation": WORLD_TREE_GENERATION, &"furnace": furnace, &"demand": demand,
|
||||
&"consumed": consumed, &"shortage": demand - consumed,
|
||||
&"overflow": maxi(remaining - CAPACITY, 0), &"after": mini(remaining, CAPACITY)}
|
||||
|
||||
## Legacy saves without a reserve receipt must already fit the approved capacity.
|
||||
static func amount_is_valid(value: Variant) -> bool:
|
||||
return _amount(value) and value <= CAPACITY
|
||||
|
||||
## A current save may hold more than capacity until the next boundary settles it.
|
||||
static func stored_amount_is_valid(value: Variant) -> bool:
|
||||
return _amount(value)
|
||||
|
||||
func create_snapshot() -> Dictionary:
|
||||
return {&"version": VERSION, &"policy_id": POLICY_ID, &"capacity": CAPACITY,
|
||||
&"generation": WORLD_TREE_GENERATION, &"interval_minutes": INTERVAL_MINUTES,
|
||||
&"minute": _minute, &"last_flow": _last_flow.duplicate(true)}
|
||||
|
||||
## Validates fully before committing; a rejected snapshot leaves state intact.
|
||||
func restore_snapshot(source: Variant) -> bool:
|
||||
if not source is Dictionary or not _keys(source, ["version", "policy_id", "capacity", "generation", "interval_minutes", "minute", "last_flow"]):
|
||||
return false
|
||||
if typeof(source["version"]) != TYPE_INT or source["version"] != VERSION \
|
||||
or typeof(source["policy_id"]) not in [TYPE_STRING, TYPE_STRING_NAME] \
|
||||
or StringName(source["policy_id"]) != POLICY_ID \
|
||||
or typeof(source["capacity"]) != TYPE_INT or source["capacity"] != CAPACITY \
|
||||
or typeof(source["generation"]) != TYPE_INT or source["generation"] != WORLD_TREE_GENERATION \
|
||||
or typeof(source["interval_minutes"]) != TYPE_INT or source["interval_minutes"] != INTERVAL_MINUTES \
|
||||
or not _amount(source["minute"]) or source["minute"] % INTERVAL_MINUTES != 0 \
|
||||
or not source["last_flow"] is Dictionary:
|
||||
return false
|
||||
var flow: Dictionary = source["last_flow"]
|
||||
if not flow.is_empty():
|
||||
if not _keys(flow, FLOW_KEYS):
|
||||
return false
|
||||
for key: Variant in flow:
|
||||
if not _amount(flow[key]):
|
||||
return false
|
||||
if flow["to_minute"] != source["minute"] \
|
||||
or flow["generation"] != WORLD_TREE_GENERATION \
|
||||
or flow["from_minute"] + INTERVAL_MINUTES != flow["to_minute"]:
|
||||
return false
|
||||
var expected := plan(flow["before"], flow["demand"], flow["furnace"], flow["from_minute"], flow["to_minute"])
|
||||
if not _same_flow(expected, flow):
|
||||
return false
|
||||
_minute = source["minute"]
|
||||
var normalized := {}
|
||||
for key: Variant in flow:
|
||||
normalized[StringName(key)] = flow[key]
|
||||
_last_flow = normalized
|
||||
return true
|
||||
|
||||
static func _same_flow(expected: Dictionary, actual: Dictionary) -> bool:
|
||||
for key: String in FLOW_KEYS:
|
||||
if expected[StringName(key)] != actual[key]:
|
||||
return false
|
||||
return true
|
||||
|
||||
static func _amount(value: Variant) -> bool:
|
||||
return typeof(value) == TYPE_INT and value >= 0 and value <= LIMIT
|
||||
|
||||
static func _keys(source: Dictionary, expected: Array) -> bool:
|
||||
if source.size() != expected.size():
|
||||
return false
|
||||
for key: Variant in source:
|
||||
if typeof(key) not in [TYPE_STRING, TYPE_STRING_NAME] or String(key) not in expected:
|
||||
return false
|
||||
return true
|
||||
|
||||
static func _failure(code: StringName) -> Dictionary:
|
||||
return {&"ok": false, &"code": code}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dhi1bipyd67tx
|
||||
@@ -0,0 +1 @@
|
||||
uid://bi36fvp1jiiiv
|
||||
@@ -4,7 +4,10 @@ extends RefCounted
|
||||
## One physical cabin per contiguous shaft. Path connectors expose reachability;
|
||||
## boarding and arrival here remain authoritative for actual movement.
|
||||
## Dispatch is oldest request first, batching one origin/destination at a time.
|
||||
const SNAPSHOT_VERSION := 1
|
||||
## 2: connectors publish the shaft-wide wait as dynamic_cost (P14-03).
|
||||
## 1: legacy saves, where every elevator connector's dynamic_cost was 0.
|
||||
const SNAPSHOT_VERSION := 2
|
||||
const LEGACY_SNAPSHOT_VERSION := 1
|
||||
const PREFIX := "transport."
|
||||
const REQUEST_PREFIX := "transport.trip."
|
||||
const DOOR_MINUTES := 2
|
||||
@@ -170,6 +173,7 @@ func request_trip(actor_id: StringName, from: Vector3i, to: Vector3i, minute: in
|
||||
&"connector_id": connector_id, &"from": from, &"to": to,
|
||||
&"enqueued_at": minute, &"state": PathService.ELEVATOR_REQUEST_QUEUED}
|
||||
_trips[actor_id] = trip
|
||||
_refresh_shaft_wait(config[&"shaft_id"])
|
||||
last_error = ""
|
||||
return {&"ok": true, &"replay": false, &"trip": trip.duplicate(true)}
|
||||
|
||||
@@ -264,6 +268,7 @@ func _cancel_single(actor_id: StringName) -> bool:
|
||||
last_error = _path.last_error
|
||||
return false
|
||||
_trips.erase(actor_id)
|
||||
_refresh_shaft_wait(trip[&"shaft_id"])
|
||||
last_error = ""
|
||||
return true
|
||||
|
||||
@@ -369,7 +374,8 @@ func create_snapshot() -> Dictionary:
|
||||
func restore_snapshot(snapshot: Dictionary) -> bool:
|
||||
if _path == null or not _exact_keys(snapshot, [&"version", &"current_minute", &"request_sequence", &"shafts", &"trips"]):
|
||||
return _invalid("Elevator snapshot shape is invalid.")
|
||||
if snapshot[&"version"] != SNAPSHOT_VERSION or not snapshot[&"current_minute"] is int or snapshot[&"current_minute"] < 0 \
|
||||
if not snapshot[&"version"] is int or snapshot[&"version"] not in [LEGACY_SNAPSHOT_VERSION, SNAPSHOT_VERSION] \
|
||||
or not snapshot[&"current_minute"] is int or snapshot[&"current_minute"] < 0 \
|
||||
or not snapshot[&"request_sequence"] is int or snapshot[&"request_sequence"] < 0 \
|
||||
or not snapshot[&"shafts"] is Array or not snapshot[&"trips"] is Array:
|
||||
return _invalid("Elevator snapshot header is invalid.")
|
||||
@@ -400,6 +406,8 @@ func restore_snapshot(snapshot: Dictionary) -> bool:
|
||||
var connector_id: StringName = expected[&"id"]
|
||||
var live := _path.get_connector(connector_id)
|
||||
for key: StringName in expected:
|
||||
# The shaft-wide wait is derived from trips and checked below.
|
||||
if key == &"dynamic_cost": continue
|
||||
if not live.has(key) or live[key] != expected[key]: return _invalid("Path elevator configuration disagrees with cabin.")
|
||||
if live[&"cabin_state"] != _path_cabin_state(car) \
|
||||
or live[&"operational_state"] != (PathService.CONNECTOR_OPERATIONAL if car[&"operational"] else PathService.CONNECTOR_STOPPED):
|
||||
@@ -455,6 +463,18 @@ func restore_snapshot(snapshot: Dictionary) -> bool:
|
||||
if trip[key] != other[key]: return _invalid("Companion trip was split.")
|
||||
for actor_id: StringName in boarded_owners:
|
||||
if not next_trips.has(actor_id): return _invalid("Cabin seat has no matching trip.")
|
||||
for shaft_id: StringName in next_shafts:
|
||||
var queued := 0
|
||||
for trip: Dictionary in next_trips.values():
|
||||
if trip[&"shaft_id"] == shaft_id and trip[&"state"] == PathService.ELEVATOR_REQUEST_QUEUED:
|
||||
queued += 1
|
||||
for connector: Dictionary in _connector_configs(next_shafts[shaft_id][&"config"]):
|
||||
var expected_wait := maxi(queued - _path.elevator_queue_size(connector[&"id"]), 0) * PathService.ORTHOGONAL_MOVE_COST
|
||||
# Legacy saves never priced the shaft queue; v2 must match exactly.
|
||||
if snapshot[&"version"] == LEGACY_SNAPSHOT_VERSION:
|
||||
expected_wait = 0
|
||||
if int(_path.get_connector(connector[&"id"]).get(&"dynamic_cost", -1)) != expected_wait:
|
||||
return _invalid("Path shaft wait disagrees with queued trips.")
|
||||
var path_snapshot := _path.create_snapshot()
|
||||
for connector: Dictionary in path_snapshot[&"connectors"]:
|
||||
if str(connector[&"id"]).begins_with(PREFIX) and not expected_connectors.has(connector[&"id"]):
|
||||
@@ -475,6 +495,10 @@ func restore_snapshot(snapshot: Dictionary) -> bool:
|
||||
_trips = next_trips
|
||||
_minute = snapshot[&"current_minute"]
|
||||
_sequence = snapshot[&"request_sequence"]
|
||||
if snapshot[&"version"] == LEGACY_SNAPSHOT_VERSION:
|
||||
# Validated legacy queue: derive the shaft-wide wait once on load.
|
||||
for shaft_id: StringName in _names(_shafts):
|
||||
_refresh_shaft_wait(shaft_id)
|
||||
last_error = ""
|
||||
return true
|
||||
|
||||
@@ -605,6 +629,22 @@ func _publish_cabin(car: Dictionary) -> void:
|
||||
for connector: Dictionary in _connector_configs(car[&"config"]):
|
||||
_path.set_connector_operational_state(connector[&"id"], PathService.CONNECTOR_OPERATIONAL if car[&"operational"] else PathService.CONNECTOR_STOPPED)
|
||||
_path.set_elevator_cabin_state(connector[&"id"], _path_cabin_state(car))
|
||||
_refresh_shaft_wait(car[&"config"][&"shaft_id"])
|
||||
|
||||
|
||||
## The cabin serves the whole shaft, so riders queued for any other floor pair
|
||||
## delay this pair too. PathService already prices the connector's own queue;
|
||||
## the rest of the shaft queue is published as the connector's dynamic cost.
|
||||
func _refresh_shaft_wait(shaft_id: StringName) -> void:
|
||||
if not _shafts.has(shaft_id):
|
||||
return
|
||||
var queued := 0
|
||||
for trip: Dictionary in _trips.values():
|
||||
if trip[&"shaft_id"] == shaft_id and trip[&"state"] == PathService.ELEVATOR_REQUEST_QUEUED:
|
||||
queued += 1
|
||||
for connector: Dictionary in _connector_configs(_shafts[shaft_id][&"config"]):
|
||||
var own := _path.elevator_queue_size(connector[&"id"])
|
||||
_path.set_connector_dynamic_cost(connector[&"id"], maxi(queued - own, 0) * PathService.ORTHOGONAL_MOVE_COST)
|
||||
|
||||
|
||||
func _shaft_busy(shaft_id: StringName) -> bool:
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
extends RefCounted
|
||||
|
||||
## A07/A08 Mana warp: every completed warp is linked to every other warp of the
|
||||
## same kind by one path connector, so crossing is a single instant step. The
|
||||
## FRONT guest warp excludes staff routes; the BACK service warp is staff-only.
|
||||
## Connectors are derived from construction state; their operational state
|
||||
## follows the reserve and the warps' own power. The Runtime charges fares.
|
||||
const PathType := preload("res://scripts/world/path_service.gd")
|
||||
const DEFINITION_ID := &"mana_warp"
|
||||
const SERVICE_DEFINITION_ID := &"service_warp"
|
||||
const DEFINITION_IDS: Array[StringName] = [DEFINITION_ID, SERVICE_DEFINITION_ID]
|
||||
const ACCESS_BY_DEFINITION := {&"mana_warp": &"!staff", &"service_warp": &"staff"}
|
||||
const PREFIX := "warp.connector:"
|
||||
const MANA_PER_TRIP := 3
|
||||
const PATH_COST := 400
|
||||
|
||||
|
||||
static func is_warp_connector(connector_id: StringName) -> bool:
|
||||
return String(connector_id).begins_with(PREFIX)
|
||||
|
||||
|
||||
## Completed warps sorted by instance id: [{instance_id, origin}].
|
||||
static func warps_from_facilities(facilities: Array) -> Array[Dictionary]:
|
||||
var warps: Array[Dictionary] = []
|
||||
for value: Variant in facilities:
|
||||
if not value is Dictionary:
|
||||
continue
|
||||
var facility: Dictionary = value
|
||||
var definition_id := StringName(str(facility.get(&"definition_id", "")))
|
||||
if definition_id not in DEFINITION_IDS:
|
||||
continue
|
||||
var origin: Variant = facility.get(&"origin")
|
||||
if not origin is Vector3i:
|
||||
continue
|
||||
warps.append({&"instance_id": StringName(str(facility.get(&"instance_id", ""))), &"origin": origin,
|
||||
&"definition_id": definition_id})
|
||||
warps.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
||||
return String(a[&"instance_id"]) < String(b[&"instance_id"]))
|
||||
return warps
|
||||
|
||||
|
||||
static func expected_connectors(facilities: Array) -> Array[Dictionary]:
|
||||
var warps := warps_from_facilities(facilities)
|
||||
var result: Array[Dictionary] = []
|
||||
for i: int in warps.size():
|
||||
for j: int in range(i + 1, warps.size()):
|
||||
if warps[i][&"definition_id"] != warps[j][&"definition_id"]:
|
||||
continue
|
||||
result.append({
|
||||
&"access_id": ACCESS_BY_DEFINITION[warps[i][&"definition_id"]],
|
||||
&"id": StringName("%s%s|%s" % [PREFIX, warps[i][&"instance_id"], warps[j][&"instance_id"]]),
|
||||
&"from": warps[i][&"origin"],
|
||||
&"to": warps[j][&"origin"],
|
||||
&"endpoints": [warps[i][&"instance_id"], warps[j][&"instance_id"]],
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
## Rebuilds warp connectors from construction state and applies availability.
|
||||
## available: instance_id -> bool. Atomic: on failure the path is restored. The
|
||||
## rollback snapshot is taken only when a change is actually needed.
|
||||
static func synchronize(path: PathService, facilities: Array, available: Dictionary, fare_affordable: bool) -> Dictionary:
|
||||
var expected := expected_connectors(facilities)
|
||||
var expected_ids: Dictionary = {}
|
||||
for connector: Dictionary in expected:
|
||||
expected_ids[connector[&"id"]] = true
|
||||
var before: Variant = null
|
||||
for connector_id: StringName in path.get_connector_ids_with_prefix(PREFIX):
|
||||
if expected_ids.has(connector_id):
|
||||
continue
|
||||
if before == null:
|
||||
before = path.create_snapshot()
|
||||
if not path.remove_connector(connector_id):
|
||||
path.restore_snapshot(before)
|
||||
return {&"ok": false, &"code": &"WARP_CONNECTOR_REMOVE_FAILED", &"reason": path.last_error}
|
||||
for connector: Dictionary in expected:
|
||||
var connector_id: StringName = connector[&"id"]
|
||||
var existing := path.get_connector(connector_id)
|
||||
var endpoints: Array = connector[&"endpoints"]
|
||||
var usable := fare_affordable and bool(available.get(endpoints[0], false)) and bool(available.get(endpoints[1], false))
|
||||
var state := PathType.CONNECTOR_OPERATIONAL if usable else PathType.CONNECTOR_OUTAGE
|
||||
if not existing.is_empty() and existing[&"from"] == connector[&"from"] and existing[&"to"] == connector[&"to"] \
|
||||
and existing[&"access_id"] == connector[&"access_id"] and existing[&"operational_state"] == state:
|
||||
continue
|
||||
if before == null:
|
||||
before = path.create_snapshot()
|
||||
if existing.is_empty() or existing[&"from"] != connector[&"from"] or existing[&"to"] != connector[&"to"] \
|
||||
or existing[&"access_id"] != connector[&"access_id"]:
|
||||
if not existing.is_empty():
|
||||
path.remove_connector(connector_id)
|
||||
if not path.configure_connector(connector_id, connector[&"from"], connector[&"to"], PATH_COST, true, connector[&"access_id"], true,
|
||||
{&"movement_type": PathType.MOVEMENT_CONNECTOR}):
|
||||
path.restore_snapshot(before)
|
||||
return {&"ok": false, &"code": &"WARP_CONNECTOR_CONFIGURE_FAILED", &"reason": path.last_error}
|
||||
if not path.set_connector_operational_state(connector_id, state):
|
||||
path.restore_snapshot(before)
|
||||
return {&"ok": false, &"code": &"WARP_CONNECTOR_STATE_FAILED", &"reason": path.last_error}
|
||||
return {&"ok": true, &"connector_count": expected.size()}
|
||||
|
||||
|
||||
## Saved paths must hold exactly the derived warp connectors.
|
||||
static func connectors_match(path: PathService, facilities: Array) -> bool:
|
||||
var expected := expected_connectors(facilities)
|
||||
var ids := path.get_connector_ids_with_prefix(PREFIX)
|
||||
if ids.size() != expected.size():
|
||||
return false
|
||||
for connector: Dictionary in expected:
|
||||
var existing := path.get_connector(connector[&"id"])
|
||||
if existing.is_empty() or existing[&"from"] != connector[&"from"] or existing[&"to"] != connector[&"to"] \
|
||||
or int(existing[&"cost"]) != PATH_COST or StringName(existing[&"access_id"]) != connector[&"access_id"] \
|
||||
or existing[&"movement_type"] != PathType.MOVEMENT_CONNECTOR:
|
||||
return false
|
||||
return true
|
||||
@@ -0,0 +1 @@
|
||||
uid://5mjcum1r1poa
|
||||
@@ -89,6 +89,16 @@ static func room(canvas: CanvasItem, rect: Rect2, id: StringName, pixel: float,
|
||||
table(canvas, at + Vector2(-2, -7)*p, p)
|
||||
&"chapel":
|
||||
chapel_furnishings(canvas, rect, p)
|
||||
&"mana_warp", &"service_warp":
|
||||
# Original code-drawn transfer circle and rising motes; existing palette.
|
||||
var ring := Vector2(mid, floor_y - 3.0 * p)
|
||||
canvas.draw_circle(ring, 9.0 * p, INK)
|
||||
canvas.draw_circle(ring, 8.0 * p, TEAL)
|
||||
canvas.draw_circle(ring, 6.0 * p, Color("c1e0d4"))
|
||||
canvas.draw_circle(ring, 4.0 * p, TEAL)
|
||||
for i in 5:
|
||||
var x := mid + (i - 2) * 4.0 * p
|
||||
canvas.draw_rect(Rect2(x, floor_y - (9 + (i % 3) * 5) * p, p, 2.0 * p), CREAM)
|
||||
&"public_bath":
|
||||
canvas.draw_rect(Rect2(rect.position.x+4*p, floor_y-7*p, w-8*p, 7*p), CREAM)
|
||||
canvas.draw_rect(Rect2(rect.position.x+6*p, floor_y-6*p, w-12*p, 4*p), Color("72b6b8"))
|
||||
|
||||
@@ -35,6 +35,8 @@ const KIND_GUEST_ELEVATOR: StringName = &"guest_elevator"
|
||||
const KIND_SERVICE_ELEVATOR: StringName = &"service_elevator"
|
||||
const KIND_GUEST_STAIRS: StringName = &"guest_stairs"
|
||||
const KIND_SERVICE_STAIRS: StringName = &"service_stairs"
|
||||
const KIND_MANA_WARP: StringName = &"mana_warp"
|
||||
const KIND_SERVICE_WARP: StringName = &"service_warp"
|
||||
const SUPPORTED_KINDS: Array[StringName] = [
|
||||
KIND_GUEST_CORRIDOR,
|
||||
KIND_SERVICE_CORRIDOR,
|
||||
@@ -43,6 +45,8 @@ const SUPPORTED_KINDS: Array[StringName] = [
|
||||
KIND_SERVICE_ELEVATOR,
|
||||
KIND_GUEST_STAIRS,
|
||||
KIND_SERVICE_STAIRS,
|
||||
KIND_MANA_WARP,
|
||||
KIND_SERVICE_WARP,
|
||||
]
|
||||
const KNOWN_KINDS: Array[StringName] = [
|
||||
KIND_GUEST_CORRIDOR,
|
||||
@@ -52,6 +56,8 @@ const KNOWN_KINDS: Array[StringName] = [
|
||||
KIND_SERVICE_ELEVATOR,
|
||||
KIND_GUEST_STAIRS,
|
||||
KIND_SERVICE_STAIRS,
|
||||
KIND_MANA_WARP,
|
||||
KIND_SERVICE_WARP,
|
||||
]
|
||||
|
||||
const UNSUPPORTED_VERTICAL_KINDS: Array[StringName] = []
|
||||
@@ -692,6 +698,27 @@ static func _kind_schema(kind: StringName) -> Dictionary:
|
||||
&"guest_access", &"movement", &"schema_facility_proxy", &"vertical_connector",
|
||||
]),
|
||||
}
|
||||
KIND_MANA_WARP:
|
||||
# Walkable FRONT cells; WarpService links every warp pair (A07).
|
||||
return {
|
||||
&"layer": PathType.FRONT,
|
||||
&"footprint": Vector2i(2, 2),
|
||||
&"pass_through": true,
|
||||
&"staff_only": false,
|
||||
&"tags": _sorted_name_array([
|
||||
&"guest_access", &"movement", &"schema_facility_proxy", &"warp",
|
||||
]),
|
||||
}
|
||||
KIND_SERVICE_WARP:
|
||||
return {
|
||||
&"layer": PathType.BACK,
|
||||
&"footprint": Vector2i(2, 2),
|
||||
&"pass_through": true,
|
||||
&"staff_only": true,
|
||||
&"tags": _sorted_name_array([
|
||||
&"movement", &"schema_facility_proxy", &"staff_only", &"warp",
|
||||
]),
|
||||
}
|
||||
KIND_SERVICE_ELEVATOR:
|
||||
return {
|
||||
&"layer": PathType.BACK,
|
||||
|
||||
@@ -395,6 +395,27 @@ func set_connector_enabled(connector_id: StringName, enabled: bool) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
## Cost-only update (no topology change), e.g. waiting riders elsewhere in the
|
||||
## same elevator shaft. Any change may make routes cheaper, so all caches drop.
|
||||
func set_connector_dynamic_cost(connector_id: StringName, dynamic_cost: int) -> bool:
|
||||
if not _connectors.has(connector_id):
|
||||
last_error = "Unknown connector id: %s" % connector_id
|
||||
return false
|
||||
if dynamic_cost < 0:
|
||||
last_error = "Connector dynamic cost must be non-negative."
|
||||
return false
|
||||
var connector: Dictionary = _connectors[connector_id]
|
||||
if int(connector[&"dynamic_cost"]) == dynamic_cost:
|
||||
last_error = ""
|
||||
return true
|
||||
connector[&"dynamic_cost"] = dynamic_cost
|
||||
_connectors[connector_id] = connector
|
||||
traffic_revision += 1
|
||||
invalidate_all_cache()
|
||||
last_error = ""
|
||||
return true
|
||||
|
||||
|
||||
func set_connector_operational_state(connector_id: StringName, state: StringName) -> bool:
|
||||
if not _connectors.has(connector_id):
|
||||
last_error = "Unknown connector id: %s" % connector_id
|
||||
@@ -1420,6 +1441,15 @@ func has_connector(connector_id: StringName) -> bool:
|
||||
return _connectors.has(connector_id)
|
||||
|
||||
|
||||
func get_connector_ids_with_prefix(prefix: String) -> Array[StringName]:
|
||||
var ids: Array[StringName] = []
|
||||
for connector_id: Variant in _connectors:
|
||||
if String(connector_id).begins_with(prefix):
|
||||
ids.append(StringName(connector_id))
|
||||
ids.sort_custom(func(a: StringName, b: StringName) -> bool: return String(a) < String(b))
|
||||
return ids
|
||||
|
||||
|
||||
func get_connector(connector_id: StringName) -> Dictionary:
|
||||
if not _connectors.has(connector_id):
|
||||
return {}
|
||||
@@ -2088,9 +2118,16 @@ func _edge_meets_vertical_policy(from_cell: Vector3i, edge: Dictionary) -> bool:
|
||||
or not StringName(edge[&"connector_id"]).is_empty() or edge[&"movement_type"] != MOVEMENT_WALK
|
||||
|
||||
|
||||
## "" is public, "id" needs id, and "!id" is open to everyone except actors
|
||||
## that carry id (guest-only warp).
|
||||
static func _access_id_permits(required_access: StringName, allowed_access_ids: Array[StringName]) -> bool:
|
||||
if String(required_access).begins_with("!"):
|
||||
return StringName(String(required_access).substr(1)) not in allowed_access_ids
|
||||
return required_access.is_empty() or required_access in allowed_access_ids
|
||||
|
||||
|
||||
func _edge_is_accessible(edge: Dictionary, allowed_access_ids: Array[StringName]) -> bool:
|
||||
var required_access: StringName = edge[&"access_id"]
|
||||
if not required_access.is_empty() and required_access not in allowed_access_ids:
|
||||
if not _access_id_permits(edge[&"access_id"], allowed_access_ids):
|
||||
return false
|
||||
var connector_id: StringName = edge[&"connector_id"]
|
||||
if connector_id.is_empty():
|
||||
@@ -2177,8 +2214,7 @@ func _cell_is_temporarily_blocked(cell: Vector3i) -> bool:
|
||||
## Only the absence of any permitted directed crossing proves unreachable.
|
||||
func _has_permitted_layer_crossing(from_layer: int, to_layer: int, allowed_access_ids: Array[StringName]) -> bool:
|
||||
for connector: Dictionary in _connectors.values():
|
||||
var access_id: StringName = connector[&"access_id"]
|
||||
if not access_id.is_empty() and access_id not in allowed_access_ids:
|
||||
if not _access_id_permits(connector[&"access_id"], allowed_access_ids):
|
||||
continue
|
||||
var from_cell: Vector3i = connector[&"from"]
|
||||
var to_cell: Vector3i = connector[&"to"]
|
||||
@@ -2244,8 +2280,7 @@ func _route_exists_with_overrides(
|
||||
neighbor_set[neighbor] = true
|
||||
for connector_id: StringName in connector_ids:
|
||||
var connector: Dictionary = _connectors[connector_id]
|
||||
var required_access: StringName = connector[&"access_id"]
|
||||
if not required_access.is_empty() and required_access not in allowed_access_ids:
|
||||
if not _access_id_permits(connector[&"access_id"], allowed_access_ids):
|
||||
continue
|
||||
if not allow_unavailable_connectors and not _connector_is_operational(connector):
|
||||
continue
|
||||
|
||||
@@ -274,6 +274,42 @@ func _legacy_controls(writer: Dictionary, reader: Dictionary) -> void:
|
||||
_legacy_control(writer, reader, "byte-identical deep copy", "copy")
|
||||
_proof("legacy14-migration-controls", {"attempts": 17, "writer_sha256": _typed_sha(writer), "reader_sha256": _typed_sha(reader), "failures": failures})
|
||||
|
||||
## Approved later save migrations applied when an older save is loaded, each
|
||||
## checked against its own contract and then mapped into `expected` in place:
|
||||
## A06 starts a fresh finite Mana reserve at the restored boundary, and P14-03
|
||||
## raises the elevator snapshot from v1 to v2 with every other field unchanged.
|
||||
func _approved_runtime_migrations(expected: Dictionary, reader: Dictionary) -> Dictionary:
|
||||
var old: Dictionary = expected[&"systems"][&"runtime"]
|
||||
var now: Dictionary = reader[&"systems"][&"runtime"]
|
||||
var fields: Array = []
|
||||
var proof := {}
|
||||
var ok := true
|
||||
if not old.has(&"mana_reserve") and now.has(&"mana_reserve"):
|
||||
var minute := int(expected[&"clock"][&"absolute_minute"])
|
||||
var fresh := {&"version": 1, &"policy_id": &"MP-01-r02", &"capacity": 300, &"generation": 20,
|
||||
&"interval_minutes": 10, &"minute": minute - minute % 10, &"last_flow": {}}
|
||||
ok = ok and now[&"mana_reserve"] == fresh
|
||||
old[&"mana_reserve"] = now[&"mana_reserve"].duplicate(true)
|
||||
fields.append("mana_reserve_absent_to_A06_fresh_start")
|
||||
proof["mana_reserve"] = {"fresh_start_minute": minute - minute % 10, "matches_contract": ok}
|
||||
if old.get(&"elevators", {}).get(&"version") == 1 and now.get(&"elevators", {}).get(&"version") == 2:
|
||||
var raised: Dictionary = old[&"elevators"].duplicate(true)
|
||||
raised[&"version"] = 2
|
||||
var same_otherwise: bool = raised == now[&"elevators"]
|
||||
ok = ok and same_otherwise
|
||||
old[&"elevators"] = raised
|
||||
fields.append("elevators_v1_to_v2")
|
||||
proof["elevators"] = {"only_version_changed": same_otherwise}
|
||||
if not fields.is_empty():
|
||||
# Saves keep the Runtime's own key order, so the mapped tree follows it.
|
||||
ok = ok and old.size() == now.size()
|
||||
var ordered := {}
|
||||
for key: Variant in now:
|
||||
ok = ok and old.has(key)
|
||||
ordered[key] = old.get(key)
|
||||
expected[&"systems"][&"runtime"] = ordered
|
||||
return {"ok": ok, "fields": fields, "proof": proof}
|
||||
|
||||
func _compare_loaded(writer: Dictionary, write_path: String, read_path: String, label: String, migrate_relationships_v1: bool = false) -> void:
|
||||
var reader: Dictionary = runtime.create_snapshot()
|
||||
var old: Dictionary = writer[&"systems"][&"runtime"][&"domain_events"]["recent_events"].back().duplicate(true)
|
||||
@@ -286,6 +322,8 @@ func _compare_loaded(writer: Dictionary, write_path: String, read_path: String,
|
||||
valid = valid and var_to_bytes(event) == var_to_bytes(old)
|
||||
event["event_type"] = actual["event_type"]; event["payload"] = actual["payload"].duplicate(true); replaced += 1
|
||||
var migration_proof := {}
|
||||
var runtime_migrations := _approved_runtime_migrations(expected, reader)
|
||||
valid = valid and runtime_migrations["ok"]
|
||||
if migrate_relationships_v1:
|
||||
_legacy_controls(expected, reader)
|
||||
var migration := _legacy_checkpoint(expected, reader)
|
||||
@@ -300,6 +338,8 @@ func _compare_loaded(writer: Dictionary, write_path: String, read_path: String,
|
||||
if migrate_relationships_v1:
|
||||
binding["mapped_fields"].append("relationships_v1_to_v2")
|
||||
binding["relationships_v1_to_v2"] = migration_proof
|
||||
binding["mapped_fields"].append_array(runtime_migrations["fields"])
|
||||
binding["approved_runtime_migrations"] = runtime_migrations["proof"]
|
||||
_proof(label + "-io1-binding", binding)
|
||||
expect(valid, "whole typed loaded state unchanged except complete identity/saveID-bound IO1: " + label)
|
||||
|
||||
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
# P14-03 旧形式セーブの fixture
|
||||
|
||||
`legacy_rush_v1/` は、P14-03 より前の本物のセーブパッケージです。待ち時間を shaft 全体で数えるようにする前の版で書き出しています(ElevatorService snapshot v1、Runtime schema 14)。
|
||||
- 籠Aの1階で12人の客が3階行きを待っています。
|
||||
- 昇降機の接続はすべて `dynamic_cost` が0です。
|
||||
|
||||
## 生成方法(2026-10-20)
|
||||
|
||||
1. 作業ツリーの次の2ファイルを、一時的に `50ceca0` 時点の版へ差し替えました。
|
||||
- `scripts/transport/elevator_service.gd`
|
||||
- `scripts/world/path_service.gd`
|
||||
2. `legacy_rush_writer.gd.txt` を `LEGACY_OUT=user://tests/p14_03_legacy_rush` として実行しました(headless)。出力は次のとおりです。
|
||||
- `LEGACY dynamic costs [0, 0, 0, 0, 0, 0, 0]`
|
||||
- `SAVE true`
|
||||
3. 2ファイルを元に戻しました。git の blob が HEAD と一致することを確認済みです。
|
||||
- elevator: 574ba4f3…
|
||||
- path: 5cc9516f…
|
||||
|
||||
## SHA256
|
||||
|
||||
```
|
||||
978fd98f250ae5a9d3249f22adc3dcf032007dde92ddd10d556e050dd87616d8 journal.log
|
||||
80c251341bd3f2a6c4a659f30e477dd856c9fec7961d5b2dfd98d7e9eb5006ab manifest.json
|
||||
0ef55674eca98b6748ae5ccca85a14b042c1545a143810c9c47b487119895c32 thumbnail.png
|
||||
26171ed8994982ed13fe038d225522044f1049070be53d76a4ed5572a227257d world.bin
|
||||
```
|
||||
|
||||
`tests/transport/test_route_wait_choice.gd` が確認する内容は次のとおりです。
|
||||
- この保存は読み込みに成功し、待ち時間が再計算されて v2 として保存されます。
|
||||
- v1 には現れ得ない非0の待ち費用を含む保存は、原子的に拒否されます。
|
||||
@@ -0,0 +1 @@
|
||||
save_completed 2026-10-20T07:38:59
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"absolute_minute": 1091,
|
||||
"checksum_world": "baa5cb8d4eb3ed651934cca0bc1f26f22fd345dc9886a72ca383c3722438ffb9",
|
||||
"content_pack_ids": [],
|
||||
"created_at_real": "2026-10-20T07:38:59",
|
||||
"day": 1,
|
||||
"display_name": "p14_03_legacy_rush",
|
||||
"enabled_filter_profile": "default",
|
||||
"format_version": 1,
|
||||
"game_version": "0.1.0",
|
||||
"save_id": "p14_03_legacy_rush",
|
||||
"seed": 1403,
|
||||
"uncompressed_size": 197716,
|
||||
"updated_at_real": "2026-10-20T07:38:59"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 83 B |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cnde4l2e1lib7"
|
||||
path="res://.godot/imported/thumbnail.png-307ea4ea2aa92de395157267241a407e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://tests/fixtures/p14-03/legacy_rush_v1/thumbnail.png"
|
||||
dest_files=["res://.godot/imported/thumbnail.png-307ea4ea2aa92de395157267241a407e.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
BIN
Binary file not shown.
+34
@@ -0,0 +1,34 @@
|
||||
extends SceneTree
|
||||
var n := 0
|
||||
func c(rt, clock, k, p):
|
||||
n += 1
|
||||
var r: CommandResult = rt.submit_command(k, p, &"player", &"", 0, clock.absolute_minute, StringName("p1403.%d" % n))
|
||||
if not r.is_success(): print("FAIL ", k, " ", r.message)
|
||||
return r
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
func _run() -> void:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var rt: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0); state.reset(); rt.initialize(clock, state, 1403); clock.set_paused(true)
|
||||
c(rt, clock, &"change_resources", {&"deltas": {&"gold": 30000, &"materials": 300}})
|
||||
c(rt, clock, &"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0,0,0)})
|
||||
c(rt, clock, &"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(4,0,0)})
|
||||
clock.advance_minutes(360)
|
||||
c(rt, clock, &"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0,2,0)})
|
||||
for x in [2, 3]: c(rt, clock, &"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(x,1,0)})
|
||||
clock.advance_minutes(360)
|
||||
clock.advance_minutes(10 - int(clock.absolute_minute) % 10 + 1)
|
||||
for i in 12:
|
||||
var gid := StringName("rush_%02d" % i)
|
||||
c(rt, clock, &"spawn_guest", {&"guest_id": gid, &"guest_type_id": &"traveler", &"options": {&"current_cell": Vector3i(0,0,0)}})
|
||||
var t = rt.elevators.request_trip(StringName("guest:%s" % gid), Vector3i(0,0,0), Vector3i(0,3,0), clock.absolute_minute)
|
||||
if not t.ok: print("TRIPFAIL ", t)
|
||||
var dyn := []
|
||||
for row in rt.path_service.create_snapshot()[&"connectors"]:
|
||||
if String(row[&"id"]).begins_with("transport."): dyn.append(int(row[&"dynamic_cost"]))
|
||||
print("LEGACY dynamic costs ", dyn)
|
||||
var out := OS.get_environment("LEGACY_OUT")
|
||||
print("SAVE ", SaveService.save_snapshot(out, rt.create_snapshot())[&"ok"], " ", out)
|
||||
rt.free(); clock.free(); state.free(); quit(0)
|
||||
@@ -0,0 +1,18 @@
|
||||
extends RefCounted
|
||||
|
||||
## Test-only sustained reserve outage under the A06 finite Mana contract.
|
||||
## Zeroing the reserve alone recovers at the next 10-minute boundary because the
|
||||
## world tree keeps generating. A disabled consumer adds upkeep demand that the
|
||||
## reserve must pay but takes no local network allocation, so the tower stays
|
||||
## at 0 until release() without changing any other consumer's supply.
|
||||
const SINK_ID := &"test.mana_outage_sink"
|
||||
const SINK_DEMAND := 1000
|
||||
|
||||
static func hold(runtime: Variant, state: Variant) -> void:
|
||||
if runtime.mana_network_service.get_node(SINK_ID).is_empty():
|
||||
runtime.mana_network_service.register_consumer(SINK_ID, SINK_DEMAND, ManaNetworkService.ConsumerPriority.NORMAL, false)
|
||||
state.set_resource(&"mana", 0)
|
||||
|
||||
static func release(runtime: Variant, state: Variant, amount := 100) -> void:
|
||||
runtime.mana_network_service.unregister_node(SINK_ID)
|
||||
state.set_resource(&"mana", amount)
|
||||
@@ -0,0 +1 @@
|
||||
uid://3h45paa0oqnn
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
var runtime: Variant
|
||||
@@ -59,7 +60,7 @@ func run() -> void:
|
||||
var mid: Dictionary=runtime.create_snapshot()
|
||||
expect(runtime.restore_snapshot(mid), "real guest and in-progress premium service restore together")
|
||||
var elapsed: int=runtime.facility_service.get_service(service_id).elapsed_minutes
|
||||
state.set_resource(&"mana",0)
|
||||
ManaOutage.hold(runtime, state)
|
||||
clock.advance_minutes(15)
|
||||
var paused: ServiceInstance=runtime.facility_service.get_service(service_id)
|
||||
expect(paused.state==ServiceInstance.State.PAUSED and paused.elapsed_minutes==elapsed, "zero mana pauses actual guest lodging without consuming service time")
|
||||
@@ -70,7 +71,7 @@ func run() -> void:
|
||||
var saved := SaveService.load_snapshot(disk_path)
|
||||
expect(saved[&"ok"] and runtime.restore_snapshot(saved[&"snapshot"]), "restore real guest, paused service and empty mana from disk")
|
||||
SaveService.delete_save(disk_path)
|
||||
state.set_resource(&"mana",100)
|
||||
ManaOutage.release(runtime, state)
|
||||
|
||||
for minute in 370:
|
||||
clock.advance_one_minute()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
func _initialize() -> void:
|
||||
@@ -31,11 +32,12 @@ func _run() -> void:
|
||||
runtime.free(); clock.free(); state.free(); quit(failures); return
|
||||
# Only reserve depletion is a diagnostic input; no numeric generation policy
|
||||
# is asserted by this test. Everything else advances through Runtime ticks.
|
||||
state.set_resource(&"mana", 0)
|
||||
ManaOutage.hold(runtime, state)
|
||||
# Pre-fix saves legitimately contain zero reserve with old powered flags.
|
||||
var legacy: Dictionary = runtime.create_snapshot()
|
||||
var legacy_data: Dictionary = legacy[&"systems"][&"runtime"]
|
||||
legacy_data[&"runtime_schema_version"] = 9
|
||||
legacy_data.erase(&"mana_reserve")
|
||||
preload("res://tests/fixtures/pre_upkeep_runtime.gd").strip_empty_upkeep(legacy_data)
|
||||
var source_copy := legacy.duplicate(true)
|
||||
var old_reception_id := reception.instance_id
|
||||
@@ -65,7 +67,7 @@ func _run() -> void:
|
||||
var room_id := room.instance_id
|
||||
var reception_id := reception.instance_id
|
||||
runtime.facility_service.set_operational_status(room_id, {&"suspension_reason": &"DESTROYED"}, clock.absolute_minute)
|
||||
state.set_resource(&"mana", 1)
|
||||
ManaOutage.release(runtime, state, 1)
|
||||
clock.advance_minutes(1)
|
||||
room = runtime.facility_service.get_instance(room_id)
|
||||
reception = runtime.facility_service.get_instance(reception_id)
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
extends SceneTree
|
||||
|
||||
## P06-02 whole-tower stop under the A06 finite reserve, reached naturally: the
|
||||
## tower's own upkeep demand exceeds the world tree's 20 Mana per 10 minutes.
|
||||
## "-- writer" (default) builds an ordinary operating tower, waits for natural
|
||||
## depletion, checks every domain during the outage and saves mid-outage;
|
||||
## "-- reader" restores that save in a new process and must match exactly.
|
||||
const SAVE_PATH := "user://saves/p06_02_whole_outage"
|
||||
const EXPECTED_PATH := "user://saves/p06_02_whole_outage_expected.json"
|
||||
const OUTAGE_OBSERVE_MINUTES := 60
|
||||
const CONTINUE_MINUTES := 60
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
var _serial := 0
|
||||
var _clock: Variant
|
||||
var _state: Variant
|
||||
var _runtime: Variant
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var mode := "reader" if "reader" in OS.get_cmdline_user_args() else "writer"
|
||||
print("Whole tower outage %s (pid %d)" % [mode, OS.get_process_id()])
|
||||
_clock = load("res://autoload/game_clock.gd").new()
|
||||
_state = load("res://autoload/game_state.gd").new()
|
||||
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
||||
_clock.reset(1, 6, 0)
|
||||
_state.reset()
|
||||
_runtime.initialize(_clock, _state, 6602)
|
||||
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_clock.set_paused(true)
|
||||
_expect(bool(_runtime.enable_production_operations().get(&"ok", false)), "natural operations start at Day 1 06:00")
|
||||
if mode == "writer":
|
||||
_write()
|
||||
else:
|
||||
_read()
|
||||
_runtime.free(); _clock.free(); _state.free()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary) -> CommandResult:
|
||||
_serial += 1
|
||||
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("p0602.%d" % _serial))
|
||||
|
||||
|
||||
func _build(id: StringName, origin: Vector3i) -> bool:
|
||||
var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin})
|
||||
_expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message])
|
||||
return result.is_success()
|
||||
|
||||
|
||||
func _facility(id: StringName) -> FacilityInstance:
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
if facility.definition_id == id:
|
||||
return facility
|
||||
return null
|
||||
|
||||
|
||||
## Everything an outage must not create, consume or lose.
|
||||
func _ledger() -> Dictionary:
|
||||
var revenue := 0
|
||||
var wallets := {}
|
||||
for guest: GuestRuntime in _runtime.guest_service.get_guests():
|
||||
wallets[String(guest.guest_id)] = _runtime.economy_service.get_balance(StringName("GUEST_WALLET:%s" % guest.guest_id))
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
revenue += _runtime.economy_service.get_balance(StringName("FACILITY_REVENUE:%s" % facility.instance_id))
|
||||
var meals := 0
|
||||
var stock := 0
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
meals += _runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.BASIC_MEAL)
|
||||
stock += _runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD)
|
||||
var guest_ids: Array[String] = []
|
||||
for guest: GuestRuntime in _runtime.guest_service.get_guests():
|
||||
guest_ids.append(String(guest.guest_id))
|
||||
guest_ids.sort()
|
||||
return {"revenue": revenue, "wallets": wallets, "meals": meals, "raw_food": stock,
|
||||
"guests": guest_ids, "cash": _runtime.economy_service.get_balance(&"PLAYER_CASH"), "staff": _runtime.staff_service.get_staff_members().size()}
|
||||
|
||||
|
||||
func _events_since(minute: int, types: Array) -> int:
|
||||
var count := 0
|
||||
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
|
||||
if event.absolute_minute > minute and event.event_type in types:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
func _write() -> void:
|
||||
_expect(_cmd(&"change_resources", {&"deltas": {&"gold": 30000, &"materials": 300}}).is_success(), "fund the mixed tower fixture")
|
||||
# FRONT: warps x-6..-5, massage x-4..-1, elevator stack x0-1, reception/rooms x2-5, cafeteria x6-7.
|
||||
_build(&"guest_elevator", Vector3i(0, 0, 0))
|
||||
_build(&"reception", Vector3i(2, 0, 0))
|
||||
_build(&"cafeteria", Vector3i(6, 0, 0))
|
||||
_build(&"mana_warp", Vector3i(-6, 0, 0))
|
||||
_build(&"massage_room", Vector3i(-4, 0, 0))
|
||||
_build(&"massage_room", Vector3i(-2, 0, 0))
|
||||
_build(&"warehouse", Vector3i(2, 0, 1))
|
||||
_build(&"security_station", Vector3i(-2, 0, 1))
|
||||
_clock.advance_minutes(360)
|
||||
_build(&"guest_elevator", Vector3i(0, 2, 0))
|
||||
_build(&"standard_room", Vector3i(2, 2, 0))
|
||||
_build(&"mana_warp", Vector3i(-6, 2, 0))
|
||||
_clock.advance_minutes(360)
|
||||
var cafe := _facility(&"cafeteria")
|
||||
if cafe != null:
|
||||
var kitchen: Dictionary = load("res://tests/fixtures/required_kitchen_setup.gd").ensure(_runtime, _clock, cafe.instance_id, false)
|
||||
_expect(bool(kitchen.get(&"ok", false)), "kitchen linked to cafeteria")
|
||||
var reception := _facility(&"reception")
|
||||
_expect(reception != null and _cmd(&"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "hire receptionist")
|
||||
if reception != null:
|
||||
_expect(_cmd(&"set_staff_work_assignment", {&"staff_id": &"staff_akane", &"facility_id": reception.instance_id, &"template_id": &"DAY"}).is_success(), "assign receptionist")
|
||||
var demand: int = _runtime.call(&"_mana_reserve_demand")
|
||||
print("FIXTURE demand=%d mana=%d minute=%d" % [demand, _state.get_resource(&"mana"), _clock.absolute_minute])
|
||||
_expect(demand > 20, "ordinary tower upkeep exceeds world tree generation (natural depletion)")
|
||||
|
||||
# Run normally until the reserve reaches 0 through upkeep alone.
|
||||
var depleted_at := -1
|
||||
for minute: int in range(3000):
|
||||
_clock.advance_minutes(1)
|
||||
if _state.get_resource(&"mana") == 0:
|
||||
depleted_at = _clock.absolute_minute
|
||||
break
|
||||
_expect(depleted_at > 0, "reserve reaches 0 naturally")
|
||||
if depleted_at < 0:
|
||||
return
|
||||
var flow: Dictionary = _runtime.mana_reserve.get_last_flow()
|
||||
_expect(int(flow[&"after"]) == 0 and int(flow[&"demand"]) > int(flow[&"generation"]) and int(flow[&"generation"]) == 20, "depletion comes from upkeep exceeding generation, not a test input")
|
||||
_clock.advance_minutes(1)
|
||||
var before := _ledger()
|
||||
var started: int = _clock.absolute_minute
|
||||
_clock.advance_minutes(OUTAGE_OBSERVE_MINUTES)
|
||||
var after := _ledger()
|
||||
_expect(_state.get_resource(&"mana") == 0, "outage persists while upkeep exceeds generation")
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
_expect(not facility.power_available, "%s is stopped" % facility.definition_id)
|
||||
var wallets_kept := true
|
||||
for guest_id: String in after["wallets"]:
|
||||
if before["wallets"].has(guest_id) and before["wallets"][guest_id] != after["wallets"][guest_id]:
|
||||
wallets_kept = false
|
||||
_expect(after["revenue"] == before["revenue"] and wallets_kept, "no unconditional sales during the outage (revenue and every remaining wallet unchanged)")
|
||||
_expect(_runtime.economy_service.get_balance(&"PLAYER_CASH") <= int(before["cash"]), "no income reaches the tower during the outage")
|
||||
_expect(after["meals"] == before["meals"] and after["raw_food"] == before["raw_food"], "no production and no lost stock during the outage")
|
||||
_expect(_events_since(started, [&"GUEST_SPAWNED", &"GUEST_AUTO_ARRIVED"]) == 0, "no admissions during the outage")
|
||||
_expect(_events_since(started, [&"SERVICE_SALE", &"CASH_SWEEP"]) == 0, "no service sale or revenue sweep event during the outage")
|
||||
_expect(_events_since(started, [&"WARP_TRAVELED"]) == 0, "no warp travel during the outage")
|
||||
_expect(_events_since(started, [&"ELEVATOR_BOARDED"]) == 0, "no elevator boarding during the outage")
|
||||
var departed := {}
|
||||
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
|
||||
if event.absolute_minute > started and event.event_type in [&"GUEST_LEFT", &"GUEST_DESPAWNED"]:
|
||||
departed[String(event.entity_ids[0])] = true
|
||||
for guest_id: String in before["guests"]:
|
||||
_expect(_runtime.guest_service.get_guest(StringName(guest_id)) != null or departed.has(guest_id), "guest %s stays or leaves through an ordinary departure" % guest_id)
|
||||
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
|
||||
_expect(trip[&"state"] == PathService.ELEVATOR_REQUEST_BOARDED, "only held boarded riders remain on a stopped cabin")
|
||||
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
_expect(SaveService.save_snapshot(SAVE_PATH, snapshot)[&"ok"], "save mid-outage")
|
||||
_clock.advance_minutes(CONTINUE_MINUTES)
|
||||
var file := FileAccess.open(EXPECTED_PATH, FileAccess.WRITE)
|
||||
file.store_string(JSON.stringify({
|
||||
"writer_pid": OS.get_process_id(),
|
||||
"ledger": JSON.stringify(_ledger()),
|
||||
"digest": SimulationDigest.compute(_runtime.create_snapshot()),
|
||||
}))
|
||||
file.close()
|
||||
|
||||
|
||||
func _read() -> void:
|
||||
var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(EXPECTED_PATH))
|
||||
_expect(expected is Dictionary, "reader finds writer record")
|
||||
if not expected is Dictionary:
|
||||
return
|
||||
_expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process")
|
||||
var loaded := SaveService.load_snapshot(SAVE_PATH)
|
||||
_expect(loaded[&"ok"] and _runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores mid-outage save")
|
||||
_expect(_state.get_resource(&"mana") == 0, "restored tower is still stopped")
|
||||
_clock.advance_minutes(CONTINUE_MINUTES)
|
||||
_expect(JSON.stringify(_ledger()) == String(expected["ledger"]), "continued outage ledger matches the writer")
|
||||
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == String(expected["digest"]), "whole continued state digest matches")
|
||||
SaveService.delete_save(SAVE_PATH)
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(EXPECTED_PATH))
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://jetoym5gu2ts
|
||||
@@ -66,7 +66,7 @@ func _test_tick_command_execution() -> void:
|
||||
|
||||
func _test_starter_world_layers() -> void:
|
||||
_reset_runtime()
|
||||
_expect(_runtime.definition_registry.facility_count() == 23, "Runtime loads baseline sixteen plus stairs, the infirmary and premium room/dormitory and chapel")
|
||||
_expect(_runtime.definition_registry.facility_count() == 25, "Runtime loads baseline sixteen plus stairs, the infirmary, premium room/dormitory, chapel and A07/A08 warps")
|
||||
_expect(_runtime.definition_registry.has_facility(&"chapel"), "Runtime exposes the original chapel definition")
|
||||
var front_start := Vector3i(-2, 0, WorldService.LAYER_FRONT)
|
||||
var back_goal := Vector3i(2, 0, WorldService.LAYER_BACK)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
extends SceneTree
|
||||
|
||||
## P06-03 outage recovery across a real process boundary. "-- writer" saves
|
||||
## mid-outage and records its own recovery; "-- reader" restores that save in a
|
||||
## new Godot process, recovers the same way and must match exactly. Recovery
|
||||
## must never clear DESTROYED or RAIDED suspension.
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
const OUTAGE_SAVE := "user://saves/p06_03_recovery_new_pid"
|
||||
const EXPECTED_PATH := "user://saves/p06_03_recovery_new_pid_expected.json"
|
||||
const RECOVERY_MINUTES := 30
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var mode := "reader" if "reader" in OS.get_cmdline_user_args() else "writer"
|
||||
print("S2: P06-03 Mana recovery new-PID %s (pid %d)" % [mode, OS.get_process_id()])
|
||||
if mode == "writer":
|
||||
_write()
|
||||
else:
|
||||
_read()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _new_runtime() -> Array:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0)
|
||||
state.reset()
|
||||
runtime.initialize(clock, state, 6606)
|
||||
clock.set_paused(true)
|
||||
return [clock, state, runtime]
|
||||
|
||||
|
||||
func _facility(runtime: Variant, definition_id: StringName) -> FacilityInstance:
|
||||
for facility: FacilityInstance in runtime.facility_service.get_instances():
|
||||
if facility.definition_id == definition_id:
|
||||
return facility
|
||||
return null
|
||||
|
||||
|
||||
func _summary(runtime: Variant, state: Variant) -> Dictionary:
|
||||
var rows := {}
|
||||
for facility: FacilityInstance in runtime.facility_service.get_instances():
|
||||
rows[String(facility.definition_id)] = {
|
||||
"power": facility.power_available,
|
||||
"suspension": String(facility.suspension_reason),
|
||||
"operation": facility.operation_state,
|
||||
}
|
||||
return {"mana": state.get_resource(&"mana"), "facilities": rows}
|
||||
|
||||
|
||||
func _recover(clock: Variant, state: Variant, runtime: Variant) -> void:
|
||||
ManaOutage.release(runtime, state, 0)
|
||||
clock.advance_minutes(RECOVERY_MINUTES)
|
||||
|
||||
|
||||
func _write() -> void:
|
||||
var nodes := _new_runtime()
|
||||
var clock: Variant = nodes[0]
|
||||
var state: Variant = nodes[1]
|
||||
var runtime: Variant = nodes[2]
|
||||
_expect(runtime.submit_command(&"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(-6, 0, 0)}).is_success(), "build reception")
|
||||
clock.advance_minutes(120)
|
||||
_expect(runtime.submit_command(&"build_facility", {&"definition_id": &"standard_room", &"origin": Vector3i(-6, 2, 0)}).is_success(), "build room on supported floor")
|
||||
var cafeteria_built := false
|
||||
for x: int in [-2, 2, -8, -10]:
|
||||
if runtime.submit_command(&"build_facility", {&"definition_id": &"cafeteria", &"origin": Vector3i(x, 0, 0)}).is_success():
|
||||
cafeteria_built = true
|
||||
break
|
||||
_expect(cafeteria_built, "build cafeteria on ground floor")
|
||||
clock.advance_minutes(180)
|
||||
var reception := _facility(runtime, &"reception")
|
||||
var cafeteria := _facility(runtime, &"cafeteria")
|
||||
var room := _facility(runtime, &"standard_room")
|
||||
_expect(reception != null and cafeteria != null and room != null, "facilities complete normally")
|
||||
if reception == null or cafeteria == null or room == null:
|
||||
return
|
||||
runtime.facility_service.set_operational_status(cafeteria.instance_id, {&"suspension_reason": &"DESTROYED"}, clock.absolute_minute)
|
||||
runtime.facility_service.set_operational_status(room.instance_id, {&"suspension_reason": &"RAIDED"}, clock.absolute_minute)
|
||||
ManaOutage.hold(runtime, state)
|
||||
clock.advance_minutes(15)
|
||||
_expect(not _facility(runtime, &"reception").power_available, "outage stops the tower")
|
||||
var outage: Dictionary = runtime.create_snapshot()
|
||||
_expect(SaveService.save_snapshot(OUTAGE_SAVE, outage)[&"ok"], "writer saves mid-outage")
|
||||
_recover(clock, state, runtime)
|
||||
var summary := _summary(runtime, state)
|
||||
_check_recovered(summary)
|
||||
var file := FileAccess.open(EXPECTED_PATH, FileAccess.WRITE)
|
||||
_expect(file != null, "writer records recovery")
|
||||
if file != null:
|
||||
file.store_string(JSON.stringify({
|
||||
"writer_pid": OS.get_process_id(),
|
||||
"summary": summary,
|
||||
"digest": SimulationDigest.compute(runtime.create_snapshot()),
|
||||
}))
|
||||
file.close()
|
||||
for node: Variant in nodes:
|
||||
node.free()
|
||||
|
||||
|
||||
func _read() -> void:
|
||||
var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(EXPECTED_PATH))
|
||||
_expect(expected is Dictionary, "reader finds writer record")
|
||||
if not expected is Dictionary:
|
||||
return
|
||||
_expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process")
|
||||
var nodes := _new_runtime()
|
||||
var clock: Variant = nodes[0]
|
||||
var state: Variant = nodes[1]
|
||||
var runtime: Variant = nodes[2]
|
||||
var loaded := SaveService.load_snapshot(OUTAGE_SAVE)
|
||||
_expect(loaded[&"ok"] and runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores mid-outage save")
|
||||
_expect(state.get_resource(&"mana") == 0 and not _facility(runtime, &"reception").power_available, "restored tower is still stopped")
|
||||
_recover(clock, state, runtime)
|
||||
var summary := _summary(runtime, state)
|
||||
_check_recovered(summary)
|
||||
_expect(JSON.stringify(JSON.parse_string(JSON.stringify(summary))) == JSON.stringify(expected["summary"]), "recovered facility states match the writer")
|
||||
_expect(SimulationDigest.compute(runtime.create_snapshot()) == String(expected["digest"]), "whole recovered state digest matches the writer")
|
||||
SaveService.delete_save(OUTAGE_SAVE)
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(EXPECTED_PATH))
|
||||
for node: Variant in nodes:
|
||||
node.free()
|
||||
|
||||
|
||||
func _check_recovered(summary: Dictionary) -> void:
|
||||
var rows: Dictionary = summary["facilities"]
|
||||
_expect(int(summary["mana"]) > 0, "world tree refills the reserve after the outage")
|
||||
_expect(bool(rows["reception"]["power"]) and String(rows["reception"]["suspension"]) == "", "ordinary facility power returns")
|
||||
_expect(bool(rows["cafeteria"]["power"]) and String(rows["cafeteria"]["suspension"]) == "DESTROYED" and int(rows["cafeteria"]["operation"]) == FacilityInstance.OperationState.SUSPENDED, "recovery does not clear destruction")
|
||||
_expect(bool(rows["standard_room"]["power"]) and String(rows["standard_room"]["suspension"]) == "RAIDED" and int(rows["standard_room"]["operation"]) == FacilityInstance.OperationState.SUSPENDED, "recovery does not clear raid suspension")
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bdhjn1ujekdi3
|
||||
@@ -0,0 +1,65 @@
|
||||
extends SceneTree
|
||||
const Cycle = preload("res://scripts/mana/mana_reserve_cycle.gd")
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
func expect(ok: bool, message: String) -> void:
|
||||
checks += 1
|
||||
if not ok:
|
||||
failures += 1
|
||||
push_error(message)
|
||||
|
||||
func run(before: int, demand: int, minutes: int) -> int:
|
||||
var cycle = Cycle.new()
|
||||
cycle.start_at(360)
|
||||
var amount := before
|
||||
for minute: int in range(361, 360 + minutes + 1):
|
||||
var result: Dictionary = cycle.advance_to(minute, amount, demand)
|
||||
amount = int(result[&"after"])
|
||||
return amount
|
||||
|
||||
func _initialize() -> void:
|
||||
expect(Cycle.INITIAL_MANA == 100 and Cycle.CAPACITY == 300 and Cycle.WORLD_TREE_GENERATION == 20, "approved A06 values")
|
||||
# Proposal comparison table (recommended column), F=0, start 360.
|
||||
expect(run(100, 2, 120) == 300, "low load fills to capacity")
|
||||
expect(run(100, 16, 120) == 148, "normal load 100+12*(20-16)")
|
||||
expect(run(100, 63, 120) == 0, "overload depletes")
|
||||
expect(run(0, 16, 10) == 4, "zero reserve recovers on next boundary")
|
||||
expect(run(300, 2, 10) == 300, "full reserve keeps capacity")
|
||||
var cycle = Cycle.new()
|
||||
expect(not cycle.advance_to(370, 100, 16)[&"ok"], "unstarted cycle rejected")
|
||||
expect(cycle.start_at(365) and cycle.get_minute() == 360, "start floors to boundary")
|
||||
expect(not cycle.advance_to(369, 100, 16)[&"processed"], "non-boundary minute is a no-op")
|
||||
var flow: Dictionary = cycle.advance_to(370, 100, 63)[&"flow"]
|
||||
expect(flow[&"after"] == 57 and flow[&"consumed"] == 63 and flow[&"overflow"] == 0, "demand paid from generation and reserve")
|
||||
var saved: Dictionary = cycle.create_snapshot()
|
||||
expect(not cycle.advance_to(370, 57, 63)[&"processed"] and cycle.create_snapshot() == saved, "same boundary never consumed twice")
|
||||
expect(not cycle.advance_to(360, 57, 63)[&"ok"], "time reversal rejected")
|
||||
expect(not cycle.advance_to(380, -1, 0)[&"ok"] and cycle.create_snapshot() == saved, "negative reserve rejected without change")
|
||||
var high = Cycle.new()
|
||||
high.start_at(360)
|
||||
flow = high.advance_to(370, 525, 16)[&"flow"]
|
||||
expect(flow[&"after"] == 300 and flow[&"overflow"] == 229, "amount raised above capacity between boundaries settles with recorded overflow")
|
||||
var high_restored = Cycle.new()
|
||||
expect(high_restored.restore_snapshot(high.create_snapshot()), "over-capacity before survives snapshot validation")
|
||||
var skipped: Dictionary = cycle.advance_to(400, 57, 63)
|
||||
expect(skipped[&"boundaries"] == 3 and skipped[&"after"] == 0 and skipped[&"flow"][&"shortage"] == 43, "skipped boundaries settle in order")
|
||||
var restored = Cycle.new()
|
||||
expect(restored.restore_snapshot(cycle.create_snapshot()) and restored.create_snapshot() == cycle.create_snapshot(), "snapshot round trip")
|
||||
var corrupt: Dictionary = cycle.create_snapshot()
|
||||
corrupt[&"last_flow"][&"after"] += 1
|
||||
var untouched = Cycle.new()
|
||||
expect(not untouched.restore_snapshot(corrupt) and untouched.get_minute() == -1, "tampered flow rejected atomically")
|
||||
corrupt = cycle.create_snapshot()
|
||||
corrupt[&"capacity"] = 600
|
||||
expect(not untouched.restore_snapshot(corrupt), "foreign policy capacity rejected")
|
||||
corrupt = cycle.create_snapshot()
|
||||
corrupt[&"minute"] = 405
|
||||
expect(not untouched.restore_snapshot(corrupt), "off-boundary minute rejected")
|
||||
# Furnace is judged after natural generation and demand payment.
|
||||
expect(not Cycle.furnace_offer_accepted(200, 16), "B=200 base_after 204 free 96 rejected")
|
||||
expect(Cycle.furnace_offer_accepted(196, 16), "B=196 base_after 200 free 100 accepted")
|
||||
expect(not Cycle.furnace_offer_accepted(300, 16), "B=300 full rejected")
|
||||
expect(Cycle.plan(196, 16, 100, 360, 370)[&"after"] == 300, "accepted furnace offer fills exactly")
|
||||
expect(Cycle.amount_is_valid(0) and Cycle.amount_is_valid(300) and not Cycle.amount_is_valid(301) and not Cycle.amount_is_valid(-1) and not Cycle.amount_is_valid(1.5), "legacy amount bounds")
|
||||
print("%s: %d checks" % ["PASS" if failures == 0 else "FAIL", checks])
|
||||
quit(failures)
|
||||
@@ -0,0 +1 @@
|
||||
uid://de3w8hfsk83nt
|
||||
@@ -0,0 +1,111 @@
|
||||
extends SceneTree
|
||||
|
||||
## A06 reserve across a real process boundary. Run "-- writer" first, then
|
||||
## "-- reader" in a new Godot process; the reader must reproduce the writer's
|
||||
## continuation exactly from the disk save, and migrate a legacy save.
|
||||
const Cycle = preload("res://scripts/mana/mana_reserve_cycle.gd")
|
||||
const CURRENT_SAVE := "user://saves/a06_mana_new_pid_current"
|
||||
const LEGACY_SAVE := "user://saves/a06_mana_new_pid_legacy"
|
||||
const EXPECTED_PATH := "user://saves/a06_mana_new_pid_expected.json"
|
||||
const CONTINUE_MINUTES := 75
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var mode := "writer" if not "reader" in OS.get_cmdline_user_args() else "reader"
|
||||
print("S2: A06 Mana reserve new-PID %s (pid %d)" % [mode, OS.get_process_id()])
|
||||
if mode == "writer":
|
||||
_write()
|
||||
else:
|
||||
_read()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _new_runtime() -> Array:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0)
|
||||
state.reset()
|
||||
runtime.initialize(clock, state, 4404)
|
||||
clock.set_paused(true)
|
||||
return [clock, state, runtime]
|
||||
|
||||
|
||||
func _free(nodes: Array) -> void:
|
||||
for node: Variant in nodes:
|
||||
node.free()
|
||||
|
||||
|
||||
func _write() -> void:
|
||||
var nodes := _new_runtime()
|
||||
var clock: Variant = nodes[0]
|
||||
var state: Variant = nodes[1]
|
||||
var runtime: Variant = nodes[2]
|
||||
# Stop mid-interval so the reader must not settle the pending boundary early.
|
||||
clock.advance_minutes(43)
|
||||
var snapshot: Dictionary = runtime.create_snapshot()
|
||||
_expect(SaveService.save_snapshot(CURRENT_SAVE, snapshot)[&"ok"], "writer saves current A06 reserve")
|
||||
var legacy: Dictionary = snapshot.duplicate(true)
|
||||
legacy[&"systems"][&"runtime"].erase(&"mana_reserve")
|
||||
legacy[&"game_state"][&"resources"][&"mana"] = 0
|
||||
_expect(SaveService.save_snapshot(LEGACY_SAVE, legacy)[&"ok"], "writer saves legacy zero-reserve save")
|
||||
var saved_mana: int = state.get_resource(&"mana")
|
||||
clock.advance_minutes(CONTINUE_MINUTES)
|
||||
var expected := {
|
||||
"writer_pid": OS.get_process_id(),
|
||||
"saved_minute": int(snapshot[&"clock"][&"absolute_minute"]),
|
||||
"saved_mana": saved_mana,
|
||||
"mana": state.get_resource(&"mana"),
|
||||
"reserve": var_to_str(runtime.mana_reserve.create_snapshot()),
|
||||
"digest": SimulationDigest.compute(runtime.create_snapshot()),
|
||||
}
|
||||
var file := FileAccess.open(EXPECTED_PATH, FileAccess.WRITE)
|
||||
_expect(file != null, "writer records continuation")
|
||||
if file != null:
|
||||
file.store_string(JSON.stringify(expected))
|
||||
file.close()
|
||||
_free(nodes)
|
||||
|
||||
|
||||
func _read() -> void:
|
||||
var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(EXPECTED_PATH))
|
||||
_expect(expected is Dictionary, "reader finds writer record")
|
||||
if not expected is Dictionary:
|
||||
return
|
||||
_expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process")
|
||||
var nodes := _new_runtime()
|
||||
var clock: Variant = nodes[0]
|
||||
var state: Variant = nodes[1]
|
||||
var runtime: Variant = nodes[2]
|
||||
var loaded := SaveService.load_snapshot(CURRENT_SAVE)
|
||||
_expect(loaded[&"ok"] and runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores current save from disk")
|
||||
_expect(clock.absolute_minute == int(expected["saved_minute"]) and state.get_resource(&"mana") == int(expected["saved_mana"]), "restored minute and reserve match the save")
|
||||
clock.advance_minutes(CONTINUE_MINUTES)
|
||||
_expect(state.get_resource(&"mana") == int(expected["mana"]), "continued reserve matches the writer")
|
||||
_expect(var_to_str(runtime.mana_reserve.create_snapshot()) == String(expected["reserve"]), "continued reserve receipt matches the writer")
|
||||
_expect(SimulationDigest.compute(runtime.create_snapshot()) == String(expected["digest"]), "whole continued state digest matches the writer")
|
||||
|
||||
var legacy := SaveService.load_snapshot(LEGACY_SAVE)
|
||||
_expect(legacy[&"ok"] and runtime.restore_snapshot(legacy[&"snapshot"]), "reader migrates legacy save from disk")
|
||||
_expect(state.get_resource(&"mana") == 0 and runtime.mana_reserve.get_last_flow().is_empty(), "legacy explicit 0 stays stopped")
|
||||
_expect(runtime.create_snapshot()[&"systems"][&"runtime"].has(&"mana_reserve"), "migrated state saves with the A06 reserve")
|
||||
for path: String in [CURRENT_SAVE, LEGACY_SAVE]:
|
||||
SaveService.delete_save(path)
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(EXPECTED_PATH))
|
||||
_free(nodes)
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://c340fnfuwchcs
|
||||
@@ -0,0 +1,68 @@
|
||||
extends SceneTree
|
||||
|
||||
## Runs every writer/reader save-restart pair as two real child Godot processes
|
||||
## (distinct PIDs) from this one suite, so a script-level regression roster also
|
||||
## proves the new-PID halves. Each child's complete stdout+stderr and exit code
|
||||
## are kept under user://pair-evidence/ (inside the run's private profile);
|
||||
## summaries printed here avoid completion-marker wording on purpose.
|
||||
const PAIRS: Array[String] = [
|
||||
"res://tests/s2/test_mana_reserve_new_pid.gd",
|
||||
"res://tests/s2/test_mana_recovery_new_pid.gd",
|
||||
"res://tests/transport/test_runtime_warp.gd",
|
||||
"res://tests/mvp/test_whole_tower_outage.gd",
|
||||
"res://tests/transport/test_route_wait_choice.gd",
|
||||
]
|
||||
const EVIDENCE_DIR := "user://pair-evidence"
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var engine := OS.get_executable_path()
|
||||
var project := ProjectSettings.globalize_path("res://")
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(EVIDENCE_DIR))
|
||||
print("New-PID pairs: parent pid %d engine %s" % [OS.get_process_id(), engine])
|
||||
for script: String in PAIRS:
|
||||
var pids := {}
|
||||
for mode: String in ["writer", "reader"]:
|
||||
var output: Array = []
|
||||
var code := OS.execute(engine, ["--headless", "--path", project, "--script", script, "--", mode], output, true)
|
||||
var text := "".join(output)
|
||||
var name := "%s.%s" % [script.get_file().get_basename(), mode]
|
||||
var file := FileAccess.open(EVIDENCE_DIR.path_join(name + ".log"), FileAccess.WRITE)
|
||||
file.store_string(text)
|
||||
file.close()
|
||||
var pid := _child_pid(text)
|
||||
pids[mode] = pid
|
||||
var passed := code == 0 and _child_marker_passed(text)
|
||||
print(" pair %s %s exit=%d child_pid=%d bytes=%d sha256=%s" % [script.get_file(), mode, code, pid, text.to_utf8_buffer().size(), text.sha256_text()])
|
||||
_expect(passed, "%s %s child completes with zero failures" % [script.get_file(), mode])
|
||||
_expect(int(pids["writer"]) > 0 and int(pids["reader"]) > 0 and pids["writer"] != pids["reader"] and pids["reader"] != OS.get_process_id(), "%s writer and reader ran in distinct processes" % script.get_file())
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
## Each pair script prints its own pid on its first line ("... pid N" or "(pid N)").
|
||||
static func _child_pid(text: String) -> int:
|
||||
var regex := RegEx.new()
|
||||
regex.compile("pid (\\d+)")
|
||||
var found := regex.search(text)
|
||||
return int(found.get_string(1)) if found != null else -1
|
||||
|
||||
|
||||
static func _child_marker_passed(text: String) -> bool:
|
||||
var regex := RegEx.new()
|
||||
regex.compile("(?m)^PASS: (\\d+) checks$")
|
||||
var found := regex.search(text)
|
||||
return found != null and int(found.get_string(1)) > 0 and not text.contains("FAIL:") and not text.contains("SCRIPT ERROR")
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if not condition:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bykhpoojytbrd
|
||||
@@ -0,0 +1,151 @@
|
||||
extends SceneTree
|
||||
|
||||
const Cycle = preload("res://scripts/mana/mana_reserve_cycle.gd")
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
var _clock: Variant
|
||||
var _state: Variant
|
||||
var _runtime: Variant
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
print("S2: runtime finite Mana reserve (A06)")
|
||||
_reset_runtime()
|
||||
_test_generation_and_demand()
|
||||
_test_save_round_trip_and_migration()
|
||||
_test_depletion_stops_tower()
|
||||
_test_between_boundary_increase()
|
||||
_test_destroyed_facility_demand()
|
||||
_cleanup()
|
||||
if _failures == 0:
|
||||
print("PASS: %d checks" % _checks)
|
||||
else:
|
||||
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _reset_runtime() -> void:
|
||||
_cleanup()
|
||||
_clock = load("res://autoload/game_clock.gd").new()
|
||||
_state = load("res://autoload/game_state.gd").new()
|
||||
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
||||
_clock.reset(1, 6, 0)
|
||||
_state.reset()
|
||||
var report: Dictionary = _runtime.initialize(_clock, _state, 3303)
|
||||
_clock.set_paused(true)
|
||||
_expect(report[&"valid"], "runtime initializes")
|
||||
_expect(_state.get_resource(&"mana") == Cycle.INITIAL_MANA, "new game starts with approved 100 Mana")
|
||||
_expect(_runtime.mana_reserve.get_minute() == _clock.absolute_minute, "reserve starts on the opening boundary")
|
||||
|
||||
|
||||
func _cleanup() -> void:
|
||||
for node: Variant in [_runtime, _clock, _state]:
|
||||
if node != null:
|
||||
node.free()
|
||||
_runtime = null
|
||||
_clock = null
|
||||
_state = null
|
||||
|
||||
|
||||
func _test_generation_and_demand() -> void:
|
||||
_clock.advance_minutes(10)
|
||||
var demand: int = _runtime.mana_network_service.get_total_consumer_demand()
|
||||
var flow: Dictionary = _runtime.mana_reserve.get_last_flow()
|
||||
_expect(flow[&"generation"] == 20 and flow[&"demand"] == demand, "boundary uses world tree 20 and registered demand")
|
||||
_expect(_state.get_resource(&"mana") == mini(Cycle.CAPACITY, 100 + 20 - demand), "one boundary settles exactly once")
|
||||
_clock.advance_minutes(9)
|
||||
var mid: int = _state.get_resource(&"mana")
|
||||
_expect(mid == int(flow[&"after"]), "no settlement between boundaries")
|
||||
_clock.advance_minutes(200)
|
||||
_expect(_state.get_resource(&"mana") <= Cycle.CAPACITY, "reserve never exceeds capacity")
|
||||
|
||||
|
||||
func _test_save_round_trip_and_migration() -> void:
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
var runtime_data: Dictionary = snapshot[&"systems"][&"runtime"]
|
||||
_expect(runtime_data.has(&"mana_reserve"), "new saves carry the reserve contract")
|
||||
_expect(_runtime.restore_snapshot(snapshot.duplicate(true)), "current save restores")
|
||||
_expect(_runtime.create_snapshot() == snapshot, "restored reserve state is identical")
|
||||
|
||||
var legacy: Dictionary = snapshot.duplicate(true)
|
||||
legacy[&"systems"][&"runtime"].erase(&"mana_reserve")
|
||||
legacy[&"game_state"][&"resources"][&"mana"] = 0
|
||||
_expect(_runtime.restore_snapshot(legacy), "legacy save with explicit 0 migrates")
|
||||
_expect(_state.get_resource(&"mana") == 0 and _runtime.mana_reserve.get_last_flow().is_empty(), "explicit 0 stays 0 with fresh reserve")
|
||||
|
||||
var over: Dictionary = snapshot.duplicate(true)
|
||||
over[&"systems"][&"runtime"].erase(&"mana_reserve")
|
||||
over[&"game_state"][&"resources"][&"mana"] = Cycle.CAPACITY + 1
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
_expect(not _runtime.restore_snapshot(over), "legacy amount over capacity is rejected")
|
||||
_expect(_runtime.create_snapshot() == before, "rejected migration keeps live state")
|
||||
|
||||
var tampered: Dictionary = snapshot.duplicate(true)
|
||||
tampered[&"systems"][&"runtime"][&"mana_reserve"][&"last_flow"][&"after"] += 1
|
||||
_expect(not _runtime.restore_snapshot(tampered), "tampered reserve flow is rejected")
|
||||
_expect(_runtime.restore_snapshot(snapshot.duplicate(true)), "original save still restores")
|
||||
|
||||
|
||||
func _test_depletion_stops_tower() -> void:
|
||||
_state.set_resource(&"mana", 0)
|
||||
var demand: int = _runtime.mana_network_service.get_total_consumer_demand()
|
||||
_runtime.mana_network_service.register_consumer(&"test.mana_sink", 40)
|
||||
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
||||
_expect(_state.get_resource(&"mana") == 0, "demand above generation keeps the reserve at 0")
|
||||
_expect(int(_runtime.mana_reserve.get_last_flow()[&"shortage"]) == demand + 40 - 20, "shortage is recorded")
|
||||
_runtime.mana_network_service.unregister_node(&"test.mana_sink")
|
||||
_clock.advance_minutes(10)
|
||||
_expect(_state.get_resource(&"mana") == maxi(0, 20 - demand), "world tree recovers the reserve at the next boundary")
|
||||
|
||||
|
||||
func _test_between_boundary_increase() -> void:
|
||||
_clock.advance_minutes(10 - (_clock.absolute_minute % 10) + 3)
|
||||
_state.add_resource(&"mana", 400)
|
||||
var high: int = _state.get_resource(&"mana")
|
||||
_expect(high > Cycle.CAPACITY, "mana can rise above capacity between boundaries (event refund / command)")
|
||||
var saved: Dictionary = _runtime.create_snapshot()
|
||||
_expect(_runtime.restore_snapshot(saved.duplicate(true)) and _state.get_resource(&"mana") == high, "over-capacity mid-interval save restores")
|
||||
_clock.advance_minutes(1)
|
||||
_expect(not _runtime.get_operations_report()[&"runtime_failure_latched"], "over-capacity amount does not latch a runtime fault")
|
||||
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
||||
var flow: Dictionary = _runtime.mana_reserve.get_last_flow()
|
||||
_expect(_state.get_resource(&"mana") == Cycle.CAPACITY and flow[&"before"] == high, "next boundary settles to capacity")
|
||||
_expect(flow[&"overflow"] == high + 20 - int(flow[&"consumed"]) - Cycle.CAPACITY, "excess is recorded as overflow")
|
||||
|
||||
|
||||
func _test_destroyed_facility_demand() -> void:
|
||||
var build = _runtime.submit_command(
|
||||
&"build_facility",
|
||||
{&"definition_id": &"cafeteria", &"origin": Vector3i(-5, 0, WorldService.LAYER_FRONT), &"rotation": 0},
|
||||
&"player", &"", 50, -1, &"a06.build.cafeteria"
|
||||
)
|
||||
_expect(build.is_success(), "cafeteria construction starts")
|
||||
_clock.advance_minutes(180)
|
||||
var cafeteria: FacilityInstance = null
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
if facility.definition_id == &"cafeteria":
|
||||
cafeteria = facility
|
||||
_expect(cafeteria != null, "cafeteria completes")
|
||||
if cafeteria == null:
|
||||
return
|
||||
var with_cafeteria: int = _runtime.call("_mana_reserve_demand")
|
||||
_expect(with_cafeteria == _runtime.mana_network_service.get_total_consumer_demand(), "owned facility demand counts")
|
||||
_runtime.facility_service.set_operational_status(cafeteria.instance_id, {&"business_open": false}, _clock.absolute_minute)
|
||||
_expect(_runtime.call("_mana_reserve_demand") == with_cafeteria, "closed facility still pays upkeep demand")
|
||||
_runtime.facility_service.set_operational_status(cafeteria.instance_id, {&"suspension_reason": &"DESTROYED"}, _clock.absolute_minute)
|
||||
_expect(_runtime.call("_mana_reserve_demand") == with_cafeteria - 2, "destroyed facility demand is excluded")
|
||||
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
||||
_expect(int(_runtime.mana_reserve.get_last_flow()[&"demand"]) == with_cafeteria - 2, "boundary uses demand without destroyed facility")
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cewrt8dc6d4hi
|
||||
@@ -328,10 +328,17 @@ func _test_exhausted_generated_ids_fail_atomically() -> void:
|
||||
_expect(_runtime.id_generator.restore_snapshot(allocator), "valid near-exhausted allocator is a disclosed saved-state boundary")
|
||||
var before: Dictionary = _runtime.defense_service.create_snapshot()
|
||||
var rng_before: Dictionary = _runtime.get_rng_stream(&"defense").snapshot()
|
||||
var mana_before: int = _state.get_resource(&"mana")
|
||||
var reserve_minute_before: int = _runtime.mana_reserve.get_minute()
|
||||
_advance(5)
|
||||
# A06: crossing a 10-minute boundary settles the finite reserve; that flow is
|
||||
# the only Mana change allowed here.
|
||||
var reserve_mana := mana_before
|
||||
if _runtime.mana_reserve.get_minute() != reserve_minute_before:
|
||||
reserve_mana = int(_runtime.mana_reserve.get_last_flow()[&"after"])
|
||||
before[&"current_minute"] = _clock.absolute_minute
|
||||
_expect(before == _runtime.defense_service.create_snapshot() and rng_before == _runtime.get_rng_stream(&"defense").snapshot() and _clock.paused, "exhausted automatic allocator fails without advancing combat or RNG")
|
||||
_expect(_state.get_resource(&"gold") == 5_000 and _state.get_resource(&"mana") == 100, "allocation failure charges no skill or outcome resources")
|
||||
_expect(_state.get_resource(&"gold") == 5_000 and _state.get_resource(&"mana") == reserve_mana, "allocation failure charges no skill or outcome resources (Mana moves only by the A06 reserve flow)")
|
||||
_expect(_count_domain_events(&"DEFENSE_AUTOMATIC_ROUND_FAILED", &"raid.id.exhausted") == 1 and _runtime.id_generator.get_counter(&"defense_round_resolution") == StableIdGenerator.MAX_COUNTER - 1, "exhaustion records one diagnostic and rolls skipped-ID allocation back")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://b0c3ouuq42ycg
|
||||
@@ -0,0 +1 @@
|
||||
uid://dftsky13pe0uj
|
||||
@@ -0,0 +1 @@
|
||||
uid://4r40f1kpjmvj
|
||||
@@ -0,0 +1 @@
|
||||
uid://cnnon5woufurp
|
||||
@@ -0,0 +1 @@
|
||||
uid://4n5welqfhdqg
|
||||
@@ -0,0 +1 @@
|
||||
uid://8uytmpjup8q4
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
var runtime: Variant
|
||||
@@ -50,7 +51,7 @@ func run() -> void:
|
||||
expect(patient.current_cell == medic.current_cell and patient.health == 0 and runtime.elevators.get_report()[&"trips"].is_empty(), "emergency unloads both at the same floor without healing")
|
||||
expect(runtime.restore_snapshot(runtime.create_snapshot()), "emergency pair release remains saveable")
|
||||
expect(runtime.restore_snapshot(checkpoint), "restore boarded rescue pair for tower-wide depletion")
|
||||
state.set_resource(&"mana", 0)
|
||||
ManaOutage.hold(runtime, state)
|
||||
clock.advance_minutes(1)
|
||||
patient = runtime.staff_service.get_staff(&"patient"); medic = runtime.staff_service.get_staff(&"medic")
|
||||
expect(patient.current_cell == medic.current_cell and patient.health == 0 and runtime.elevators.get_report()[&"trips"].is_empty(), "reserve depletion lands both rescue passengers together without healing")
|
||||
@@ -60,7 +61,7 @@ func run() -> void:
|
||||
expect(runtime.staff_service.get_staff(&"patient").health == 0 and runtime.staff_service.get_staff(&"patient").current_cell == landing, "stopped clinic cannot heal or teleport the landed patient")
|
||||
var outage: Dictionary = runtime.create_snapshot()
|
||||
expect(runtime.staff_service.validate_invariants()[&"ok"] and runtime.restore_snapshot(outage) and runtime.create_snapshot() == outage, "depleted rescue ownership survives exact complete save restoration")
|
||||
state.set_resource(&"mana", 100)
|
||||
ManaOutage.release(runtime, state)
|
||||
clock.advance_minutes(100)
|
||||
expect(runtime.staff_service.get_staff(&"patient").health == 100, "restored power resumes rescue and treatment for the same patient")
|
||||
expect(runtime.staff_service.validate_invariants()[&"ok"] and runtime.restore_snapshot(runtime.create_snapshot()), "recovered rescue has no orphan ownership and remains saveable")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
var runtime: Variant
|
||||
@@ -40,11 +41,11 @@ func run() -> void:
|
||||
expect(runtime.create_snapshot()==arrived,"staff movement and recovery replay exactly")
|
||||
worker=runtime.staff_service.get_staff(&"dorm_sleeper")
|
||||
var health:=worker.health
|
||||
state.set_resource(&"mana",0);clock.advance_minutes(30)
|
||||
ManaOutage.hold(runtime, state);clock.advance_minutes(30)
|
||||
expect(worker.health==health and not runtime.get_staff_rest_report(worker.staff_id).get(&"resting",false),"unpowered dormitory stops recovery")
|
||||
expect(runtime.restore_snapshot(runtime.create_snapshot()),"stopped dormitory and staff position restore")
|
||||
worker=runtime.staff_service.get_staff(&"dorm_sleeper")
|
||||
state.set_resource(&"mana",100);clock.advance_minutes(40)
|
||||
ManaOutage.release(runtime, state);clock.advance_minutes(40)
|
||||
expect(worker.health>health,"power recovery resumes rest")
|
||||
runtime.facility_service.set_operational_status(room_id,{&"operator_open":false},clock.absolute_minute)
|
||||
health=worker.health;clock.advance_minutes(30)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
|
||||
## Real construction, hired porter and purchased stock; failure injection is
|
||||
## limited to route closures, capacity pressure and expired cargo.
|
||||
@@ -363,12 +364,12 @@ func _finish() -> void:
|
||||
|
||||
func _test_reserve_outage() -> void:
|
||||
_expect(_runtime.restore_snapshot(_loaded), "restore physically loaded cargo for reserve outage")
|
||||
_state.set_resource(&"mana", 0)
|
||||
ManaOutage.hold(_runtime, _state)
|
||||
_clock.advance_minutes(30)
|
||||
_expect(_request().state == LogisticsRequest.State.DELIVERING and _quantity(_cargo()) == 8 and _quantity(_inventory(_destination)) == 0, "outage retains loaded cargo without delivering into stopped facility")
|
||||
_expect(_crosslinks(), "outage preserves Request Task inventory ownership")
|
||||
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "cargo waiting through outage restores")
|
||||
_state.set_resource(&"mana", 100)
|
||||
ManaOutage.release(_runtime, _state)
|
||||
_wait_for(LogisticsRequest.State.COMPLETED, 60)
|
||||
_expect(_request().state == LogisticsRequest.State.COMPLETED and _quantity(_cargo()) == 0 and _quantity(_inventory(_destination)) == 8, "recovered power delivers cargo exactly once")
|
||||
_expect(_crosslinks(), "recovered delivery ownership reconciles")
|
||||
@@ -389,7 +390,7 @@ func _test_kitchen_reserve_outage() -> void:
|
||||
if before[&"active_batch"].is_empty():
|
||||
_runtime.restore_snapshot(_loaded)
|
||||
return
|
||||
_state.set_resource(&"mana", 0)
|
||||
ManaOutage.hold(_runtime, _state)
|
||||
_clock.advance_minutes(30)
|
||||
var stopped: Dictionary = _runtime.kitchen_production.get_report(_destination)
|
||||
_expect(stopped[&"state"] == &"NO_POWER" and stopped[&"active_batch"] == before[&"active_batch"], "thirty stopped minutes preserve batch identity reservations and progress")
|
||||
@@ -397,7 +398,7 @@ func _test_kitchen_reserve_outage() -> void:
|
||||
_expect(_runtime.kitchen_production.validate_crosslinks(), "stopped kitchen reservations remain owned")
|
||||
var saved: Dictionary = _runtime.create_snapshot()
|
||||
_expect(_runtime.restore_snapshot(saved) and _runtime.create_snapshot() == saved, "active cooking outage round trips through complete Runtime save")
|
||||
_state.set_resource(&"mana", 100)
|
||||
ManaOutage.release(_runtime, _state)
|
||||
_clock.advance_minutes(30)
|
||||
_expect(_quantity(_inventory(_destination)) == 4 and _runtime.inventory_service.total_quantity(_inventory(_destination), SupplyCatalog.BASIC_MEAL) == 4, "recovery consumes exactly four raw food and produces exactly four meals")
|
||||
_expect(_runtime.kitchen_production.get_report(_destination)[&"state"] == &"TARGET_REACHED" and _runtime.kitchen_production.validate_crosslinks(), "recovered batch releases reservations and stops at target")
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
extends SceneTree
|
||||
|
||||
## P14-03 route choice with real waiting. Two guest shafts with real guests at
|
||||
## real cells; a registered worker physically walks the chosen route. Covers a
|
||||
## whole-shaft rush detour, shared cabins within capacity with every rider's
|
||||
## arrival cell, cancellation, route loss on demolition, service-elevator and
|
||||
## stair congestion pricing, warp-exit congestion, legacy v1 save migration and
|
||||
## v2 tamper rejection. "-- writer"/"-- reader" add a new-PID rush save check.
|
||||
const START := Vector3i(1, 0, 0)
|
||||
const GOAL := Vector3i(2, 1, 0)
|
||||
const LEGACY_FIXTURE := "res://tests/fixtures/p14-03/legacy_rush_v1"
|
||||
const LEGACY_COPY := "user://tests/p14_03_legacy_rush_copy"
|
||||
const PID_SAVE := "user://saves/p14_03_route_new_pid"
|
||||
const PID_EXPECTED := "user://saves/p14_03_route_new_pid_expected.json"
|
||||
const PID_CONTINUE := 60
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
var _serial := 0
|
||||
var _clock: Variant
|
||||
var _state: Variant
|
||||
var _runtime: Variant
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var args := OS.get_cmdline_user_args()
|
||||
var mode := "reader" if "reader" in args else ("writer" if "writer" in args else "full")
|
||||
print("Route wait choice (P14-03) %s pid %d" % [mode, OS.get_process_id()])
|
||||
_new_runtime()
|
||||
if mode == "reader":
|
||||
_read_new_pid()
|
||||
else:
|
||||
_layout()
|
||||
if mode == "writer":
|
||||
_write_new_pid()
|
||||
else:
|
||||
_test_shaft_rush()
|
||||
_test_worker_walks_detour()
|
||||
_test_shared_cabin()
|
||||
_test_route_loss()
|
||||
_test_service_elevator_wait()
|
||||
_test_stair_congestion()
|
||||
_test_warp_exit_congestion()
|
||||
_test_legacy_v1_migration()
|
||||
_free_runtime()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _new_runtime() -> void:
|
||||
_clock = load("res://autoload/game_clock.gd").new()
|
||||
_state = load("res://autoload/game_state.gd").new()
|
||||
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
||||
_clock.reset(1, 6, 0)
|
||||
_state.reset()
|
||||
_runtime.initialize(_clock, _state, 1403)
|
||||
_clock.set_paused(true)
|
||||
|
||||
|
||||
func _free_runtime() -> void:
|
||||
_runtime.free(); _clock.free(); _state.free()
|
||||
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary) -> CommandResult:
|
||||
_serial += 1
|
||||
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("p1403.%d" % _serial))
|
||||
|
||||
|
||||
func _build(id: StringName, origin: Vector3i) -> void:
|
||||
var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin})
|
||||
_expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message])
|
||||
|
||||
|
||||
func _next_evaluation() -> void:
|
||||
_clock.advance_minutes(10 - int(_clock.absolute_minute) % 10 + 1)
|
||||
|
||||
|
||||
func _layout() -> void:
|
||||
_expect(_cmd(&"change_resources", {&"deltas": {&"gold": 40000, &"materials": 400}}).is_success(), "fund the fixture")
|
||||
# FRONT: warps x-4, shaft A x0 (floors 0-3), shaft B x4 (floors 0-1), floor-1 link x2-3.
|
||||
# BACK: service shaft x6, security station x-2 for the worker's shift.
|
||||
_build(&"guest_elevator", Vector3i(0, 0, 0))
|
||||
_build(&"guest_elevator", Vector3i(4, 0, 0))
|
||||
_build(&"mana_warp", Vector3i(-4, 0, 0))
|
||||
_build(&"service_elevator", Vector3i(6, 0, 1))
|
||||
_build(&"security_station", Vector3i(-2, 0, 1))
|
||||
_clock.advance_minutes(360)
|
||||
_build(&"guest_elevator", Vector3i(0, 2, 0))
|
||||
_build(&"mana_warp", Vector3i(-4, 2, 0))
|
||||
for x: int in [2, 3]:
|
||||
_build(&"guest_corridor", Vector3i(x, 1, 0))
|
||||
_clock.advance_minutes(360)
|
||||
_next_evaluation()
|
||||
_expect(_runtime.elevators.get_report()[&"shafts"].size() == 3, "two guest shafts and one service shaft")
|
||||
|
||||
|
||||
func _spawn(guest_id: StringName, cell: Vector3i) -> bool:
|
||||
var result := _cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"options": {&"current_cell": cell}})
|
||||
_expect(result.is_success(), "spawn real guest %s at %s %s" % [guest_id, cell, result.message])
|
||||
return result.is_success()
|
||||
|
||||
|
||||
func _route() -> Array:
|
||||
return _runtime.path_service.find_path(START, GOAL)
|
||||
|
||||
|
||||
func _uses_shaft_a(path: Array) -> bool:
|
||||
return path.size() > 1 and path[1] == Vector3i(0, 0, 0)
|
||||
|
||||
|
||||
func _queue_rush(count: int) -> Array[StringName]:
|
||||
var riders: Array[StringName] = []
|
||||
for i: int in count:
|
||||
var guest_id := StringName("rush_%02d" % i)
|
||||
if _spawn(guest_id, Vector3i(0, 0, 0)):
|
||||
var actor_id := StringName("guest:%s" % guest_id)
|
||||
var trip: Dictionary = _runtime.elevators.request_trip(actor_id, Vector3i(0, 0, 0), Vector3i(0, 3, 0), _clock.absolute_minute)
|
||||
_expect(bool(trip.get(&"ok", false)), "real guest %s queues for floor 3" % guest_id)
|
||||
riders.append(actor_id)
|
||||
return riders
|
||||
|
||||
|
||||
func _shaft_a_link() -> StringName:
|
||||
return _runtime.path_service.get_step_connector(Vector3i(0, 0, 0), Vector3i(0, 1, 0), [])
|
||||
|
||||
|
||||
func _test_shaft_rush() -> void:
|
||||
var idle := _route()
|
||||
var idle_cost: int = _runtime.path_service.last_path_cost
|
||||
_expect(_uses_shaft_a(idle), "idle tower routes through the nearer shaft A")
|
||||
var riders := _queue_rush(12)
|
||||
var rush := _route()
|
||||
_expect(not _uses_shaft_a(rush) and rush.has(Vector3i(4, 0, 0)), "whole-shaft rush diverts the 0->1 trip to idle shaft B")
|
||||
_expect(_runtime.path_service.last_path_cost > idle_cost, "diverted route is dearer than the idle one")
|
||||
_expect(int(_runtime.path_service.get_connector(_shaft_a_link())[&"dynamic_cost"]) == 12 * PathService.ORTHOGONAL_MOVE_COST, "shaft A 0->1 prices all 12 riders waiting elsewhere in the shaft")
|
||||
var saved: Dictionary = _runtime.create_snapshot()
|
||||
_expect(int(saved[&"systems"][&"runtime"][&"elevators"][&"version"]) == ElevatorService.SNAPSHOT_VERSION, "new saves carry elevator snapshot v2")
|
||||
_expect(_runtime.restore_snapshot(saved.duplicate(true)) and _runtime.create_snapshot() == saved, "rush wait costs and queues survive save restore exactly")
|
||||
for bad: int in [0, 300]:
|
||||
var tampered: Dictionary = saved.duplicate(true)
|
||||
for row: Dictionary in tampered[&"systems"][&"runtime"][&"path"][&"connectors"]:
|
||||
if row[&"id"] == _shaft_a_link(): row[&"dynamic_cost"] = bad
|
||||
var live: Dictionary = _runtime.create_snapshot()
|
||||
_expect(not _runtime.restore_snapshot(tampered) and _runtime.create_snapshot() == live, "v2 save with shaft wait %d (disagreeing with its queue) is rejected atomically" % bad)
|
||||
var a_route_during_rush: int = _runtime.path_service.calculate_path_cost(idle)
|
||||
for actor_id: StringName in riders:
|
||||
_runtime.elevators.cancel_actor(actor_id)
|
||||
_expect(int(_runtime.path_service.get_connector(_shaft_a_link())[&"dynamic_cost"]) == 0, "cancelled rush clears the shaft wait")
|
||||
_expect(_runtime.path_service.calculate_path_cost(idle) == a_route_during_rush - 12 * PathService.ORTHOGONAL_MOVE_COST, "cleared shaft wait removes exactly the queued riders' cost from shaft A's route")
|
||||
_expect(_runtime.restore_snapshot(saved), "rewind to the rush state")
|
||||
|
||||
|
||||
func _register_walker(staff_id: StringName, cell: Vector3i) -> bool:
|
||||
var registered := _cmd(&"register_staff", {&"staff_id": staff_id, &"role_id": &"porter", &"options": {
|
||||
&"current_cell": cell, &"state": StaffRuntime.State.OFF_DUTY, &"role_tags": [&"porter"],
|
||||
&"permitted_task_tags": [&"work"], &"skills": {&"operation": 50}}})
|
||||
var station: FacilityInstance = null
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
if facility.definition_id == &"security_station": station = facility
|
||||
var minute: int = posmod(_clock.absolute_minute, 1440)
|
||||
var shift := _cmd(&"create_staff_shift", {&"staff_id": staff_id, &"template_id": &"FLEX", &"start_day": _clock.absolute_minute / 1440,
|
||||
&"start_minute_of_day": minute, &"end_minute_of_day": mini(minute + 600, 1439), &"facility_id": station.instance_id})
|
||||
_expect(registered.is_success() and shift.is_success(), "register real walker %s at %s %s %s" % [staff_id, cell, registered.message, shift.message])
|
||||
return registered.is_success() and shift.is_success()
|
||||
|
||||
|
||||
## Sends the walker to GOAL through an ordinary Staff task; returns visited cells.
|
||||
func _walk(staff_id: StringName, task_id: StringName) -> Array:
|
||||
var created := _cmd(&"create_staff_task", {&"task_id": task_id, &"task_type": &"inspect", &"priority": 90, &"options": {
|
||||
&"target_cell": GOAL, &"required_role_tags": [&"porter"], &"task_tags": [&"work"], &"estimated_minutes": 1}})
|
||||
_expect(created.is_success(), "walk task %s created %s" % [task_id, created.message])
|
||||
var visited: Array = []
|
||||
for minute: int in 180:
|
||||
_clock.advance_minutes(1)
|
||||
var worker: StaffRuntime = _runtime.staff_service.get_staff(staff_id)
|
||||
if worker == null:
|
||||
break
|
||||
if visited.is_empty() or visited.back() != worker.current_cell:
|
||||
visited.append(worker.current_cell)
|
||||
if worker.current_cell == GOAL:
|
||||
break
|
||||
return visited
|
||||
|
||||
|
||||
func _test_worker_walks_detour() -> void:
|
||||
# Still in the rush state saved above: shaft A has 12 riders queued.
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
if not _register_walker(&"walker_rush", START):
|
||||
return
|
||||
var visited := _walk(&"walker_rush", &"walk_rush")
|
||||
_expect(not visited.is_empty() and visited.back() == GOAL, "real walker reaches the goal during the rush")
|
||||
_expect(visited.has(Vector3i(4, 0, 0)) and visited.has(Vector3i(4, 1, 0)), "walker physically rides shaft B (%s)" % [visited])
|
||||
_expect(not visited.has(Vector3i(0, 1, 0)), "walker never rides the congested shaft A")
|
||||
_expect(_runtime.restore_snapshot(before), "rewind walker drill")
|
||||
|
||||
|
||||
func _shaft_id_at(x: int, layer: int) -> StringName:
|
||||
for shaft: Dictionary in _runtime.elevators.get_report()[&"shafts"]:
|
||||
var bottom: Vector3i = (shaft[&"stops"] as Array).front()
|
||||
if bottom.x == x and bottom.z == layer:
|
||||
return shaft[&"shaft_id"]
|
||||
return &""
|
||||
|
||||
|
||||
func _test_shared_cabin() -> void:
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
|
||||
_runtime.elevators.cancel_actor(StringName(trip[&"actor_id"]))
|
||||
var riders: Array[StringName] = []
|
||||
for i: int in 6:
|
||||
var guest_id := StringName("share_%02d" % i)
|
||||
if not _spawn(guest_id, Vector3i(4, 0, 0)):
|
||||
continue
|
||||
# Real guest with a floor-1 destination; the Runtime's own path travel
|
||||
# requests the elevator ride (no test-side trip injection).
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(guest_id)
|
||||
guest.path_handle = {&"target_cell": Vector3i(4, 1, 0)}
|
||||
_expect(bool(_runtime.guest_service.transition_guest(guest_id, GuestRuntime.State.ENTERING, _clock.absolute_minute, &"TEST_ENTRY").get(&"ok", false))
|
||||
and bool(_runtime.guest_service.transition_guest(guest_id, GuestRuntime.State.MOVING, _clock.absolute_minute, &"TEST_DESTINATION").get(&"ok", false)), "rider %s heads for floor 1" % guest_id)
|
||||
riders.append(StringName("guest:%s" % guest_id))
|
||||
var requested := {}
|
||||
var leaver: StringName = &""
|
||||
var shaft_b := _shaft_id_at(4, 0)
|
||||
var max_boarded := 0
|
||||
var arrival_cell := {}
|
||||
for minute: int in 60:
|
||||
_clock.advance_minutes(1)
|
||||
var boarded := 0
|
||||
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
|
||||
if trip[&"shaft_id"] == shaft_b and trip[&"state"] == PathService.ELEVATOR_REQUEST_BOARDED:
|
||||
boarded += 1
|
||||
if leaver.is_empty():
|
||||
# A boarded guest told to leave (patience/closing) must ride on.
|
||||
leaver = StringName(trip[&"actor_id"])
|
||||
_expect(bool(_runtime.guest_service.begin_leaving(StringName(String(leaver).trim_prefix("guest:")), _clock.absolute_minute, &"TEST_LEAVE_WHILE_ABOARD").get(&"ok", false)), "boarded rider %s starts leaving mid-ride" % leaver)
|
||||
max_boarded = maxi(max_boarded, boarded)
|
||||
for actor_id: StringName in riders:
|
||||
if not _runtime.elevators.get_actor_trip(actor_id).is_empty():
|
||||
requested[actor_id] = true
|
||||
continue
|
||||
if arrival_cell.has(actor_id) or not requested.has(actor_id):
|
||||
continue
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(StringName(String(actor_id).trim_prefix("guest:")))
|
||||
arrival_cell[actor_id] = guest.current_cell if guest != null else null
|
||||
_expect(max_boarded > 1 and max_boarded <= 4, "riders share the cabin up to its capacity of 4 (max %d)" % max_boarded)
|
||||
_expect(requested.size() == riders.size(), "the Runtime requested a ride for every rider")
|
||||
var arrived_at := -1
|
||||
var despawned_at := -1
|
||||
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
|
||||
if not (leaver in event.entity_ids or StringName(String(leaver).trim_prefix("guest:")) in event.entity_ids):
|
||||
continue
|
||||
if event.event_type == &"ELEVATOR_ARRIVED" and event.payload.get(&"cell") == Vector3i(4, 1, 0):
|
||||
arrived_at = event.absolute_minute
|
||||
if event.event_type == &"GUEST_DESPAWNED":
|
||||
despawned_at = event.absolute_minute
|
||||
_expect(not leaver.is_empty() and arrived_at >= 0 and (despawned_at < 0 or despawned_at >= arrived_at), "a rider leaving mid-ride rides on and alights at the floor-1 landing before it can leave (arrived %d, left %d)" % [arrived_at, despawned_at])
|
||||
_expect(arrival_cell.size() == riders.size(), "every rider's trip ends")
|
||||
for actor_id: StringName in riders:
|
||||
if actor_id == leaver:
|
||||
continue
|
||||
_expect(arrival_cell.get(actor_id) == Vector3i(4, 1, 0), "%s exists and stands on the floor-1 landing when the ride ends (%s)" % [actor_id, arrival_cell.get(actor_id)])
|
||||
_expect(_runtime.restore_snapshot(before), "rewind shared-cabin drill")
|
||||
|
||||
|
||||
func _test_route_loss() -> void:
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
|
||||
_runtime.elevators.cancel_actor(StringName(trip[&"actor_id"]))
|
||||
var shaft_b_facility: StringName = &""
|
||||
for facility: Dictionary in _runtime.construction_service.get_facilities():
|
||||
if facility[&"definition_id"] == &"guest_elevator" and facility[&"origin"] == Vector3i(4, 0, 0):
|
||||
shaft_b_facility = facility[&"instance_id"]
|
||||
_expect(_cmd(&"demolish_facility", {&"instance_id": shaft_b_facility}).is_success(), "demolish shaft B")
|
||||
_expect(_uses_shaft_a(_route()), "route loss falls back to the remaining shaft")
|
||||
if _register_walker(&"walker_loss", START):
|
||||
var visited := _walk(&"walker_loss", &"walk_loss")
|
||||
_expect(not visited.is_empty() and visited.back() == GOAL and visited.has(Vector3i(0, 1, 0)), "real walker reaches the goal through shaft A after the loss (%s)" % [visited])
|
||||
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "post-loss state restores")
|
||||
_expect(_runtime.restore_snapshot(before), "rewind route-loss drill")
|
||||
|
||||
|
||||
func _test_service_elevator_wait() -> void:
|
||||
var ps: PathService = _runtime.path_service
|
||||
var lower := Vector3i(6, 0, 1)
|
||||
var upper := Vector3i(6, 1, 1)
|
||||
var staff_only: Array[StringName] = [&"staff"]
|
||||
var layers: Array[int] = [WorldService.LAYER_FRONT, WorldService.LAYER_BACK]
|
||||
_expect(ps.find_path(lower, upper).is_empty() and not ps.find_path(lower, upper, layers, staff_only).is_empty(), "service shaft is staff-only")
|
||||
var idle_cost: int = ps.last_path_cost
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
for i: int in 4:
|
||||
var staff_id := StringName("svc_rider_%d" % i)
|
||||
_expect(_cmd(&"register_staff", {&"staff_id": staff_id, &"role_id": &"porter", &"options": {&"current_cell": lower, &"state": StaffRuntime.State.OFF_DUTY, &"role_tags": [&"porter"]}}).is_success(), "register real worker %s on the service landing" % staff_id)
|
||||
_expect(bool(_runtime.elevators.request_trip(StringName("staff:%s" % staff_id), lower, upper, _clock.absolute_minute, staff_only).get(&"ok", false)), "real worker %s queues on the service shaft" % staff_id)
|
||||
ps.find_path(lower, upper, layers, staff_only)
|
||||
_expect(ps.last_path_cost > idle_cost, "queued workers raise the service elevator's A* cost")
|
||||
_expect(_runtime.restore_snapshot(before), "rewind service drill")
|
||||
|
||||
|
||||
func _test_stair_congestion() -> void:
|
||||
var ps: PathService = _runtime.path_service
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
_expect(_cmd(&"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(-1, 1, 0)}).is_success(), "build a floor-1 corridor beside shaft A")
|
||||
_expect(_cmd(&"build_facility", {&"definition_id": &"guest_stairs", &"origin": Vector3i(-2, 1, 0)}).is_success(), "build stairs to floor 1")
|
||||
_clock.advance_minutes(20)
|
||||
var landing := Vector3i(-2, 1, 0)
|
||||
var foot := Vector3i(-2, 0, 0)
|
||||
var quiet: Array = ps.find_path(foot, landing)
|
||||
var quiet_cost: int = ps.last_path_cost
|
||||
_expect(quiet == [foot, landing], "stairs connect the two floors")
|
||||
for i: int in 6:
|
||||
_spawn(StringName("stair_crowd_%d" % i), landing)
|
||||
ps.find_path(foot, landing)
|
||||
_expect(ps.last_path_cost > quiet_cost, "a crowded stair landing raises the stair route's A* cost")
|
||||
_expect(_runtime.restore_snapshot(before), "rewind stair drill")
|
||||
|
||||
|
||||
func _test_warp_exit_congestion() -> void:
|
||||
var ps: PathService = _runtime.path_service
|
||||
var lower := Vector3i(-4, 0, 0)
|
||||
var upper := Vector3i(-4, 2, 0)
|
||||
var quiet: Array = ps.find_path(lower, upper)
|
||||
var quiet_cost: int = ps.last_path_cost
|
||||
_expect(quiet == [lower, upper], "idle warp is the cheapest way up")
|
||||
var crowd := 0
|
||||
for i: int in 6:
|
||||
if _spawn(StringName("crowd_%02d" % i), upper):
|
||||
crowd += 1
|
||||
ps.find_path(lower, upper)
|
||||
_expect(crowd > 0 and ps.last_path_cost > quiet_cost, "a crowded warp exit raises the warp route's A* cost")
|
||||
|
||||
|
||||
## A genuine save written by 50ceca0 (pre-P14-03, elevator snapshot v1) with 12
|
||||
## riders queued for floor 3 and every elevator dynamic_cost 0.
|
||||
func _test_legacy_v1_migration() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(LEGACY_COPY))
|
||||
for member: String in ["world.bin", "manifest.json", "thumbnail.png", "journal.log"]:
|
||||
var bytes := FileAccess.get_file_as_bytes(LEGACY_FIXTURE.path_join(member))
|
||||
var file := FileAccess.open(LEGACY_COPY.path_join(member), FileAccess.WRITE)
|
||||
file.store_buffer(bytes)
|
||||
file.close()
|
||||
var loaded := SaveService.load_snapshot(LEGACY_COPY)
|
||||
_expect(loaded[&"ok"], "legacy v1 package loads from disk")
|
||||
if not loaded[&"ok"]:
|
||||
return
|
||||
var legacy: Dictionary = loaded[&"snapshot"]
|
||||
_expect(int(legacy[&"systems"][&"runtime"][&"elevators"][&"version"]) == 1, "fixture really is elevator snapshot v1")
|
||||
var live: Dictionary = _runtime.create_snapshot()
|
||||
var bad: Dictionary = legacy.duplicate(true)
|
||||
for row: Dictionary in bad[&"systems"][&"runtime"][&"path"][&"connectors"]:
|
||||
if String(row[&"id"]).begins_with("transport.") and int(row[&"dynamic_cost"]) == 0:
|
||||
row[&"dynamic_cost"] = 500
|
||||
break
|
||||
_expect(not _runtime.restore_snapshot(bad) and _runtime.create_snapshot() == live, "v1 save with a nonzero wait (never written by v1) is rejected atomically")
|
||||
_expect(_runtime.restore_snapshot(legacy.duplicate(true)), "legacy v1 rush save migrates")
|
||||
_expect(int(_runtime.path_service.get_connector(_shaft_a_link())[&"dynamic_cost"]) == 12 * PathService.ORTHOGONAL_MOVE_COST, "migration derives the shaft-wide wait from the legacy queue")
|
||||
_expect(not _uses_shaft_a(_route()), "migrated rush diverts like a fresh one")
|
||||
var migrated: Dictionary = _runtime.create_snapshot()
|
||||
_expect(int(migrated[&"systems"][&"runtime"][&"elevators"][&"version"]) == ElevatorService.SNAPSHOT_VERSION and _runtime.restore_snapshot(migrated) and _runtime.create_snapshot() == migrated, "migrated state saves as v2 and round-trips exactly")
|
||||
SaveService.delete_save(LEGACY_COPY)
|
||||
|
||||
|
||||
func _write_new_pid() -> void:
|
||||
_queue_rush(12)
|
||||
_register_walker(&"walker_pid", START)
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
var serial_at_save := _serial
|
||||
_expect(SaveService.save_snapshot(PID_SAVE, snapshot)[&"ok"], "writer saves the rush state")
|
||||
var visited := _walk(&"walker_pid", &"walk_pid")
|
||||
_clock.advance_minutes(maxi(PID_CONTINUE - visited.size(), 0))
|
||||
var file := FileAccess.open(PID_EXPECTED, FileAccess.WRITE)
|
||||
file.store_string(JSON.stringify({"writer_pid": OS.get_process_id(), "serial_at_save": serial_at_save, "visited": var_to_str(visited),
|
||||
"minute": _clock.absolute_minute, "digest": SimulationDigest.compute(_runtime.create_snapshot())}))
|
||||
file.close()
|
||||
_expect(not visited.is_empty() and visited.back() == GOAL and visited.has(Vector3i(4, 1, 0)), "writer walker reaches the goal through shaft B")
|
||||
|
||||
|
||||
func _read_new_pid() -> void:
|
||||
var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(PID_EXPECTED))
|
||||
_expect(expected is Dictionary, "reader finds writer record")
|
||||
if not expected is Dictionary:
|
||||
return
|
||||
_expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process")
|
||||
var loaded := SaveService.load_snapshot(PID_SAVE)
|
||||
_expect(loaded[&"ok"] and _runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores the rush save")
|
||||
_expect(int(_runtime.path_service.get_connector(_shaft_a_link())[&"dynamic_cost"]) == 12 * PathService.ORTHOGONAL_MOVE_COST, "restored shaft wait matches the queue")
|
||||
# Same player command sequence as the writer after its save.
|
||||
_serial = int(expected["serial_at_save"])
|
||||
var visited := _walk(&"walker_pid", &"walk_pid")
|
||||
_clock.advance_minutes(maxi(PID_CONTINUE - visited.size(), 0))
|
||||
_expect(var_to_str(visited) == String(expected["visited"]), "walker takes the same physical route after restart")
|
||||
_expect(_clock.absolute_minute == int(expected["minute"]) and SimulationDigest.compute(_runtime.create_snapshot()) == String(expected["digest"]), "whole continued state digest matches the writer")
|
||||
SaveService.delete_save(PID_SAVE)
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(PID_EXPECTED))
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://btfqo5k3jf8p2
|
||||
@@ -16,7 +16,7 @@ func _run() -> void:
|
||||
_state = load("res://autoload/game_state.gd").new()
|
||||
_reset()
|
||||
_expect(_runtime.path_service.get_vertical_walk_requires_connector(), "new Runtime requires explicit vertical transport")
|
||||
_expect(_runtime.definition_registry.facility_count() == 23, "Runtime registers baseline16, stairs, infirmary and premium room/dormitory and chapel")
|
||||
_expect(_runtime.definition_registry.facility_count() == 25, "Runtime registers baseline16, stairs, infirmary, premium room/dormitory, chapel and A07/A08 warps")
|
||||
var before: Dictionary = _runtime.create_snapshot()
|
||||
var invalid := _command(&"build_facility", {&"definition_id": &"guest_stairs", &"origin": Vector3i(4, 3, 0)})
|
||||
_expect(not invalid.is_success() and invalid.details.get(&"reason") == &"STAIR_LANDING_MISSING", "stairs without a lower landing reject before any construction")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends SceneTree
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
|
||||
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
|
||||
var _finishing := false
|
||||
@@ -128,7 +129,7 @@ func _verify_mid_ride() -> bool:
|
||||
_expect(_runtime.restore_snapshot(snapshot), "rewind depletion drill to original passenger state")
|
||||
for cell: Vector3i in shaft[&"stops"]:
|
||||
_runtime.path_service.configure_cell_traffic(cell, 4, 0, true)
|
||||
_state.set_resource(&"mana", 0)
|
||||
ManaOutage.hold(_runtime, _state)
|
||||
_clock.advance_minutes(1)
|
||||
for actor_id: StringName in riders:
|
||||
_expect(_runtime.elevators.get_actor_trip(actor_id).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "no safe landing retains boarded passenger instead of teleporting")
|
||||
@@ -140,7 +141,7 @@ func _verify_mid_ride() -> bool:
|
||||
_expect(_runtime.restore_snapshot(held_snapshot) and _runtime.create_snapshot() == held_snapshot, "no-landing outage preserves full Runtime save and passenger ownership")
|
||||
for cell: Vector3i in shaft[&"stops"]:
|
||||
_runtime.path_service.configure_cell_traffic(cell)
|
||||
_state.set_resource(&"mana", 100)
|
||||
ManaOutage.release(_runtime, _state)
|
||||
_clock.advance_minutes(45)
|
||||
for actor_id: StringName in riders:
|
||||
_expect(_runtime.elevators.get_actor_trip(actor_id).is_empty() and _runtime.path_service.get_active_elevator_request(actor_id).is_empty(), "landing repair and power restoration release held trip ownership")
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
extends SceneTree
|
||||
|
||||
## A07 Mana warp through ordinary construction, natural guests and the reserve.
|
||||
## "-- writer" (default) runs the mechanics and saves mid-operation; "-- reader"
|
||||
## restores that save in a new process and must reproduce the writer exactly.
|
||||
const Warp = preload("res://scripts/transport/warp_service.gd")
|
||||
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
||||
const SAVE_PATH := "user://saves/p14_02_warp_new_pid"
|
||||
const EXPECTED_PATH := "user://saves/p14_02_warp_new_pid_expected.json"
|
||||
const CONTINUE_MINUTES := 120
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
var _serial := 0
|
||||
var _clock: Variant
|
||||
var _state: Variant
|
||||
var _runtime: Variant
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var mode := "reader" if "reader" in OS.get_cmdline_user_args() else "writer"
|
||||
print("Warp runtime %s (pid %d)" % [mode, OS.get_process_id()])
|
||||
_clock = load("res://autoload/game_clock.gd").new()
|
||||
_state = load("res://autoload/game_state.gd").new()
|
||||
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
||||
_clock.reset(1, 6, 0)
|
||||
_state.reset()
|
||||
_runtime.initialize(_clock, _state, 8808)
|
||||
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_clock.set_paused(true)
|
||||
_expect(bool(_runtime.enable_production_operations().get(&"ok", false)), "natural operations start at Day 1 06:00")
|
||||
if mode == "writer":
|
||||
_write()
|
||||
else:
|
||||
_read()
|
||||
_runtime.free(); _clock.free(); _state.free()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary) -> CommandResult:
|
||||
_serial += 1
|
||||
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("warp.test.%d" % _serial))
|
||||
|
||||
|
||||
func _build(id: StringName, origin: Vector3i) -> bool:
|
||||
var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin})
|
||||
_expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message])
|
||||
return result.is_success()
|
||||
|
||||
|
||||
func _warp_connectors() -> Array[StringName]:
|
||||
return _runtime.path_service.get_connector_ids_with_prefix(Warp.PREFIX)
|
||||
|
||||
|
||||
func _warp_events() -> Array[Dictionary]:
|
||||
var rows: Array[Dictionary] = []
|
||||
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
|
||||
if event.event_type == &"WARP_TRAVELED":
|
||||
rows.append({&"minute": event.absolute_minute, &"mana": int(event.payload[&"mana"])})
|
||||
return rows
|
||||
|
||||
|
||||
func _write() -> void:
|
||||
_expect(_cmd(&"change_resources", {&"deltas": {&"gold": 12000, &"materials": 100}}).is_success(), "fund four warps for the fixture")
|
||||
var gold_before: int = _state.get_resource(&"gold")
|
||||
if not _build(&"mana_warp", Vector3i(0, 0, 0)) or not _build(&"reception", Vector3i(2, 0, 0)):
|
||||
return
|
||||
_expect(gold_before - int(_state.get_resource(&"gold")) >= 3000, "warp construction charges its approved 3000G")
|
||||
_clock.advance_minutes(359)
|
||||
_expect(_warp_connectors().is_empty(), "a single warp has no destination")
|
||||
_clock.advance_minutes(1)
|
||||
if not _build(&"mana_warp", Vector3i(0, 2, 0)) or not _build(&"standard_room", Vector3i(2, 2, 0)):
|
||||
return
|
||||
_clock.advance_minutes(360)
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 2, 0)).is_empty(), "new warp waits for its first power evaluation")
|
||||
_clock.advance_minutes(10 - int(_clock.absolute_minute) % 10)
|
||||
var ids := _warp_connectors()
|
||||
_expect(ids.size() == 1, "two completed warps are linked by one connector")
|
||||
if ids.size() != 1:
|
||||
return
|
||||
var connector: Dictionary = _runtime.path_service.get_connector(ids[0])
|
||||
_expect(connector[&"from"] == Vector3i(0, 0, 0) and connector[&"to"] == Vector3i(0, 2, 0) and connector[&"operational_state"] == PathService.CONNECTOR_OPERATIONAL, "connector joins both warp origins and is operational")
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 2, 0)) == [Vector3i(0, 0, 0), Vector3i(0, 2, 0)], "path crosses floors in one instant step")
|
||||
_expect(_runtime.mana_network_service.get_total_consumer_demand() >= 2, "each warp registers 1 Mana upkeep")
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 2, 0), [WorldService.LAYER_FRONT, WorldService.LAYER_BACK], [&"staff"]).is_empty(), "staff routes never use the guest warp (A08)")
|
||||
_expect(_build(&"service_warp", Vector3i(0, 0, 1)), "build lower BACK service warp")
|
||||
_clock.advance_minutes(360)
|
||||
_expect(_build(&"service_warp", Vector3i(0, 2, 1)), "build upper BACK service warp on the completed one")
|
||||
_clock.advance_minutes(360)
|
||||
_clock.advance_minutes(10 - int(_clock.absolute_minute) % 10)
|
||||
_expect(_warp_connectors().size() == 2, "service warps link only to each other, never to guest warps")
|
||||
var staff_route: Array = _runtime.path_service.find_path(Vector3i(0, 0, 1), Vector3i(0, 2, 1), [WorldService.LAYER_FRONT, WorldService.LAYER_BACK], [&"staff"])
|
||||
_expect(staff_route == [Vector3i(0, 0, 1), Vector3i(0, 2, 1)], "staff cross floors through the BACK service warp")
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 1), Vector3i(0, 2, 1)).is_empty(), "guests cannot use the service warp")
|
||||
_expect(_runtime.call(&"_warp_step_connector", Vector3i(0, 0, 1), Vector3i(0, 2, 1), [&"staff"] as Array[StringName]) != &"", "a staff step through the service warp is fare-bearing")
|
||||
|
||||
# Outage and an unaffordable fare both remove the warp from routing.
|
||||
var before_outage: Dictionary = _runtime.create_snapshot()
|
||||
ManaOutage.hold(_runtime, _state)
|
||||
_clock.advance_minutes(1)
|
||||
_expect(_runtime.path_service.get_connector(ids[0])[&"operational_state"] == PathService.CONNECTOR_OUTAGE, "reserve outage stops the warp")
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 2, 0)).is_empty(), "stopped warp offers no route")
|
||||
_expect(_runtime.path_service.find_path_report(Vector3i(0, 0, 0), Vector3i(0, 2, 0))[&"reason"] == PathService.ROUTE_CONNECTOR_UNAVAILABLE, "guest sees the stopped warp as an unavailable connector")
|
||||
ManaOutage.release(_runtime, _state, Warp.MANA_PER_TRIP - 1)
|
||||
_clock.advance_minutes(1)
|
||||
_expect(_runtime.path_service.get_connector(ids[0])[&"operational_state"] == PathService.CONNECTOR_OUTAGE, "reserve below one fare keeps the warp closed")
|
||||
_expect(_runtime.restore_snapshot(before_outage), "rewind outage drill")
|
||||
_clock.advance_minutes(1)
|
||||
var mana_blocked: int = _state.get_resource(&"mana")
|
||||
_runtime.path_service.set_cell_temporary_block(Vector3i(0, 2, 0), true)
|
||||
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 2, 0)).is_empty()
|
||||
and _runtime.call(&"_warp_step_connector", Vector3i(0, 0, 0), Vector3i(0, 2, 0), [] as Array[StringName]) == &"", "blocked arrival offers no warp step and no fare")
|
||||
_expect(_state.get_resource(&"mana") == mana_blocked, "blocked arrival charges nothing")
|
||||
_expect(_runtime.restore_snapshot(before_outage), "rewind arrival drill")
|
||||
|
||||
# Natural guests use the warp; each crossing charges exactly 3 Mana once.
|
||||
_expect(_cmd(&"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "hire receptionist")
|
||||
var reception: FacilityInstance = null
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
if facility.definition_id == &"reception": reception = facility
|
||||
_expect(_cmd(&"set_staff_work_assignment", {&"staff_id": &"staff_akane", &"facility_id": reception.instance_id, &"template_id": &"DAY"}).is_success(), "assign receptionist")
|
||||
var saved := false
|
||||
var crossings := 0
|
||||
for minute: int in range(1200):
|
||||
var mana_before: int = _state.get_resource(&"mana")
|
||||
var events_before := _warp_events().size()
|
||||
_clock.advance_minutes(1)
|
||||
var new_events := _warp_events().size() - events_before
|
||||
if new_events > 0:
|
||||
crossings += new_events
|
||||
var boundary: bool = int(_clock.absolute_minute) % 10 == 0
|
||||
if not boundary:
|
||||
_expect(mana_before - int(_state.get_resource(&"mana")) == Warp.MANA_PER_TRIP * new_events, "warp crossing charges 3 Mana per rider")
|
||||
if not saved:
|
||||
saved = true
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
_expect(SaveService.save_snapshot(SAVE_PATH, snapshot)[&"ok"], "save right after a paid crossing")
|
||||
_clock.advance_minutes(CONTINUE_MINUTES)
|
||||
var file := FileAccess.open(EXPECTED_PATH, FileAccess.WRITE)
|
||||
file.store_string(JSON.stringify({
|
||||
"writer_pid": OS.get_process_id(),
|
||||
"saved_events": events_before + new_events,
|
||||
"mana": _state.get_resource(&"mana"),
|
||||
"digest": SimulationDigest.compute(_runtime.create_snapshot()),
|
||||
"events": JSON.stringify(_warp_events()),
|
||||
}))
|
||||
file.close()
|
||||
break
|
||||
if crossings == 0:
|
||||
for guest: GuestRuntime in _runtime.guest_service.get_guests():
|
||||
print("GUEST ", guest.guest_id, " ", guest.state, " ", guest.current_cell)
|
||||
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
||||
print("FAC ", facility.definition_id, " ", facility.state, " ", facility.active_staff_ids)
|
||||
_expect(crossings > 0, "a natural guest crosses by warp")
|
||||
_expect(saved, "mid-operation save written")
|
||||
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "warp state round trips")
|
||||
|
||||
# Demolishing one end removes the link.
|
||||
var upper: StringName = &""
|
||||
for facility: Dictionary in _runtime.construction_service.get_facilities():
|
||||
if facility[&"definition_id"] == &"mana_warp" and facility[&"origin"] == Vector3i(0, 2, 0):
|
||||
upper = facility[&"instance_id"]
|
||||
var occupied := false
|
||||
for guest: GuestRuntime in _runtime.guest_service.get_guests():
|
||||
if guest.current_cell.x <= 1 and guest.current_cell.y >= 2: occupied = true
|
||||
if not occupied:
|
||||
_expect(_cmd(&"demolish_facility", {&"instance_id": upper}).is_success(), "demolish upper warp")
|
||||
_expect(_warp_connectors().is_empty(), "demolition removes the warp link")
|
||||
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "post-demolition state restores")
|
||||
|
||||
|
||||
func _read() -> void:
|
||||
var expected: Variant = JSON.parse_string(FileAccess.get_file_as_string(EXPECTED_PATH))
|
||||
_expect(expected is Dictionary, "reader finds writer record")
|
||||
if not expected is Dictionary:
|
||||
return
|
||||
_expect(int(expected["writer_pid"]) != OS.get_process_id(), "reader runs in a different process")
|
||||
var loaded := SaveService.load_snapshot(SAVE_PATH)
|
||||
_expect(loaded[&"ok"] and _runtime.restore_snapshot(loaded[&"snapshot"]), "reader restores mid-operation save")
|
||||
_expect(_warp_events().size() == int(expected["saved_events"]), "restored crossing history is not replayed")
|
||||
_clock.advance_minutes(CONTINUE_MINUTES)
|
||||
_expect(_state.get_resource(&"mana") == int(expected["mana"]), "continued Mana matches: no double fare")
|
||||
_expect(JSON.stringify(_warp_events()) == String(expected["events"]), "continued crossings match the writer")
|
||||
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == String(expected["digest"]), "whole continued state digest matches")
|
||||
SaveService.delete_save(SAVE_PATH)
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(EXPECTED_PATH))
|
||||
|
||||
|
||||
func _expect(condition: bool, label: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print(" ✓ %s" % label)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
@@ -0,0 +1 @@
|
||||
uid://73ewrfw84byc
|
||||
@@ -64,6 +64,8 @@ func _run() -> void:
|
||||
_state.set_resource(&"mana", 0)
|
||||
_expect("停止" in mana_label.text and "0" in mana_label.text, "resource signal visibly identifies zero-reserve outage")
|
||||
_expect("残量と配電" in mana_label.tooltip_text and "休止は解除されません" in mana_label.tooltip_text, "mana HUD explains recovery without promising suspension reset")
|
||||
_expect("供給 +20" in mana_label.tooltip_text and "消費 −" in mana_label.tooltip_text and "蓄積 0/300" in mana_label.tooltip_text, "mana HUD shows A06 supply, consumption and storage")
|
||||
_expect(mana_label.text.ends_with("/300・停止"), "mana HUD shows capacity with outage")
|
||||
if OS.get_environment("THE_TREE_MANA_CAPTURE") == "1":
|
||||
await process_frame
|
||||
await RenderingServer.frame_post_draw
|
||||
@@ -129,7 +131,8 @@ func _run() -> void:
|
||||
)
|
||||
await _test_defense_notice()
|
||||
var palette := _main.find_child("BuildPalette", true, false) as GridContainer
|
||||
_expect(palette.get_child_count() == 23, "all23 facilities including the cleaning room appear in the build palette")
|
||||
_expect(palette.get_child_count() == 25, "all25 facilities including the cleaning room and both A07/A08 warps appear in the build palette")
|
||||
_expect((_main.get("build_buttons") as Dictionary).has(&"mana_warp") and (_main.get("build_buttons") as Dictionary).has(&"service_warp"), "guest and service Mana warps are buildable from the ordinary palette")
|
||||
var iconized_build_buttons := 0
|
||||
for build_button_value: Variant in (_main.get("build_buttons") as Dictionary).values():
|
||||
if (build_button_value as Button).icon != null:
|
||||
|
||||
@@ -8,9 +8,9 @@ class PalettePlacement extends Placement:
|
||||
expect(palette != null, "production facility palette exists")
|
||||
if palette == null:
|
||||
return
|
||||
expect(palette.get_child_count() == 23 and palette.columns == 23,
|
||||
"palette has exactly 23 facility slots")
|
||||
expect(main.build_buttons.size() == 23, "all 23 registered facility buttons exist")
|
||||
expect(palette.get_child_count() == 25 and palette.columns == 25,
|
||||
"palette has exactly 25 facility slots (A07/A08 warps added)")
|
||||
expect(main.build_buttons.size() == 25, "all 25 registered facility buttons exist")
|
||||
for child: Node in palette.get_children():
|
||||
expect(child is Button and main.build_buttons.values().has(child),
|
||||
"every palette child is a registered facility button")
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
"new_audio_QA": false
|
||||
},
|
||||
"tests/ui/test_main_dashboard.gd": {
|
||||
"source_sha256": "fa9ee0c4b9aee00a2be6d05a9dab7c0c3e8b0beb2b944257de1dc308bfc3cc20",
|
||||
"source_sha256": "305227c1077a92ef3ee522b561a68b0685767740b46b51ff789c800931647ef1",
|
||||
"ordinary_cleanup_prepared": false,
|
||||
"ordinary_control_labels": [],
|
||||
"old_bounded_teardown": true,
|
||||
@@ -379,7 +379,7 @@
|
||||
"source_only_ownership_review": true
|
||||
},
|
||||
"tests/transport/test_runtime_transport.gd": {
|
||||
"source_sha256": "ec6f8fa8e8f2a76a6f6214b4ee84861aba1bb13845a342c5126905b01f428a15",
|
||||
"source_sha256": "78a5563ff2421ae65a0988cf0d26669f54bcc0f44250b00e5334cadf1228da94",
|
||||
"ordinary_cleanup_prepared": true,
|
||||
"ordinary_control_labels": [
|
||||
"single"
|
||||
@@ -407,7 +407,7 @@
|
||||
"new_audio_QA": false
|
||||
},
|
||||
"tests/ui/test_p08_normal_placement_ops.gd": {
|
||||
"source_sha256": "d3c246d34f8fd66f6ed237e42a86df89ebaf481bc9dee143b4de8d654ce0eb2e",
|
||||
"source_sha256": "a5c5abdc1348e7dadafd70f73072ac98afcc4c918211c3389e88da5e903af803",
|
||||
"ordinary_cleanup_prepared": true,
|
||||
"ordinary_control_labels": [
|
||||
"single"
|
||||
|
||||
Reference in New Issue
Block a user