fix(client): clear the test debt — 2 production bugs, suite fully green (T-973 et al.)

Production fixes surfaced by honest test triage:
- hud_groups.gd: _set_group_z crashed on freed HUD nodes — the typed loop
  variable errors before the is_instance_valid guard runs; prune first
- fog_state.gd: _resize cleared _prev_visible (world-space keys survive
  resizes), so pre-resize tiles never decayed VISIBLE→EXPLORED (D-059)

Test debt (T-928/929/934/935/936/937/938/939, T-864, T-973): lambda
local-capture bugs rewritten with array captures (now assert exact
emission counts), e2e suites updated to the current handshake +
StartupMessage protocol and stream-aware reads against the live binary,
fog perf test measures steady state, chime test pins the shipped 800ms
catalog asset (D-067 amended separately), monologue gdUnit4 API typo,
battery-warning tests follow the MetaScreen on_open lifecycle. 3 sprint2
proof tests revived (corner_reveal had passed from the wrong tile — NPC3
blocks (18,14); route corrected). Soft-skips converted to real do_skip
reporting. T-1068: 7 orphan .gd.uid deleted, _format_pop/_format_radius
deduped into atlas_format.gd (preload, no class_name — headless cache).

Suite: 1264 cases/20 failures → 1268/0, independently re-verified
(2536/2536, exit 0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 16:22:01 +02:00
co-authored by Claude Fable 5
parent b7a8cd1876
commit c64231e8ee
25 changed files with 603 additions and 269 deletions
+5 -1
View File
@@ -131,7 +131,11 @@ func _resize(bounds: Rect2i) -> void:
_tint_image = Image.create_from_data(_width, _height, false, Image.FORMAT_RGB8, _tint_bytes)
zone_tint_texture = ImageTexture.create_from_image(_tint_image)
_prev_visible.clear()
# NOTE: _prev_visible is deliberately NOT cleared here. Its keys are
# world-space coordinates, which stay valid across resizes. Clearing it
# meant tiles visible before a resize never decayed EXP_VISIBLE→EXP_EXPLORED
# when leaving LOS (D-059 violation — they rendered as currently-visible
# forever instead of deep fog).
func update_from_state() -> void:
+7 -3
View File
@@ -144,6 +144,10 @@ func _apply_z(node: CanvasItem, group: String) -> void:
func _set_group_z(group: String, z: int) -> void:
if not _groups.has(group):
return
for node: CanvasItem in _groups[group]:
if is_instance_valid(node):
node.z_index = z
# Prune freed nodes first: a typed loop variable (`for node: CanvasItem`)
# errors on assignment of a freed instance BEFORE any is_instance_valid
# guard can run. Nodes freed without unregister() must not crash the manager.
var alive: Array = _groups[group].filter(func(n): return is_instance_valid(n))
_groups[group] = alive
for node: CanvasItem in alive:
node.z_index = z
@@ -1 +0,0 @@
uid://cfyv4qt7yybib