diff --git a/.pql/changelog/ticket_idmap/2026-08.sql b/.pql/changelog/ticket_idmap/2026-08.sql new file mode 100644 index 000000000..01665c98b --- /dev/null +++ b/.pql/changelog/ticket_idmap/2026-08.sql @@ -0,0 +1 @@ +INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXF1VDVQDQ8EFGTXX787M90R', 'T-1239', '2026-08-06 14:43:24.767', '2026-08-06 14:43:24.767', NULL, 'ae5ccb58f885727882f56ce2a32c91e3', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash); diff --git a/.pql/changelog/tickets/2026-08.sql b/.pql/changelog/tickets/2026-08.sql new file mode 100644 index 000000000..60d1f03df --- /dev/null +++ b/.pql/changelog/tickets/2026-08.sql @@ -0,0 +1 @@ +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FXF1VDVQDQ8EFGTXX787M90R', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas rivers vanish at native resolution — 375 courses arrive, 0 drawn', 'Found 2026-08-06 when the visual capture resolution was raised from 960x540 to the native 3440x1440. On Ferrath (GJ820Bc) Global the wire delivers 375 river courses and the annotation layer draws NONE: ''courses=375 drawn=0'' in the view-transform readout. At 960x540 the SAME build drew them as visible strokes, so this is resolution-dependent, not a river-generation failure -- the courses are present and correct on the wire. Suspect the D-261 cull (drop a course below 15 px of on-screen length, 3x the 5 px stroke) or the water-truncation step: T-1237 computes the clipped/culled polylines ONCE on canvas adoption (set_frame) rather than per draw, so if adoption runs against a pre-layout or stale viewport the screen-length measurement is wrong for every course at once -- which matches an all-or-nothing drawn=0 rather than a partial cull. Note the scale moved the RIGHT way for visibility (46.792 km/gridunit at 960x540 vs 35.267 at 3440x1440 -- more gridunits across the body, so a river spans MORE of them), which makes a legitimate cull an unlikely explanation. Reproduce: tests/run-visual --screenshot atlas_GJ820Bc_Global and read the drawn= count.', 'backlog', 'high', NULL, 'client', 'D-261', '2026-08-06 14:43:24.765', '2026-08-06 14:43:24.765', NULL, '9597ed74aa67fb7ebeef78f60374d89c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/tests/run-visual b/tests/run-visual index 686f58bac..0be6a50f0 100755 --- a/tests/run-visual +++ b/tests/run-visual @@ -81,33 +81,52 @@ for name in c.get('scenarios', {}): # -- Helpers ------------------------------------------------------------------- +# Offscreen wrapper for EVERY capture path. +# +# Captures must never steal the desktop: this box is also the developer's own +# session. Until 2026-08-06 only the golden path even tried, via xvfb-run — +# which is not installed here, so it hit the "using visible window" fallback, +# and --screenshot/--movie never wrapped at all. Every capture grabbed focus. +# +# gamescope is preferred, for two independent reasons: +# 1. It renders on the REAL GPU. The goldens are pinned to this box's +# Mesa/AMD output (T-1121 — they do not port across rendering stacks), and +# xvfb-run would fall back to llvmpipe software rendering, shifting every +# pixel. Offscreen must not mean a different renderer. +# 2. It is the only option that lets us SET the output size. cage is also +# installed and also GPU-backed, but it is a kiosk compositor: it forces +# its client to the headless output's default 1280x720 and silently +# overrides --resolution (measured — a 960x540 request produced a 1280x720 +# PNG). A wrapper that quietly changes resolution is worse than none, +# because RESOLUTION is load-bearing here: D-255's extent inversion makes +# the shorter viewport axis span exactly one cell of the rung, so window +# size decides how much world a rung shows. cage is deliberately not used. +CAPTURE_PREFIX=() +if command -v gamescope >/dev/null 2>&1; then + CAPTURE_PREFIX=(gamescope --backend headless -W "${RESOLUTION%x*}" -H "${RESOLUTION#*x}" --) +elif command -v xvfb-run >/dev/null 2>&1; then + echo "Note: gamescope not found — falling back to xvfb-run (software GL; goldens may drift)" >&2 + CAPTURE_PREFIX=(xvfb-run -a --server-args="-screen 0 ${RESOLUTION}x24") +else + echo "Warning: no offscreen compositor (gamescope/xvfb-run) — capturing in a VISIBLE window" >&2 +fi + godot_capture() { local mode_flag="$1" # --scenario or --flow local name="$2" local output="$3" local extra_args=("${@:4}") - "$GODOT" --rendering-driver opengl3 --fixed-fps 60 --resolution "$RESOLUTION" \ + "${CAPTURE_PREFIX[@]}" "$GODOT" --rendering-driver opengl3 --fixed-fps 60 \ + --resolution "$RESOLUTION" \ --path "$ROOT/client" -s res://tests/visual_capture.gd -- \ $mode_flag "$name" --output "$output" "${extra_args[@]}" 2>&1 } +# Retained as the golden-path name; offscreen handling now lives in +# godot_capture, so both paths get it. xvfb_capture() { - local mode_flag="$1" - local name="$2" - local output="$3" - local extra_args=("${@:4}") - - # Try xvfb-run for deterministic captures - if command -v xvfb-run >/dev/null 2>&1; then - xvfb-run -a --server-args="-screen 0 ${RESOLUTION/x/x}x24" \ - "$GODOT" --rendering-driver opengl3 --fixed-fps 60 --resolution "$RESOLUTION" \ - --path "$ROOT/client" -s res://tests/visual_capture.gd -- \ - $mode_flag "$name" --output "$output" "${extra_args[@]}" 2>&1 - else - echo "Warning: xvfb-run not found — using visible window" >&2 - godot_capture "$mode_flag" "$name" "$output" "${extra_args[@]}" - fi + godot_capture "$@" } # -- Server lifecycle (live scenarios) ----------------------------------------- diff --git a/tests/visual.json b/tests/visual.json index 7403b10de..fb4f5da78 100644 --- a/tests/visual.json +++ b/tests/visual.json @@ -1,5 +1,6 @@ { - "resolution": [960, 540], + "resolution": [3440, 1440], + "_comment_resolution": "Native panel resolution (3440x1440 ultrawide, 2.39:1) as of 2026-08-06, raised from 960x540. Not cosmetic: D-255's extent inversion makes the shorter viewport axis span exactly one cell of the rung, so the viewport decides how much world a rung shows and at what cell count — a small capture is a DIFFERENT map, not a scaled one. 960x540 was also 16:9, so it never exercised the ultrawide aspect path added in 21e263d0a, which is the aspect actually used daily. Captures run offscreen under gamescope at this size (see tests/run-visual); goldens must be regenerated whenever this changes.", "settle_frames": 30, "tolerance": 5, "max_diff_pct": 0.5,