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

This commit is contained in:
Agent Control Runner
2026-10-14 03:52:45 +09:00
parent 7dd450c64c
commit c2b59f04f9
13 changed files with 409 additions and 53 deletions
+26
View File
@@ -241,6 +241,24 @@ def make(base, source, checker):
formal = folder/'formal'
formal.mkdir()
for member in checker.MEMBERS: (formal/member).write_text('synthetic '+member+'\n')
shutil.copytree(formal, folder/'formal.bak')
shutil.copyfile(formal/'manifest.json', folder/'formal-slot-metadata.json')
profile = '/synthetic-private-profile'
absolute = profile+'/saves/manual_ui'
put(folder/'formal-export.json', {'pid':pids[0], 'requested_path':'user://saves/manual_ui',
'absolute_path':absolute, 'user_data_dir':profile,
'packages':{name:{'present':True, 'source_path':absolute+suffix,
'members':{n:{'sha256':sha(folder/name/n),'bytes':(folder/name/n).stat().st_size} for n in checker.MEMBERS}}
for name,suffix in [('formal',''),('formal.bak','.bak')]},
'slot_metadata':{'file':'formal-slot-metadata.json','sha256':sha(formal/'manifest.json')}})
for role,pid,op in [('writer',pids[0],'save'),('reader',pids[1],'load')]:
stem = role+'-save-result-0001'
(folder/(stem+'.var')).write_bytes(b'synthetic raw Dictionary stand-in')
put(folder/(stem+'.json'), {'pid':pid,'operation':'runtime_operation',
'requested_path':'user://saves/manual_ui','absolute_path':absolute,'user_data_dir':profile,
'result':typed({'ok':True,'operation':op,'save_path':'user://saves/manual_ui'}),
'raw_sha256':sha(folder/(stem+'.var'))})
put(folder/(role+'-save-results.json'), {'pid':pid,'records':[stem]})
put(folder/'save.json', {'members': {n: sha(formal/n) for n in checker.MEMBERS},
'pid': pids[0], 'cell': target, 'instance_id': 'f4'})
row = {'id': 'resume_overlap', 'kind': 'build', 'position': position(target),
@@ -259,6 +277,14 @@ def make(base, source, checker):
pair('pair', [101, 102], 'handler')
pair('os-pair', [201, 202], 'native_os')
shutil.copytree(out/'pair/formal', out/'pair/initial-formal')
initial = copy.deepcopy(json.loads((out/'pair/formal-export.json').read_text()))
initial.update(requested_path='user://saves/p08_initial',absolute_path='/synthetic-private-profile/saves/p08_initial')
initial['packages'] = {'initial-formal':initial['packages']['formal'],
'initial-formal.bak':{'present':False,'source_path':initial['absolute_path']+'.bak','members':{}}}
initial['packages']['initial-formal']['source_path'] = initial['absolute_path']
shutil.copyfile(out/'pair/initial-formal/manifest.json',out/'pair/initial-formal-slot-metadata.json')
initial['slot_metadata']['file'] = 'initial-formal-slot-metadata.json'
put(out/'pair/initial-formal-export.json',initial)
for role, pid in (('writer',201),('reader',202)):
prefix = 'os-runs/'+role+'/'
folder = out/prefix
+2 -6
View File
@@ -73,6 +73,7 @@ func setup() -> void:
if role == "writer":
await load_menu(main.find_child("SaveLoadMenu", true, false))
var stored: Dictionary = SaveService.inspect_snapshot(ProjectSettings.globalize_path(String(main.manual_save_path)))
record_save_result("initial_inspect", String(main.manual_save_path), stored)
expect(bool(stored.get("ok", false)), "read-only initial formal inspection")
expect(runtime.create_snapshot() == stored.get("snapshot", {}), "OS initial formal model exact")
expect(clock.paused, "diagnostic formal fixture starts paused")
@@ -154,12 +155,7 @@ func load_menu(menu: MenuButton) -> void:
await request(action)
func archive_formal() -> void:
DirAccess.make_dir_recursive_absolute(directory.path_join("formal"))
for member: String in MEMBERS:
var source := ProjectSettings.globalize_path(String(main.manual_save_path)).path_join(member)
var destination := directory.path_join("formal/" + member)
expect(DirAccess.copy_absolute(source, destination) == OK, "archive formal " + member)
expect(FileAccess.get_sha256(source) == FileAccess.get_sha256(destination), "archive formal SHA " + member)
export_package(String(main.manual_save_path), "formal")
func finish_observations() -> void:
json_file(role + "-requests.json", {"pid": OS.get_process_id(), "requests": all_requests})
+74 -4
View File
@@ -18,6 +18,9 @@ var target := Vector3i(-8, 0, 0)
var budget: Dictionary = {}
var input_method := "handler"
var observed_cell: Variant = null
var save_serial := 0
var save_records: Array = []
const INITIAL_SAVE := "user://saves/p08_initial"
func annotate(row: Dictionary) -> void:
row["input_method"] = input_method
@@ -69,6 +72,8 @@ func button(node: BaseButton) -> void:
expect(node != null and not node.disabled, "available production button")
if node == null or node.disabled:
return
if node.toggle_mode:
node.button_pressed = not node.button_pressed
node.pressed.emit()
await frames()
@@ -77,7 +82,9 @@ func choose(id: StringName) -> void:
await button(main.palette_toggle_button)
var node: Button = main.build_buttons[id]
await reveal(node)
await button(node)
if not node.button_pressed:
await button(node)
expect(node.button_pressed and main._selected_definition_id == id, "chosen production definition")
func click(cell: Vector3i) -> Dictionary:
var view: WorldPreview = main.world_preview
@@ -253,6 +260,7 @@ func run(p_tree: SceneTree, output: String, requested_role: String) -> Dictionar
expect(DirAccess.make_dir_recursive_absolute(directory) == OK, "diagnostic directory")
runtime = tree.root.get_node("SimulationRuntime")
clock = tree.root.get_node("GameClock")
runtime.save_operation_completed.connect(record_save_operation)
await setup()
runtime.command_completed.connect(observe)
main.world_preview.cell_clicked.connect(selected_cell)
@@ -265,10 +273,12 @@ func run(p_tree: SceneTree, output: String, requested_role: String) -> Dictionar
if requested_role == "standalone":
role = "reader"
actions = []
save_records = []
await reader(false)
json_file(role + "-actions.json", {"setup": "diagnostic", "input_method": input_method, "actions": actions})
finish_observations()
runtime.command_completed.disconnect(observe)
runtime.save_operation_completed.disconnect(record_save_operation)
var cleanup: Dictionary = await Cleanup.free_owner_and_wait(tree, main)
expect(cleanup.get(&"passed", false), "ordinary Main/audio cleanup")
json_file(role + "-process.json", {"pid": OS.get_process_id(), "engine": Engine.get_version_info(), "checks": checks, "failures": failures, "role": requested_role, "setup": "diagnostic"})
@@ -296,18 +306,78 @@ func setup() -> void:
clock.set_paused(true)
tree.root.size = Vector2i(1440, 810)
main = load("res://scenes/main/main.tscn").instantiate()
main.manual_save_path = directory.path_join("formal")
main.manual_save_path = "user://saves/manual_ui"
if role == "reader":
import_package(directory.path_join("formal"), String(main.manual_save_path))
tree.root.add_child(main)
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
func save_initial_fixture() -> void:
# Explicit diagnostic input, not an OS action or natural-play save.
var saved: Dictionary = SaveService.save_snapshot(directory.path_join("initial-formal"), runtime.create_snapshot())
var saved: Dictionary = SaveService.save_snapshot(INITIAL_SAVE, runtime.create_snapshot())
record_save_result("initial_save", INITIAL_SAVE, saved)
expect(bool(saved.get("ok", false)), "diagnostic initial formal save")
export_package(INITIAL_SAVE, "initial-formal")
# Seed the private normal slot, so its later normal save produces a backup.
import_package(ProjectSettings.globalize_path(INITIAL_SAVE), String(main.manual_save_path))
json_file("diagnostic-budget.json", budget)
func archive_formal() -> void:
pass
export_package(String(main.manual_save_path), "formal")
func record_save_operation(result: Dictionary) -> void:
record_save_result("runtime_operation", String(main.manual_save_path), result)
func record_save_result(operation: String, path: String, result: Dictionary) -> void:
save_serial += 1
var stem := "%s-save-result-%04d" % [role, save_serial]
var raw := FileAccess.open(directory.path_join(stem + ".var"), FileAccess.WRITE)
expect(raw != null, "raw save result")
if raw != null:
raw.store_buffer(var_to_bytes(result))
raw.close()
json_file(stem + ".json", {"pid": OS.get_process_id(), "operation": operation,
"requested_path": path, "absolute_path": ProjectSettings.globalize_path(path),
"user_data_dir": OS.get_user_data_dir(), "result": Codec.new().typed(result),
"raw_sha256": FileAccess.get_sha256(directory.path_join(stem + ".var"))})
save_records.append(stem)
json_file(role + "-save-results.json", {"pid": OS.get_process_id(), "records": save_records})
func import_package(source: String, destination: String) -> void:
var absolute := ProjectSettings.globalize_path(destination)
expect(DirAccess.make_dir_recursive_absolute(absolute) == OK, "private save directory")
for member: String in MEMBERS:
var origin := source.path_join(member)
var copy := absolute.path_join(member)
expect(DirAccess.copy_absolute(origin, copy) == OK, "private save input " + member)
expect(FileAccess.get_sha256(origin) == FileAccess.get_sha256(copy), "private save input SHA " + member)
func export_package(path: String, name: String) -> void:
var absolute := ProjectSettings.globalize_path(path)
var pins := {}
for suffix: String in ["", ".bak"]:
var source := absolute + suffix
var present := DirAccess.dir_exists_absolute(source)
pins[name + suffix] = {"source_path": source, "present": present, "members": {}}
if not present:
expect(not suffix.is_empty(), "formal package exists")
continue
var destination := directory.path_join(name + suffix)
expect(DirAccess.make_dir_recursive_absolute(destination) == OK, "export directory")
for member: String in MEMBERS:
var origin := source.path_join(member)
var copy := destination.path_join(member)
expect(DirAccess.copy_absolute(origin, copy) == OK, "export " + member)
var digest := FileAccess.get_sha256(origin)
expect(not digest.is_empty() and digest == FileAccess.get_sha256(copy), "export SHA " + member)
pins[name + suffix]["members"][member] = {"sha256": digest, "bytes": FileAccess.get_file_as_bytes(copy).size()}
# Slot metadata lives in the existing manifest; copy it verbatim.
var metadata := absolute.path_join("manifest.json")
var metadata_copy := directory.path_join(name + "-slot-metadata.json")
expect(DirAccess.copy_absolute(metadata, metadata_copy) == OK, "slot metadata export")
json_file(name + "-export.json", {"pid": OS.get_process_id(), "requested_path": path,
"absolute_path": absolute, "user_data_dir": OS.get_user_data_dir(), "packages": pins,
"slot_metadata": {"file": name + "-slot-metadata.json", "sha256": FileAccess.get_sha256(metadata_copy)}})
func finish_observations() -> void:
pass