fix(client): Global rung derived its canvas from a pre-layout viewport

Eyeballed on Lendel: the Atlas opened on a Global map that was literally two
cells — one green, one blue — stretched across the window, reporting
19,598.512 km/gridunit, which is exactly half the body's circumference.

Two bugs, both of which the D-255 extent inversion turned from harmless into
fatal.

enter() fires its first request BEFORE this Control is laid out, and a
not-yet-laid-out size is not always exactly Vector2.ZERO — a few stray pixels
sailed past the `== Vector2.ZERO` guard, so the viewer asked for a 2x2
gridunit canvas and the server's 2:1 fit floored it to 2x1. That never
mattered while Global discarded the requested extent and took its cell counts
from the body's region grid; the moment the request became the canvas size, a
transient layout artefact became the map. Any viewport below a plausible
panel size is now treated as not-laid-out.

And Global was excluded from the refetch settle entirely, so a canvas born at
the wrong size could never heal however the window was resized. That
exclusion was correct when no viewport could change Global's extent. Global
now takes the SIZE refit like every other rung, but still never the pan
re-float — its canvas is whole-body and origin-anchored, and the server
ignores `center` for it.

Both have regression tests. The second asserts on _world_center rather than
_view_offset, because _recompute_canvas_transform() legitimately re-centres
the offset on any canvas adoption and would have made the test pass for the
wrong reason.

Worth noting for the class: no test written today could have caught this.
Every one supplies an explicit viewport. The bug lived entirely in the gap
between "scene loads" and "layout completes" — a seam a live launch
exercises and a unit test does not.

Also stages governance/README.md's pql-maintained record index (D-258).

Pair session with Jeroen, 2026-07-26.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-26 23:43:32 +02:00
co-authored by Claude
parent 02fe71e9f3
commit 1c45cd2ec8
3 changed files with 86 additions and 7 deletions
+51
View File
@@ -787,6 +787,57 @@ func test_resize_recenters_an_already_held_global_canvas() -> void:
).is_not_equal(offset_before)
## EYEBALL REGRESSION (pair session 2026-07-26, Lendel): the Atlas opened on a
## Global map that was literally two cells — one green, one blue — stretched
## across the window, reporting 19,598 km/gridunit, exactly half the body's
## circumference. Cause: enter() fires its first request BEFORE this Control is
## laid out, and the not-laid-out size is not always exactly ZERO, so a few
## stray pixels sailed past the `== Vector2.ZERO` guard and asked for a 2x2
## gridunit canvas. Harmless while Global ignored the requested extent and took
## its cell counts from the body's region grid; load-bearing the moment the
## D-255 extent inversion made the request the canvas size.
func test_request_extent_ignores_a_not_yet_laid_out_viewport() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
for degenerate in [Vector2.ZERO, Vector2(4.0, 4.0), Vector2(1920.0, 2.0)]:
v.size = degenerate
var extent: Vector2i = v._request_extent()
var expected: Vector2i = StepCanvasTransport.viewport_fit_extent(
StepCanvasViewer.FALLBACK_VIEWPORT_PX, v.get_held_rung()
)
assert_that(extent).override_failure_message(
"a %s viewport must fall back, not be taken literally — got %s" % [degenerate, extent]
).is_equal(expected)
## ...and the second half of the same bug: Global was excluded from the refetch
## settle entirely, so a canvas born at the wrong size could never heal however
## the window was resized. Global must take the SIZE refit (it is viewport-sized
## like every rung now) but never the pan re-float (its canvas is whole-body and
## origin-anchored — the server ignores `center` for Global), which _refloat_now()
## would betray by zeroing _view_offset.
func test_global_takes_the_size_refit_but_never_the_pan_refloat() -> void:
var v: StepCanvasViewer = _make_viewer()
add_child(v)
v.size = Vector2(1920.0, 1080.0)
v.enter({"body_id": "T1189_extent_letterbox_test_body", "body_radius_km": 6371.0}, {})
TestStepCanvasViewer._land_global_canvas(v, GJ1C_GLOBAL_EXTENT.x, GJ1C_GLOBAL_EXTENT.y)
# Drift the view far enough that a fixed rung would hard re-float.
v._view_offset = Vector2(-100_000.0, -100_000.0)
v._on_refetch_settle()
# _refloat_now() would re-centre the request on whatever world point sits
# under the viewport centre; Global's canvas is origin-anchored and the
# server ignores `center` for it, so the world centre must not move.
# (_view_offset is NOT the probe here — _recompute_canvas_transform()
# legitimately re-centres it on any canvas adoption.)
assert_that(v._world_center).override_failure_message(
"Global has no centre to re-float to — _world_center must stay at the origin"
).is_equal(Vector2.ZERO)
## Hoshe (review round 2): a narrow viewport where GJ1c's canvas exceeds
## the available width on the gridunit lattice itself (177 gridunits >
## available px after the legend column is reserved) used to make the OLD