fix(ci): address PR #31 review — harden cross-encoder fixture pipeline

- Fail on encode errors instead of silently writing empty .msgpack files
- Fail test on missing/empty fixture dir instead of silent skip
- Add all missing action variants (MoveSouth, MoveEast, MoveWest,
  Unpause, ToggleStanceDown, WalkAway) to GDScript fixture generator
- Add GDScript fixture staleness check to make pre-pr
- Validate repo root detection before writing outside client/
- Add file.flush() before close in headless mode
- Document fixture failure recovery in DEVOPS.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 09:45:13 +01:00
co-authored by Claude Opus 4.6
parent da63aa580e
commit 3ccac5432b
4 changed files with 69 additions and 21 deletions
+33 -4
View File
@@ -12,6 +12,7 @@ extends SceneTree
var _Msgpack: GDScript
var _count := 0
var _errors := 0
var _output_dir: String
@@ -22,27 +23,48 @@ func _init():
func _run():
_Msgpack = load("res://addons/messagepack/messagepack.gd")
# Resolve repo root from Godot project root (client/).
# Assumes client/ is one level below repo root — validated below.
var project_root := ProjectSettings.globalize_path("res://")
var repo_root := project_root.rstrip("/").get_base_dir()
_output_dir = repo_root.path_join("server/tests/fixtures/gdscript")
if not DirAccess.dir_exists_absolute(repo_root.path_join("server")):
push_error("Repo root detection failed: %s/server/ does not exist" % repo_root)
quit(1)
return
DirAccess.make_dir_recursive_absolute(_output_dir)
_generate_inputs()
_generate_boundary_inputs()
_generate_batch()
if _errors > 0:
push_error("FAILED: %d encode errors encountered" % _errors)
quit(1)
return
if _count == 0:
push_error("FAILED: no fixtures generated")
quit(1)
return
print("Generated %d GDScript fixtures at %s" % [_count, _output_dir])
quit()
func _write_fixture(name: String, bytes: PackedByteArray) -> void:
if bytes.is_empty():
push_error("Encode produced empty bytes for fixture: %s" % name)
_errors += 1
return
var path := _output_dir.path_join(name + ".msgpack")
var file := FileAccess.open(path, FileAccess.WRITE)
if file == null:
push_error("Failed to write fixture: %s (error: %d)" % [path, FileAccess.get_open_error()])
_errors += 1
return
file.store_buffer(bytes)
file.flush()
file.close()
print(" Wrote %s (%d bytes)" % [name, bytes.size()])
_count += 1
@@ -90,10 +112,11 @@ func _encode_inputs(inputs: Array) -> PackedByteArray:
func _generate_inputs() -> void:
# Unit variants: movement directions (tick=100)
_write_fixture("input_move_north",
_encode_input(100, "MoveNorth"))
for dir_name in ["MoveNortheast", "MoveSoutheast", "MoveSouthwest", "MoveNorthwest"]:
# Unit variants: all 8 movement directions (tick=100)
for dir_name in [
"MoveNorth", "MoveSouth", "MoveEast", "MoveWest",
"MoveNortheast", "MoveSoutheast", "MoveSouthwest", "MoveNorthwest",
]:
_write_fixture("input_%s" % dir_name.to_snake_case(),
_encode_input(100, dir_name))
@@ -108,8 +131,14 @@ func _generate_inputs() -> void:
# Other unit variants
_write_fixture("input_pause",
_encode_input(100, "Pause"))
_write_fixture("input_unpause",
_encode_input(100, "Unpause"))
_write_fixture("input_toggle_stance_up",
_encode_input(100, "ToggleStanceUp"))
_write_fixture("input_toggle_stance_down",
_encode_input(100, "ToggleStanceDown"))
_write_fixture("input_walk_away",
_encode_input(100, "WalkAway"))
func _generate_boundary_inputs() -> void: