P08-03: 配置の通常操作受入

This commit is contained in:
Agent Control Runner
2026-10-14 04:21:06 +09:00
parent c2b59f04f9
commit 8e3d79d5f2
12 changed files with 543 additions and 54 deletions
+74 -3
View File
@@ -20,6 +20,7 @@ var input_method := "handler"
var observed_cell: Variant = null
var save_serial := 0
var save_records: Array = []
var operation_results: Array[Dictionary] = []
const INITIAL_SAVE := "user://saves/p08_initial"
func annotate(row: Dictionary) -> void:
@@ -43,8 +44,10 @@ func json_file(name: String, data: Variant) -> void:
file.close()
func capture(label: String) -> String:
return capture_model(label, runtime.create_snapshot())
func capture_model(label: String, snapshot: Dictionary) -> String:
var name := role + "-" + label
var snapshot: Dictionary = runtime.create_snapshot()
json_file(name + ".json", Codec.new().typed(snapshot))
var file := FileAccess.open(directory.path_join(name + ".var"), FileAccess.WRITE)
if file != null:
@@ -226,10 +229,15 @@ func writer() -> void:
await demolish(target, "demolition")
await build(&"reception", target, "rebuild")
await camera()
capture("presave")
await button(main.find_child("ManualSaveButton", true, false))
expect(main.build_status_label.text == "手動セーブを保存しました。", "normal save")
capture("checkpoint")
await archive_formal()
var formal := inspect_formal()
var presave: Dictionary = bytes_to_var(FileAccess.get_file_as_bytes(directory.path_join("writer-presave.var")))
expect(var_to_bytes(presave) == var_to_bytes(formal), "whole presave formal model")
verify_operation(formal, operation_results.back(), &"SAVE_CHECKPOINT_SUCCEEDED")
var members := {}
for member: String in MEMBERS:
members[member] = FileAccess.get_sha256(directory.path_join("formal/" + member))
@@ -245,8 +253,10 @@ func reader(distinct: bool) -> void:
await frames()
expect(main.build_status_label.text == "手動セーブを読み込みました。", "normal resume")
capture("checkpoint")
var original := FileAccess.get_file_as_bytes(directory.path_join("writer-checkpoint.var"))
expect(original == var_to_bytes(runtime.create_snapshot()), "whole formal resume model")
var formal := inspect_formal()
var original := FileAccess.get_file_as_bytes(directory.path_join("writer-formal-model.var"))
expect(original == var_to_bytes(formal), "whole formal resume model")
verify_operation(formal, operation_results.back(), &"SAVE_RECOVERY_LOADED")
expect(String(runtime.world_service.get_occupant(target)) == saved.instance_id, "same resumed instance/cell")
for member: String in MEMBERS:
expect(FileAccess.get_sha256(directory.path_join("formal/" + member)) == saved.members[member], "reader preserves formal member " + member)
@@ -325,7 +335,68 @@ func save_initial_fixture() -> void:
func archive_formal() -> void:
export_package(String(main.manual_save_path), "formal")
func inspect_formal() -> Dictionary:
var stored := SaveService.inspect_snapshot(String(main.manual_save_path))
record_save_result("formal_inspect", String(main.manual_save_path), stored)
expect(bool(stored.get(&"ok", false)), "read-only formal inspection")
var formal: Dictionary = stored.get(&"snapshot", {})
capture_model("formal-model", formal)
return formal
func verify_operation(formal: Dictionary, result: Dictionary, event_type: StringName) -> void:
var expected := formal.duplicate(true)
var model: Dictionary = expected[&"systems"][&"runtime"]
var counters: Dictionary = model[&"id_generator"]["counters"]
var next: int = int(counters.get("domain_event", 0)) + 1
counters["domain_event"] = next
var event_id := "domain_event.%012d" % next
var payload := {
&"ok": bool(result.get(&"ok", false)),
&"code": StringName(result.get(&"code", &"UNKNOWN")),
&"message": str(result.get(&"message", "")),
&"operation": StringName(result.get(&"operation", &"unknown")),
&"save_path": str(result.get(&"save_path", result.get(&"path", ""))),
&"reason": StringName(result.get(&"reason", &"")),
&"request_id": int(result.get(&"request_id", 0)),
&"checkpoint_absolute_minute": int(result.get(&"checkpoint_absolute_minute", formal[&"clock"][&"absolute_minute"])),
&"duration_us": int(result.get(&"duration_us", 0)),
&"runtime_duration_us": int(result.get(&"runtime_duration_us", 0)),
}
if result.get(&"selected") is Dictionary:
var selected: Dictionary = result[&"selected"]
payload[&"selected_candidate_id"] = StringName(selected.get(&"id", &""))
payload[&"selected_candidate_kind"] = StringName(selected.get(&"kind", &""))
payload[&"selected_generation"] = int(selected.get(&"generation", -1))
if result.get(&"manifest") is Dictionary:
var manifest: Dictionary = result[&"manifest"]
payload[&"save_id"] = str(manifest.get("save_id", ""))
payload[&"saved_absolute_minute"] = int(manifest.get("absolute_minute", -1))
var stream: Dictionary = model[&"domain_events"]
stream["published_ids"].append(event_id)
stream["published_ids"].sort()
var notification := SimulationDomainEvent.new(StringName(event_id), event_type,
int(formal[&"clock"][&"absolute_minute"]), &"save_service", [], payload, 0)
stream["recent_events"].append(notification.to_dictionary())
if stream["recent_events"].size() > DomainEventStream.RECENT_EVENT_LIMIT:
stream["recent_events"].pop_front()
capture_model("expected-operation", expected)
capture("operation-actual")
var actual: Dictionary = bytes_to_var(FileAccess.get_file_as_bytes(directory.path_join(role + "-checkpoint.var")))
json_file(role + "-operation-comparison.json", {
"pid": OS.get_process_id(), "delta_kind": "source_defined_notification",
"formal_model": role + "-formal-model", "actual_model": role + "-checkpoint",
"expected_model": role + "-expected-operation", "returned_result": Codec.new().typed(result),
"saved_recent_count": formal[&"systems"][&"runtime"][&"domain_events"]["recent_events"].size(),
"expected_recent_count": stream["recent_events"].size(),
"actual_recent_count": actual[&"systems"][&"runtime"][&"domain_events"]["recent_events"].size(),
"whole_raw_equal": var_to_bytes(expected) == var_to_bytes(actual),
})
# Complete raw actual/expected snapshots remain available on unknown drift.
expect(var_to_bytes(expected) == var_to_bytes(actual), "whole source-defined save operation model; see " + role + "-operation-comparison.json")
expect(var_to_bytes(actual) == var_to_bytes(runtime.create_snapshot()), "read-only inspection preserves live model")
func record_save_operation(result: Dictionary) -> void:
operation_results.append(result.duplicate(true))
record_save_result("runtime_operation", String(main.manual_save_path), result)
func record_save_result(operation: String, path: String, result: Dictionary) -> void: