Record original-alignment implementation and traceability checkpoint

Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
This commit is contained in:
Takeshi
2026-09-30 23:52:24 +09:00
parent 6077e02ebe
commit 5dde0c5ec2
490 changed files with 54051 additions and 809 deletions
+323
View File
@@ -0,0 +1,323 @@
extends SceneTree
## Command-backed UI acceptance. Incidents are disclosed threat fixtures;
## combat progression uses the ordinary game clock.
var _checks := 0
var _failures := 0
var _main: Control
var _runtime: Variant
var _clock: Variant
var _panel: DefenseManagementPanel
var _facility_id: StringName = &""
var _path := "user://tests/defense_management_%d/slot" % OS.get_process_id()
var _slots: Array[String] = []
func _initialize() -> void:
call_deferred("_run")
func _expect(ok: bool, message: String) -> void:
_checks += 1
if not ok:
_failures += 1
push_error(message)
func _run() -> void:
root.gui_embed_subwindows = true
root.size = Vector2i(1440, 810)
_runtime = root.get_node("SimulationRuntime")
_clock = root.get_node("GameClock")
_clock.reset(1, 6, 0)
root.get_node("GameState").reset()
_runtime.initialize(_clock, root.get_node("GameState"), 42540)
_clock.set_paused(true)
_main = load("res://scenes/main/main.tscn").instantiate()
_main.set("manual_save_path", _path)
root.add_child(_main)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
await process_frame
await process_frame
_main.call("_set_management_panel_open", true)
_main.find_child("DefenseManagementButton", true, false).pressed.emit()
_panel = _main.get("defense_manager").find_child("DefenseManagementPanel", true, false)
_expect(_main.get("defense_manager").visible and _panel.selected_raid_id.is_empty(), "ordinary management button opens empty defense screen")
_expect("ありません" in _panel.summary_label.text and _panel.start_button.disabled, "no incident has no actionable start")
# Paid normal construction and hiring establish real target/defender links.
_main.get("defense_manager").hide()
_main.call("_select_build_definition", &"reception")
_main.call("_on_map_cell_clicked", Vector3i(-8, 0, 0))
_main.call("_clear_build_selection")
_clock.advance_minutes(120)
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"reception": _facility_id = facility.instance_id
_expect(not _facility_id.is_empty(), "normal paid reception construction provides a real target")
_main.get("staff_candidate_toggle_button").pressed.emit()
_main.get("staff_candidate_buttons")[&"staff_klaus"].pressed.emit()
_expect(_runtime.staff_service.has_staff(&"staff_klaus"), "ordinary recruitment hires guard with existing combat data")
if _facility_id.is_empty() or not _runtime.staff_service.has_staff(&"staff_klaus"):
_finish()
return
_expect(_create_incident(&"ui.defense.fixture").is_success(), "disclosed threat fixture creates an existing preparing incident")
_main.find_child("DefenseManagementButton", true, false).pressed.emit()
_expect(_panel.selected_raid_id == &"ui.defense.fixture" and "準備中" in _panel.summary_label.text, "real popup observes Runtime incident publication")
_expect(_panel.find_child("DefenseCancelPreparation", true, false) == null and &"cancel_defense_raid" not in DefenseManagementPanel.COMMANDS, "preparation has no free incident-cancellation button or allowed UI command")
var preparing_before: Dictionary = _runtime.create_snapshot()
_panel.call("_submit", &"cancel_defense_raid")
_panel.retreat_button.pressed.emit()
_expect(not _panel.confirmation.visible and (_panel.get("_confirmation_context") as Dictionary).is_empty(), "preparation cannot open a withdrawal confirmation even through a stale hidden button signal")
_expect(preparing_before == _runtime.create_snapshot(), "forbidden preparation cancellation and withdrawal do not enqueue commands or mutate any Runtime state")
var before: Dictionary = _runtime.create_snapshot()
var report: Dictionary = _runtime.get_defense_management_report(&"ui.defense.fixture")
_expect(before == _runtime.create_snapshot(), "management report does not mutate gameplay or RNG")
report[&"selected"][&"objective_ids"].clear()
report[&"staff"][0][&"combat"] = 999
_expect(before == _runtime.create_snapshot(), "compact projections are detached from Defense and Staff")
_expect(not _panel.register_staff_button.disabled and _panel.start_button.disabled, "unregistered objective explains and prevents start")
_panel.register_staff_button.pressed.emit()
var raid: Dictionary = _runtime.defense_service.get_raid(&"ui.defense.fixture")
var guard: StaffRuntime = _runtime.staff_service.get_staff(&"staff_klaus")
_expect(raid[&"combatants"].has(&"staff_klaus"), "real participation button registers defender through Runtime command")
_expect(raid[&"combatants"][&"staff_klaus"][&"combat"] == guard.skills[&"combat"] and guard.state == StaffRuntime.State.UNAVAILABLE, "defender uses actual stored skill and becomes unavailable for work")
_panel.register_objective_button.pressed.emit()
raid = _runtime.defense_service.get_raid(&"ui.defense.fixture")
_expect(raid[&"assets"].has(_facility_id) and not _panel.start_button.disabled, "real target button registers existing facility and enables start")
_expect(raid[&"assets"][_facility_id][&"integrity"] == _runtime.facility_service.get_instance(_facility_id).durability, "objective reflects current facility durability")
# Policy edits survive publication until explicitly applied, and do not
# change current policy before the player presses the apply button.
_panel.policy_option.select(2)
_panel.policy_option.item_selected.emit(2)
_panel.threshold_spin.value = 7
_panel.refresh()
_expect(_panel.policy_option.selected == 2 and _panel.threshold_spin.value == 7, "uncommitted retreat draft survives UI refresh")
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"retreat_policy"] == &"HOLD", "editing a draft alone does not alter defense")
_panel.policy_button.pressed.emit()
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"retreat_policy"] == &"THRESHOLD" and _runtime.defense_service.get_raid(&"ui.defense.fixture")[&"defender_retreat_threshold"] == 7, "apply button updates existing retreat policy contract")
_main.call("_save_manual")
_slots.append(_path)
var package := SaveService.load_snapshot(_path)
_expect(package.get(&"ok", false) and package.get(&"manifest", {}).get("interface_selection", {}).get("defense_id") == "ui.defense.fixture" and package.get(&"manifest", {}).get("interface_selection", {}).get("defense_open") == true, "normal save captures selected incident and open popup")
# Change a real policy after saving so the following read proves restoration.
_panel.policy_option.select(0)
_panel.policy_option.item_selected.emit(0)
_panel.policy_button.pressed.emit()
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"retreat_policy"] == &"HOLD", "ordinary policy button changes live preparation after the saved checkpoint")
_main.get("defense_manager").hide()
_main.call("_load_manual", true)
_expect(_main.get("defense_manager").visible and _panel.selected_raid_id == &"ui.defense.fixture", "manual load restores actual saved incident and visibility")
var restored_preparing: Dictionary = _runtime.defense_service.get_raid(&"ui.defense.fixture")
_expect(restored_preparing[&"state"] == DefenseService.RaidState.PREPARING and _runtime.staff_service.get_staff(&"staff_klaus").state == StaffRuntime.State.UNAVAILABLE, "saved preparation remains a live threat and keeps its actual defender locked")
_expect(restored_preparing[&"retreat_policy"] == &"THRESHOLD" and restored_preparing[&"defender_retreat_threshold"] == 7, "manual load restores the saved policy instead of the later live edit")
_clock.set_paused(false)
_panel.start_button.pressed.emit()
_panel.start_button.pressed.emit()
var queued := 0
for command: Dictionary in _runtime.command_queue.create_snapshot().get("pending", []):
if command.get("command_type", "") == "start_defense_raid": queued += 1
_expect(queued == 1 and _panel.start_button.disabled, "playing simulation accepts a single pending start despite repeated button activation")
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.PREPARING, "accepted running command has not mutated the incident before its tick")
_clock.advance_minutes(1)
_clock.set_paused(true)
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.ACTIVE and _panel.retreat_button.visible, "start button enters actual battle and exposes withdrawal")
# Stale start invocation is rejected by Runtime and never restarts combat.
var active: Dictionary = _runtime.defense_service.create_snapshot()
_panel.start_button.pressed.emit()
_expect(active == _runtime.defense_service.create_snapshot(), "invalid repeated start preserves active battle state")
# Withdrawal is available only after the existing incident starts. Backing
# out changes presentation alone and retains the threat and defender locks.
var active_before_return: Dictionary = _runtime.create_snapshot()
_panel.retreat_button.pressed.emit()
_expect(_panel.confirmation.visible and _panel.confirmation.title == "撤退の確認", "active battle opens only a withdrawal confirmation")
_panel.confirmation.canceled.emit()
_panel.confirmation.hide()
_expect((_panel.get("_confirmation_context") as Dictionary).is_empty() and active_before_return == _runtime.create_snapshot(), "returning from active withdrawal clears confirmation without changing gameplay or queued commands")
# Save the ACTIVE incident, then replace its queue through an actual load.
_main.call("_save_manual")
var active_package := SaveService.load_snapshot(_path)
_expect(active_package.get(&"ok", false), "normal save writes the active battle before stale-context testing")
_panel.retreat_button.pressed.emit()
var context: Dictionary = _panel.get("_confirmation_context").duplicate(true)
_expect(_panel.confirmation.visible and not context.is_empty(), "active withdrawal captures the current command queue before load")
_main.call("_load_manual", true)
_expect(_main.get("build_status_label").text == "手動セーブを読み込みました。" and _main.get("defense_manager").visible and _panel.selected_raid_id == &"ui.defense.fixture", "successful strict read restores active incident selection and popup")
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.ACTIVE, "active save restores actual battle state")
_expect(not _panel.confirmation.visible and (_panel.get("_confirmation_context") as Dictionary).is_empty(), "successful active load dismisses and invalidates the old withdrawal confirmation")
_expect(_runtime.command_queue.get_instance_id() != int(context.get(&"queue_instance_id", 0)), "successful load uses a different command queue than the pending confirmation")
var loaded_before_replay: Dictionary = _runtime.create_snapshot()
_panel.set("_confirmation_context", context)
_panel.confirmation.confirmed.emit()
_expect(loaded_before_replay == _runtime.create_snapshot(), "replayed old-queue withdrawal context leaves every loaded Runtime subsystem and command unchanged")
_expect("対象が変わりました" in _panel.result_label.text and _runtime.staff_service.get_staff(&"staff_klaus").state == StaffRuntime.State.UNAVAILABLE, "stale withdrawal reports rejection and leaves the loaded defender assigned")
# The disclosed fixture creates only the threat. Combat now progresses from
# ordinary game minutes without a round button or a synthetic player command.
var interval := int(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"round_minutes"])
_clock.advance_minutes(interval - 1)
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"current_round"] == 0, "ordinary battle waits for its persisted round interval")
_clock.advance_minutes(1)
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"current_round"] == 1, "ordinary clock automatically resolves the first attack")
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.BETWEEN_WAVES and _panel.next_wave_button.visible, "service wave outcome exposes ordinary next-wave control")
_panel.next_wave_button.pressed.emit()
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.ACTIVE, "player next-wave button advances existing incident through Runtime")
_panel.retreat_button.pressed.emit()
_panel.confirmation.confirmed.emit()
_panel.confirmation.hide()
_expect(_runtime.defense_service.get_raid(&"ui.defense.fixture")[&"state"] == DefenseService.RaidState.DEFENDERS_RETREATED, "confirmed withdrawal terminates battle through Runtime")
_expect(_runtime.staff_service.get_staff(&"staff_klaus").state == StaffRuntime.State.FLEEING and _panel.policy_button.disabled, "retreat reconciles staff and terminal UI disables commands")
# Failed gameplay loads retain current selection. Optional bad presentation
# must not invalidate a valid gameplay load or reopen this inspector.
var selected := _panel.selected_raid_id
_expect(not _runtime.load_from_path(_path + "_absent").get(&"ok", true), "missing package load fails explicitly")
_expect(_panel.selected_raid_id == selected and _main.get("defense_manager").visible, "failed load preserves visible defense selection")
for entry: Array in [["types", []], ["missing", "missing.raid"], ["long", "x".repeat(257)]]:
var slot: String = _path + "_" + entry[0]
_slots.append(slot)
_expect(SaveService.save_snapshot(slot, _runtime.create_snapshot(), {&"interface_selection": {&"version": 1, &"defense_id": entry[1], &"defense_open": true}}).get(&"ok", false), "write malformed optional UI metadata fixture " + entry[0])
_expect(_runtime.load_from_path(slot).get(&"ok", false), "valid gameplay still loads with optional invalid UI metadata " + entry[0])
_expect(not _main.get("defense_manager").visible, "invalid incident selection does not reopen popup " + entry[0])
_main.find_child("DefenseManagementButton", true, false).pressed.emit()
await _test_incident_presentation()
var capture := OS.get_environment("THE_TREE_DEFENSE_MANAGEMENT_CAPTURE")
if not capture.is_empty():
for frame: int in 4: await process_frame
await RenderingServer.frame_post_draw
_expect(root.get_texture().get_image().save_png(capture) == OK, "capture actual defense popup after source-label and saved-selection checks")
_finish()
func _create_incident(id: StringName) -> CommandResult:
return _runtime.submit_command(&"create_defense_raid", {&"raid_id": id, &"definition": {
&"entry_points": [&"fixture.gate"], &"objective_ids": [_facility_id],
&"waves": [
{&"wave_id": &"fixture.wave.one", &"threat_strength": 5, &"max_rounds": 1},
{&"wave_id": &"fixture.wave.two", &"threat_strength": 5, &"max_rounds": 1},
],
}}, &"test.fixture")
func _finish() -> void:
if is_instance_valid(_main): _main.queue_free()
for path: String in _slots:
SaveService.delete_save(path)
if _failures == 0: print("PASS: %d checks" % _checks)
else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
## Display acceptance uses explicit disclosed causes through the actual Event
## queue. These cases do not claim normal threat generation or approve balance.
func _test_incident_presentation() -> void:
var expected := {"THIEF": "盗賊の襲撃", "BANKRUPTCY_RIOT": "客の暴動", "BANK_DELINQUENCY": "銀行による取り立て", "AUTHORITY_ESCALATION": "監査による摘発"}
var event_ids: Dictionary = {}
for kind: String in expected:
var event_id := StringName("ui.presentation.event." + kind)
var instance_id := StringName("ui.presentation.instance." + kind)
var action_id := &"ui.presentation.threat"
var action := EventAction.new().configure(action_id, EventAction.Type.CREATE_DEFENSE_RAID, {
&"template_id": &"ui.presentation.disclosed.template", &"source_kind": kind,
&"definition": _presentation_raid_definition(),
})
var registered: CommandResult = _runtime.submit_command(&"register_event_definition", {&"definition": {
&"event_id": event_id, &"category": &"DEFENSE", &"foreground": false,
&"transaction_policy": &"ROLLBACK_ALL", &"automatic_actions": [action.create_snapshot()],
}}, &"test.fixture")
_expect(registered.is_success(), "disclosed presentation source registers " + kind)
var queued: CommandResult = _runtime.submit_command(&"enqueue_event", {
&"event_id": event_id, &"actor_ids": [], &"options": {
&"event_instance_id": instance_id,
&"context": {&"threat_source_id": StringName("ui.presentation.cause." + kind)},
},
}, &"test.fixture")
_expect(queued.is_success(), "disclosed presentation cause enters the real event queue " + kind)
_clock.advance_minutes(1)
var raid_id := StringName("event_defense_raid." + JSON.stringify([String(instance_id), String(action_id)], "", true, true).sha256_text())
event_ids[kind] = raid_id
var raid: Dictionary = _runtime.defense_service.get_raid(raid_id)
_expect(not raid.is_empty() and raid.get(&"event_source", {}).get("source_kind", "") == kind, "ordinary event processing supplies explicit source provenance " + kind)
# The two manual incidents deliberately resemble a source enum. They have
# no event provenance and must remain generic defense records.
var manual_one := &"THIEF"
var manual_two := &"ui.presentation.manual.thief.source"
for id: StringName in [manual_one, manual_two]:
var definition := _presentation_raid_definition()
definition[&"source_id"] = &"THIEF"
var created: CommandResult = _runtime.submit_command(&"create_defense_raid", {&"raid_id": id, &"definition": definition}, &"test.fixture")
_expect(created.is_success(), "manual same-minute presentation incident is disclosed and created")
_main.find_child("DefenseManagementButton", true, false).pressed.emit()
_panel.refresh()
var before: Dictionary = _runtime.create_snapshot()
var report: Dictionary = _runtime.get_defense_management_report(event_ids["THIEF"])
var selected: Dictionary = report.get(&"selected", {})
_expect(selected.get(&"source_kind", "") == "THIEF" and typeof(selected.get(&"created_at")) == TYPE_INT, "compact management report exposes only explicit cause and persisted creation time")
selected[&"source_kind"] = "BANK_DELINQUENCY"
selected[&"created_at"] = -1
_expect(_runtime.create_snapshot() == before, "display projection fields remain owned and cannot mutate gameplay, source or RNG")
for kind: String in expected:
var raid_id := StringName(event_ids[kind])
_select_presentation_option(raid_id)
var index := _panel.raid_option.selected
_expect(_panel.selected_raid_id == raid_id and _panel.raid_option.get_item_metadata(index) == raid_id, "cause label never replaces internal selection metadata " + kind)
_expect(expected[kind] in _panel.raid_option.get_item_text(index) and "日目" in _panel.raid_option.get_item_text(index), "option shows readable cause and occurrence time " + kind)
_expect(expected[kind] in _panel.summary_label.text and String(raid_id) not in _panel.summary_label.text and "event_defense_raid." not in _panel.raid_option.get_item_text(index), "summary and option keep the SHA out of user text " + kind)
var again: Dictionary = _runtime.create_snapshot()
_panel.refresh()
_expect(_runtime.create_snapshot() == again and _panel.selected_raid_id == raid_id, "repeated label refresh preserves every gameplay system and selected record " + kind)
var manual_labels: Array[String] = []
for id: StringName in [manual_one, manual_two]:
_select_presentation_option(id)
manual_labels.append(_panel.raid_option.get_item_text(_panel.raid_option.selected))
_expect(_panel.selected_raid_id == id and "防衛戦" in _panel.summary_label.text and "盗賊" not in _panel.summary_label.text, "manual ID and source_id never imply a thief cause")
_expect(manual_labels[0] != manual_labels[1] and "(1)" in manual_labels[0] and "(2)" in manual_labels[1], "same cause/time manual incidents remain separately readable")
_expect(_panel.call("_raid_base_label", {&"raid_id": &"THIEF", &"source_id": &"THIEF"}) == "防衛戦 · 時刻不明", "legacy projection without cause/time never fabricates a source or midnight timestamp")
_expect("防衛戦" in String(_panel.call("_raid_base_label", {&"source_kind": "FUTURE_KIND", &"created_at": 0})), "unknown cause text receives a neutral label")
var saved_id := StringName(event_ids["THIEF"])
_select_presentation_option(saved_id)
var saved_text := _panel.summary_label.text
_panel.policy_option.select(2)
_panel.policy_option.item_selected.emit(2)
_panel.threshold_spin.value = 17
_panel.policy_button.pressed.emit()
_expect(_runtime.defense_service.get_raid(saved_id)[&"retreat_policy"] == &"THRESHOLD" and _runtime.defense_service.get_raid(saved_id)[&"defender_retreat_threshold"] == 17, "readable event selection sends the ordinary policy command to its actual internal raid")
_expect(_runtime.defense_service.get_raid(event_ids["BANK_DELINQUENCY"])[&"retreat_policy"] == &"HOLD", "selected policy action never affects another similarly displayed incident")
_main.call("_save_manual")
var package := SaveService.load_snapshot(_path)
_expect(package.get(&"ok", false) and package.get(&"manifest", {}).get("interface_selection", {}).get("defense_id") == String(saved_id), "normal UI save retains the full internal event raid ID")
_select_presentation_option(manual_two)
_main.get("defense_manager").hide()
_main.call("_load_manual", true)
_expect(_main.get("build_status_label").text == "手動セーブを読み込みました。" and _main.get("defense_manager").visible, "strict normal load restores the saved readable defense panel")
_expect(_panel.selected_raid_id == saved_id and _panel.summary_label.text == saved_text, "cause, creation time, state and exact selected event raid survive normal save/load")
_expect(_runtime.defense_service.get_raid(saved_id)[&"event_source"]["source_kind"] == "THIEF", "display labels never overwrite restored event provenance")
_expect(_runtime.defense_service.get_raid(saved_id)[&"retreat_policy"] == &"THRESHOLD" and _panel.threshold_spin.value == 17, "saved user command and policy controls restore on the same readable incident")
var restored_labels: Array[String] = []
for id: StringName in [manual_one, manual_two]:
_select_presentation_option(id)
restored_labels.append(_panel.raid_option.get_item_text(_panel.raid_option.selected))
_expect(restored_labels == manual_labels, "duplicate occurrence labels retain the same order after restoration")
_select_presentation_option(saved_id)
var title_before_failed_load := _panel.summary_label.text
_expect(not _runtime.load_from_path(_path + "_presentation_absent").get(&"ok", true), "failed load remains explicit during source-label acceptance")
_expect(_panel.selected_raid_id == saved_id and _panel.summary_label.text == title_before_failed_load, "failed read preserves readable selection and title")
for frame: int in 4: await process_frame
_expect(not _panel.raid_option.fit_to_longest_item and _panel.raid_option.clip_text, "raid selector cannot force window width from an internal or future long label")
_expect(_main.get("defense_manager").size.x <= 520 and _panel.raid_option.get_global_rect().size.x <= _main.get("defense_manager").size.x, "actual 520px defense dialog and selector stay within their intended width")
_expect(_panel.raid_option.get_item_tooltip(_panel.raid_option.selected) == _panel.raid_option.get_item_text(_panel.raid_option.selected), "clipped selector offers the full human-readable text instead of a technical ID")
func _select_presentation_option(raid_id: StringName) -> void:
for index: int in _panel.raid_option.item_count:
if _panel.raid_option.get_item_metadata(index) == raid_id:
_panel.raid_option.select(index)
_panel.raid_option.item_selected.emit(index)
return
_expect(false, "requested presentation raid exists in actual option metadata")
func _presentation_raid_definition() -> Dictionary:
return {
&"entry_points": [&"ui.presentation.disclosed.gate"], &"objective_ids": [_facility_id],
&"round_minutes": 5, &"max_duration_minutes": 120,
&"retreat_policy": &"HOLD", &"defender_retreat_threshold": 0,
&"terrain_bonus": 0, &"command_bonus": 0, &"surprise_penalty": 0, &"multiple_entry_penalty": 0,
&"waves": [{&"wave_id": &"ui.presentation.disclosed.wave", &"threat_strength": 5, &"max_rounds": 1, &"retreat_threshold": 0}],
}