P24-03: 選択と保存の受入

This commit is contained in:
Agent Control Runner
2026-10-13 01:20:44 +09:00
parent c2b8772e23
commit 4cefcff849
7 changed files with 144 additions and 18 deletions
@@ -0,0 +1,100 @@
extends SceneTree
## Passive observer: no selection, save/load, command or engine input injection.
## Runner launches separate owned processes with private user-data directories.
var _main: Control
var _elapsed := 0.0
var _last := ""
var _sequence := 0
var _output := ""
var _source_map: Dictionary = {}
func _initialize() -> void:
call_deferred("_start")
func _start() -> void:
_output = OS.get_environment("P24_OBSERVER_OUTPUT")
var pins := OS.get_environment("P24_OBSERVER_SOURCE_MAP")
if not _output.is_absolute_path() or not DirAccess.dir_exists_absolute(_output) or not pins.is_absolute_path():
push_error("P24 observer requires existing absolute output directory and source-map JSON")
quit(1)
return
var parsed: Variant = JSON.parse_string(FileAccess.get_file_as_string(pins))
if not parsed is Dictionary or parsed.is_empty():
push_error("P24 observer source map missing")
quit(1)
return
_source_map = parsed
root.size = Vector2i(1440, 810)
_main = load("res://scenes/main/main.tscn").instantiate()
root.add_child(_main)
print("P24_PROCESS pid=%d scene=res://scenes/main/main.tscn" % OS.get_process_id())
# Use the ordinary Main pause control through OS input when a frozen state
# is needed. The observer itself never changes Runtime or Clock.
func _process(delta: float) -> bool:
_elapsed += delta
if _elapsed >= 1.0 and is_instance_valid(_main):
_elapsed = 0.0
_capture()
return false
func _capture() -> void:
var runtime: Variant = root.get_node("SimulationRuntime")
var selection: Dictionary = _main.call("_capture_interface_selection")
var facility_id := StringName(selection.get("id", ""))
var facility: Variant = runtime.facility_service.get_instance(facility_id)
var guest_panel: GuestObservationPanel
var dialog: Variant = _main.get("guest_observer")
if dialog != null:
guest_panel = dialog.find_child("GuestObservationPanel", true, false) as GuestObservationPanel
var staff_panel: StaffManagementPanel = _main.get("staff_management_panel")
var staff_model: Dictionary = {}
for row: Dictionary in runtime.get_staff_management_report().get(&"staff", []):
if String(row.get(&"staff_id", "")) == String(selection.get("staff_id", "")):
staff_model = row.duplicate(true)
var needs: Dictionary = {}
if guest_panel != null:
for need: StringName in guest_panel.bars:
needs[need] = guest_panel.bars[need].value
var observation := {
"interface_selection": selection,
"layer": root.get_node("GameState").get("active_layer"),
"facility": {"id": String(facility_id), "model_snapshot": facility.create_snapshot() if facility != null else {},
"heading_text": _main.get("selected_heading_label").text,
"detail_text": _main.get("selected_detail_label").text,
"highlight_id": String(_main.get("world_preview").get("selected_entity_id"))},
"guest": {"id": selection.get("guest_id", ""),
"model_report": runtime.guest_service.get_observation(StringName(selection.get("guest_id", ""))),
"detail_text": guest_panel.details.text if guest_panel != null else "",
"priority_text": guest_panel.priority_label.text if guest_panel != null else "",
"decision_text": guest_panel.decision_label.text if guest_panel != null else "", "need_bars": needs},
"staff": {"id": selection.get("staff_id", ""), "model_report": staff_model,
"detail_text": staff_panel.staff_detail_label.text,
"editor_visible": staff_panel.editor_panel.is_visible_in_tree()},
}
var encoded := JSON.stringify(observation, "", true)
if encoded == _last:
return
_sequence += 1
var stem := "process-%d-observation-%d" % [OS.get_process_id(), _sequence]
var report_path := _output.path_join(stem + ".json")
var frame_path := _output.path_join(stem + ".png")
if FileAccess.file_exists(report_path) or FileAccess.file_exists(frame_path):
push_error("P24 observer refuses artifact overwrite")
return
var image := root.get_texture().get_image()
if image.save_png(frame_path) != OK:
push_error("P24 observer frame capture failed")
return
var file := FileAccess.open(report_path, FileAccess.WRITE)
if file == null:
push_error("P24 observer report write failed")
return
file.store_string(JSON.stringify({"pid": OS.get_process_id(), "source_map": _source_map,
"scene": "res://scenes/main/main.tscn", "ticks_usec": Time.get_ticks_usec(),
"raw_observation": observation, "screenshot": stem + ".png",
"formal_acceptance": false}, "\t", true) + "\n")
file.close()
_last = encoded
print("P24_RAW_OBSERVATION " + encoded)
@@ -0,0 +1 @@
uid://d24crossproc
+16 -15
View File
@@ -27,6 +27,22 @@ func _run() -> void:
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
await process_frame
await process_frame
# Build before intentionally preserving departed payroll debt.
var build_result: Variant = runtime.submit_command(&"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(-8, 0, 0)})
_expect(build_result.is_success(), "real facility construction starts: " + str(build_result.to_dictionary()))
clock.advance_minutes(120)
var facilities: Array = runtime.construction_service.get_facilities()
_expect(not facilities.is_empty(), "real facility construction completes")
if not facilities.is_empty():
var facility_id := StringName(facilities[0][&"instance_id"])
main.call("_on_facility_selected", facility_id)
main.get("world_preview").set("selected_entity_id", facility_id)
var previous_detail: String = main.get("selected_detail_label").text
_expect(main.get("_selected_entity_id") == facility_id, "select existing built facility")
_expect(runtime.submit_command(&"demolish_facility", {&"instance_id": facility_id}).is_success(), "real facility demolition commits")
main.call("_refresh_selected_entity")
_expect(main.get("_selected_entity_id") == &"" and main.get("_selected_entity_kind") == &"" and main.get("world_preview").get("selected_entity_id") == &"", "actual demolition clears ID kind and map highlight")
_expect(main.get("selected_entity_icon").texture == null and main.get("selected_detail_label").text != previous_detail, "actual demolition clears icon and stale detail")
var guest: GuestObservationPanel = main.call("_ensure_guest_observer")
var definition: GuestDefinition = runtime.guest_service.get_definitions()[0]
_expect(runtime.submit_command(&"spawn_guest", {&"guest_id": &"departure_guest", &"guest_type_id": definition.guest_type_id, &"gold": 731, &"options": {&"planned_exit_at": clock.absolute_minute + 1000}}).is_success(), "spawn actual guest")
@@ -75,21 +91,6 @@ func _run() -> void:
for version: Variant in [true, 1.5, "1", 0, 2]:
main.call("_restore_actor_interface_selection", {"interface_selection": {"version": version, "staff_id": "departure_unique", "staff_open": true}})
_expect(staff.selected_staff_id.is_empty() and not staff.visible, "wrong/old metadata version is ignored: " + str(version))
var build_result: Variant = runtime.submit_command(&"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(-8, 0, 0)})
_expect(build_result.is_success(), "real facility construction starts: " + str(build_result))
clock.advance_minutes(120)
var facilities: Array = runtime.construction_service.get_facilities()
_expect(not facilities.is_empty(), "real facility construction completes")
if not facilities.is_empty():
var facility_id := StringName(facilities[0][&"instance_id"])
main.call("_on_facility_selected", facility_id)
main.get("world_preview").set("selected_entity_id", facility_id)
var previous_detail: String = main.get("selected_detail_label").text
_expect(main.get("_selected_entity_id") == facility_id, "select existing built facility")
_expect(runtime.submit_command(&"demolish_facility", {&"instance_id": facility_id}).is_success(), "real facility demolition commits")
main.call("_refresh_selected_entity")
_expect(main.get("_selected_entity_id") == &"" and main.get("_selected_entity_kind") == &"" and main.get("world_preview").get("selected_entity_id") == &"", "actual demolition clears ID kind and map highlight")
_expect(main.get("selected_entity_icon").texture == null and main.get("selected_detail_label").text != previous_detail, "actual demolition clears icon and stale detail")
var original: String = OS.get_environment("THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF")
if original.is_empty() or not original.is_absolute_path():
_expect(false, "Runner must provide fresh absolute ordinary control proof")