diff --git a/.pql/changelog/ticket_history/2026-07.sql b/.pql/changelog/ticket_history/2026-07.sql index 0979bc02a..8ae335292 100644 --- a/.pql/changelog/ticket_history/2026-07.sql +++ b/.pql/changelog/ticket_history/2026-07.sql @@ -1940,3 +1940,59 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSG8P6C6WXEG3X81PVCQ833M', 'status', 'in_progress', 'review', NULL, '2026-07-25 11:38:28', '2026-07-25 11:38:28.093', '2026-07-25 11:38:28.093', NULL, '05dfe844068b316cbde4f4ba7175221f', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSCXX7E3BXT7WG0BYDB6PJA0', 'status', 'review', 'done', NULL, '2026-07-25 12:45:32', '2026-07-25 12:45:32.483', '2026-07-25 12:45:32.483', NULL, '9e2509493e56289b581edb395437e29b', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSG8P6C6WXEG3X81PVCQ833M', 'status', 'review', 'done', NULL, '2026-07-25 12:45:32', '2026-07-25 12:45:32.487', '2026-07-25 12:45:32.487', NULL, '0543405565d8175739276aa3b0fc9ffb', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRPEGNSG7VVR602MTT4VDCEW', 'description', 'PR #193 review (Tyre, finding 2): after MAX_RETRIES(30)/~110s of genuine unbroken Pending in the tiled orbital rest state, the player is permanently stuck at DERIVING TERRAIN: pan is a no-op while tiled (_maybe_refloat_window early-returns), zoom-out is floored at the canonical frame (edge-triggered reset never re-fires), and regional_screen.enter() no-ops same-body re-entry — recovery only via zooming IN across the Region boundary or visiting a different body. Pathological trigger (110s of continuous Pending) and the release-server make-atlas change makes it near-impossible on the sanctioned path, but it is a real dead-end. Fix options per the review: a manual retry affordance on the DERIVING state, or same-body re-entry re-issues tile requests when has_pending_tiles() and every tile request is exhausted-non-pending. Single-window is already fully recoverable (any pan/zoom resets the retry state) — this is tile-mode only.', 'PR #193 review (Tyre, finding 2): after MAX_RETRIES(30)/~110s of genuine unbroken Pending in the tiled orbital rest state, the player is permanently stuck at DERIVING TERRAIN: pan is a no-op while tiled (_maybe_refloat_window early-returns), zoom-out is floored at the canonical frame (edge-triggered reset never re-fires), and regional_screen.enter() no-ops same-body re-entry — recovery only via zooming IN across the Region boundary or visiting a different body. Pathological trigger (110s of continuous Pending) and the release-server make-atlas change makes it near-impossible on the sanctioned path, but it is a real dead-end. Fix options per the review: a manual retry affordance on the DERIVING state, or same-body re-entry re-issues tile requests when has_pending_tiles() and every tile request is exhausted-non-pending. Single-window is already fully recoverable (any pan/zoom resets the retry state) — this is tile-mode only. + +Refinement review (2026-07-25, post-D-255 batch): GAPS — do not implement as written. The root-cause description targets retired tiled-orbital machinery (atlas_window_tile_set.gd, has_pending_tiles()) that no longer exists; Region/Global under D-255(a) is a single flat per-rung canvas. HOWEVER the dead-end symptom plausibly survives in the new architecture: step_canvas_request.gd:47 MAX_RETRIES=30, step_canvas_viewer.gd:544 "DERIVING TERRAIN..." label, _maybe_refloat() (viewer:498) early-returns for Global, and regional_screen.enter() (screens/regional_screen.gd:38-43) no-ops on same-body re-entry guarded on body_id alone — the same-body-reentry trap the ticket cites. Re-scope: reproduce the exhausted-retry dead-end against StepCanvasRequest/StepCanvasViewer''s single-canvas retry model first, then fix what actually reproduces. Investigate-then-fix, not fix-as-described.', NULL, '2026-07-25 12:50:50', '2026-07-25 12:50:50.726', '2026-07-25 12:50:50.726', NULL, '26e8ac982e2cbe6dced1adf5c4b069a6', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRVMZYG7Z5YCKHCBS7XWJPM4', 'description', 'Observed during T-1156 wave-1 acceptance drives (2026-07-23, lead): calling AtlasWindowViewer._enter_at_rung(body, system, district_center, 32, ''District'') at a pole-edge center (Lendel district (10466,-4784), the meridian edge) produced a REGION window in one run (header ''region window - 204.800 km/cell'', center canonicalized to (10461,-4773)) while an earlier run at the same nominal center rendered the requested district window (2.048 km/cell, center uncanonicalized (10466,-4784) exactly). Suspected interplay: pole-wall clamp / canonicalize_district_center vs the entry auto-fit and _maybe_reselect_rung''s coverage walk (a pole-clamped degenerate window''s computed extent may trip is_fully_zoomed_out or the coverage ceiling). Also on file: one drive process computed a sign-flipped latitude mapping (row 0 -> +wy) unreproducible afterward — suspected stale compiled-script cache serving a revert-verification''s temporarily-flipped state (godot-cold-parse wipes exactly this class of cache). Reproduce both at the pole edge, pin with tests. Extreme-edge case: normal navigation rarely enters District directly at a pole.', 'Observed during T-1156 wave-1 acceptance drives (2026-07-23, lead): calling AtlasWindowViewer._enter_at_rung(body, system, district_center, 32, ''District'') at a pole-edge center (Lendel district (10466,-4784), the meridian edge) produced a REGION window in one run (header ''region window - 204.800 km/cell'', center canonicalized to (10461,-4773)) while an earlier run at the same nominal center rendered the requested district window (2.048 km/cell, center uncanonicalized (10466,-4784) exactly). Suspected interplay: pole-wall clamp / canonicalize_district_center vs the entry auto-fit and _maybe_reselect_rung''s coverage walk (a pole-clamped degenerate window''s computed extent may trip is_fully_zoomed_out or the coverage ceiling). Also on file: one drive process computed a sign-flipped latitude mapping (row 0 -> +wy) unreproducible afterward — suspected stale compiled-script cache serving a revert-verification''s temporarily-flipped state (godot-cold-parse wipes exactly this class of cache). Reproduce both at the pole edge, pin with tests. Extreme-edge case: normal navigation rarely enters District directly at a pole. + +Refinement review (2026-07-25, post-D-255 batch): GAPS — entirely stale as written. Every cited function (AtlasWindowViewer._enter_at_rung, canonicalize_district_center, _maybe_reselect_rung, is_fully_zoomed_out) is retired with the T-1182 rework; regional_screen.gd''s own doc confirms the orbital-mosaic-vs-window split this bug lived in is gone (one StepCanvasViewer, one path, D-255(a)). Whether a pole-edge canonicalization defect exists in the unified single-path model is an open question with no current reproduction. Do not route to an implementer as-is: needs a fresh reproduction attempt against StepCanvasViewer/regional_screen.gd near a pole-adjacent District entry; close as moot if it does not reproduce, refile with new specifics if it does.', NULL, '2026-07-25 12:50:53', '2026-07-25 12:50:53.776', '2026-07-25 12:50:53.776', NULL, 'bd0d7b4162c72fb9346ca91dcde8e79b', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'status', 'backlog', 'in_progress', NULL, '2026-07-25 12:51:01', '2026-07-25 12:51:01.110', '2026-07-25 12:51:01.110', NULL, '6cd71d2af50ff19b6274cf567537a591', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPWFRCD3M8282XJ01YWGX0', 'status', 'backlog', 'in_progress', NULL, '2026-07-25 12:51:01', '2026-07-25 12:51:01.116', '2026-07-25 12:51:01.116', NULL, '3016f301da704caa8a0542bd38fdd010', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPTY517G7ZBMPAPMMCCMK8', 'status', 'backlog', 'in_progress', NULL, '2026-07-25 12:51:01', '2026-07-25 12:51:01.117', '2026-07-25 12:51:01.117', NULL, '13d18776a2c86370f6cc8224ae7cd941', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'status', 'backlog', 'in_progress', NULL, '2026-07-25 12:51:01', '2026-07-25 12:51:01.117', '2026-07-25 12:51:01.117', NULL, '53da4420c4dc1ce87be57476a921bf4c', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'assigned_to', NULL, 'dudley', NULL, '2026-07-25 12:51:02', '2026-07-25 12:51:02.101', '2026-07-25 12:51:02.101', NULL, '622e6bd2199e976afe70fe6d49bbbec8', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPWFRCD3M8282XJ01YWGX0', 'assigned_to', NULL, 'dudley', NULL, '2026-07-25 12:51:02', '2026-07-25 12:51:02.107', '2026-07-25 12:51:02.107', NULL, '67e7ce155f021be709f36d5ce85b584b', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPTY517G7ZBMPAPMMCCMK8', 'assigned_to', NULL, 'dudley', NULL, '2026-07-25 12:51:02', '2026-07-25 12:51:02.110', '2026-07-25 12:51:02.110', NULL, '2e10cf171e26bbd5d97c51ab123d4931', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'assigned_to', NULL, 'stig', NULL, '2026-07-25 12:51:02', '2026-07-25 12:51:02.516', '2026-07-25 12:51:02.516', NULL, 'a86d711182f0b2548469cfd2f7e31e5e', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'status', 'in_progress', 'review', NULL, '2026-07-25 13:28:36', '2026-07-25 13:28:36.337', '2026-07-25 13:28:36.337', NULL, '36b5205acebfd8d1c75221b2e68513e7', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'description', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate). + +Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', NULL, '2026-07-25 13:40:57', '2026-07-25 13:40:57.886', '2026-07-25 13:40:57.886', NULL, 'bf5005a2ca802d5c8c486017843a6723', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'description', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236.', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236. + +EXPANDED (2026-07-25, lead ruling on dudley''s second scope finding): this ticket now covers RIVER labels as well as mountain-peak labels — T-1169''s implementation proved rivers have the identical missing link (the names pool is position-free, and client course polylines aren''t correlated with the server-side named attractors by construction; anchoring at the mouth ring has no matching basis). The single design decision to make FIRST, once, for both: does the name ride ON the course entry in the existing courses array (river-natural, but leaves mountains needing a second mechanism), or does a separate sparse feature-position list (positions + name-pool keys, mirroring courses/cliffs per D-255(c)) carry BOTH feature types uniformly? Preliminary lead lean: the uniform sparse list — one mechanism, one wire addition, mountains and rivers together, and course geometry stays name-free (names are annotation data, not hydrology). Decide at pickup with tyre/araminta input if contested. Server-side data all exists post-T-1169 (assignments on Layer1Output/BodyWorldState.feature_names; pool + proxy shipped). Transport + client draw + project.yaml bump remain.', NULL, '2026-07-25 13:41:04', '2026-07-25 13:41:04.273', '2026-07-25 13:41:04.273', NULL, 'be565d2ca8370ec0a49be05ce626284c', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRJD6EKPBQE08NA8VHP692W8', 'description', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward. + +Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel. + +PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again). + +Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions: + +1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing. +2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely). +3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191). +4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on. +5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture.', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward. + +Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel. + +PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again). + +Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions: + +1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing. +2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely). +3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191). +4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on. +5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture. + +Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', NULL, '2026-07-25 14:04:02', '2026-07-25 14:04:02.818', '2026-07-25 14:04:02.818', NULL, '3b7f3537280bab2c9e9ad2d09d5e0201', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'status', 'review', 'done', NULL, '2026-07-25 14:43:55', '2026-07-25 14:43:55.732', '2026-07-25 14:43:55.732', NULL, '01cf21ba37cb15ff5e77e376a2f94e36', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPWFRCD3M8282XJ01YWGX0', 'status', 'in_progress', 'review', NULL, '2026-07-25 14:59:12', '2026-07-25 14:59:12.756', '2026-07-25 14:59:12.756', NULL, 'db23cff3b1e1ddc92c8bcf4f87e63912', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'status', 'in_progress', 'review', NULL, '2026-07-25 14:59:12', '2026-07-25 14:59:12.758', '2026-07-25 14:59:12.758', NULL, '577ffe7bb2bc83c13629064601d57cbc', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FRMPTY517G7ZBMPAPMMCCMK8', 'status', 'in_progress', 'review', NULL, '2026-07-25 14:59:12', '2026-07-25 14:59:12.759', '2026-07-25 14:59:12.759', NULL, '9e0db874732ef3f049bed71b6d16e0b0', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/ticket_idmap/2026-07.sql b/.pql/changelog/ticket_idmap/2026-07.sql index 978a74209..c194e44b7 100644 --- a/.pql/changelog/ticket_idmap/2026-07.sql +++ b/.pql/changelog/ticket_idmap/2026-07.sql @@ -107,3 +107,5 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSHEPE2Q7GRKYJARH8Z8RPMR', 'T-1191', '2026-07-25 10:03:27.132', '2026-07-25 10:03:27.132', NULL, '572c6d4e5698b1f6a49e9126005e7c04', 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); INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSHHXXTS56ZBB78N2W43DD2G', 'T-1192', '2026-07-25 10:17:34.935', '2026-07-25 10:17:34.935', NULL, 'c3f3e8674c19ed92f246c8090bbcd8ea', 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); INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSHSYM96N2M9GJTQMF58E4WC', 'T-1193', '2026-07-25 10:52:37.835', '2026-07-25 10:52:37.835', NULL, '74674a181efa9bf76960713705fe6083', 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); +INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSJWSX11WV3C1XXZEV88Q3P0', 'T-1194', '2026-07-25 13:24:54.162', '2026-07-25 13:24:54.162', NULL, '684410ea827ac187639daffeb9136bee', 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); +INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'T-1195', '2026-07-25 13:27:51.669', '2026-07-25 13:27:51.669', NULL, 'a6b9411d785e64953dff256ec091e7f0', 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-07.sql b/.pql/changelog/tickets/2026-07.sql index 6effaf27b..0cbc602c2 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -2804,3 +2804,73 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat In-the-wild datapoint (2026-07-25, T-1184 eyeball session): GJ338Bd (Arbour, R=6711km) survey cell (42,29) — true position latitude -76.2S (wy=8,929,367m, region row 43) — derives with the pole-anchored baseline latitude +13.9N, producing an 18C temperate, glaciation-free lake district near the antarctic circle. Concrete demonstration of the southern-hemisphere-reads-as-northern-tropics half of the bug (the northern half clamps to +90). Conversion math used: scratchpad survey_to_district.py, the D-256 bridge formula.', 'done', 'high', 'dudley', 'server', NULL, '2026-07-24 23:30:51.889', '2026-07-25 12:45:32.482', NULL, '3e639a87ce35024288534a352e9230cf', 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); 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 ('06FSG8P6C6WXEG3X81PVCQ833M', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Lake shorelines render as hard step-edges — no transition band unlike ocean coastlines', 'Found in the T-1182 eyeball (2026-07-25): at District and Quarter rung on GJ338Bd''s big lake (DistrictPos 13652,4360), the lake shoreline is a hard rectilinear step-edge while ocean coastlines in the SAME frame show a proper multi-tone refined transition band. Two hypotheses, likely the second: (a) the T-1184 continuous filled>elevation comparison isn''t refining lake edges positionally as designed (would violate the ticket''s own ''edges refine with rung like coastlines'' requirement — verify with a shoreline-crossing sample sweep at multiple rungs); (b) PRESENTATION: ocean transition tones come from ocean_fraction_q banding, but lake cells have ocean_fraction_q=0 and Lake is a binary morphology class — so even a perfectly-refined lake edge has no gradient vocabulary to draw with. If (b), the fix is a lake-margin tone source (e.g. a filled-surface-depth band sampled at derive time, or client-side shading from the served elevation field near Lake cells — must stay inside D-255(e)''s closed input set). Determine which, then fix accordingly. Not a T-1182 defect (viewer draws what the server sends). Captures: .cache/screenshots/t1182-eyeball/rung-ladder/. Related: T-1184, D-227 amendment (4), D-255(e).', 'done', 'medium', 'dudley', 'server', NULL, '2026-07-25 07:17:23.681', '2026-07-25 12:45:32.487', NULL, 'b3f66fd935ce03512d448f63529b1b72', 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); +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 ('06FRPEGNSG7VVR602MTT4VDCEW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Tiled orbital terminal-recovery — exhausted DERIVING state has no retry path', 'PR #193 review (Tyre, finding 2): after MAX_RETRIES(30)/~110s of genuine unbroken Pending in the tiled orbital rest state, the player is permanently stuck at DERIVING TERRAIN: pan is a no-op while tiled (_maybe_refloat_window early-returns), zoom-out is floored at the canonical frame (edge-triggered reset never re-fires), and regional_screen.enter() no-ops same-body re-entry — recovery only via zooming IN across the Region boundary or visiting a different body. Pathological trigger (110s of continuous Pending) and the release-server make-atlas change makes it near-impossible on the sanctioned path, but it is a real dead-end. Fix options per the review: a manual retry affordance on the DERIVING state, or same-body re-entry re-issues tile requests when has_pending_tiles() and every tile request is exhausted-non-pending. Single-window is already fully recoverable (any pan/zoom resets the retry state) — this is tile-mode only. + +Refinement review (2026-07-25, post-D-255 batch): GAPS — do not implement as written. The root-cause description targets retired tiled-orbital machinery (atlas_window_tile_set.gd, has_pending_tiles()) that no longer exists; Region/Global under D-255(a) is a single flat per-rung canvas. HOWEVER the dead-end symptom plausibly survives in the new architecture: step_canvas_request.gd:47 MAX_RETRIES=30, step_canvas_viewer.gd:544 "DERIVING TERRAIN..." label, _maybe_refloat() (viewer:498) early-returns for Global, and regional_screen.enter() (screens/regional_screen.gd:38-43) no-ops on same-body re-entry guarded on body_id alone — the same-body-reentry trap the ticket cites. Re-scope: reproduce the exhausted-retry dead-end against StepCanvasRequest/StepCanvasViewer''s single-canvas retry model first, then fix what actually reproduces. Investigate-then-fix, not fix-as-described.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-07-22 19:07:47.532', '2026-07-25 12:50:50.726', NULL, '034c5d32daa7cd49e8a25b9f08b946d2', 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); +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 ('06FRVMZYG7Z5YCKHCBS7XWJPM4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Pole-adjacent District-rung entry can resolve to a Region window (canonicalization/reselect interplay)', 'Observed during T-1156 wave-1 acceptance drives (2026-07-23, lead): calling AtlasWindowViewer._enter_at_rung(body, system, district_center, 32, ''District'') at a pole-edge center (Lendel district (10466,-4784), the meridian edge) produced a REGION window in one run (header ''region window - 204.800 km/cell'', center canonicalized to (10461,-4773)) while an earlier run at the same nominal center rendered the requested district window (2.048 km/cell, center uncanonicalized (10466,-4784) exactly). Suspected interplay: pole-wall clamp / canonicalize_district_center vs the entry auto-fit and _maybe_reselect_rung''s coverage walk (a pole-clamped degenerate window''s computed extent may trip is_fully_zoomed_out or the coverage ceiling). Also on file: one drive process computed a sign-flipped latitude mapping (row 0 -> +wy) unreproducible afterward — suspected stale compiled-script cache serving a revert-verification''s temporarily-flipped state (godot-cold-parse wipes exactly this class of cache). Reproduce both at the pole edge, pin with tests. Extreme-edge case: normal navigation rarely enters District directly at a pole. + +Refinement review (2026-07-25, post-D-255 batch): GAPS — entirely stale as written. Every cited function (AtlasWindowViewer._enter_at_rung, canonicalize_district_center, _maybe_reselect_rung, is_fully_zoomed_out) is retired with the T-1182 rework; regional_screen.gd''s own doc confirms the orbital-mosaic-vs-window split this bug lived in is gone (one StepCanvasViewer, one path, D-255(a)). Whether a pole-edge canonicalization defect exists in the unified single-path model is an open question with no current reproduction. Do not route to an implementer as-is: needs a fresh reproduction attempt against StepCanvasViewer/regional_screen.gd near a pole-adjacent District entry; close as moot if it does not reproduce, refile with new specifics if it does.', 'backlog', 'low', NULL, 'client', 'D-226', '2026-07-23 07:15:08.545', '2026-07-25 12:50:53.776', NULL, 'b3ce996c8fed9fdf188097b7b3adf525', 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); +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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'in_progress', 'low', NULL, 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 12:51:01.110', NULL, 'ddc09c5ea7383174621d70a3ceed0a48', 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); +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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'in_progress', 'low', NULL, 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 12:51:01.116', NULL, '273ae36f28363e1efa04d94809758cee', 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); +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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'in_progress', 'high', NULL, 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 12:51:01.116', NULL, 'ed92510bbf567e13ebc7bbb0890e02e3', 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); +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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass. + +REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23. + +Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'in_progress', 'low', NULL, 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 12:51:01.117', NULL, '1aa8018dbbffdc989b1038fbffcb713b', 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); +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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate).', 'in_progress', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 12:51:02.096', NULL, 'caa5367c249a052b21eead897db4651d', 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); +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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'in_progress', 'high', 'dudley', 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 12:51:02.107', NULL, '5c10397bf02286dabd819a3b32407816', 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); +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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'in_progress', 'low', 'dudley', 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 12:51:02.109', NULL, '95bf72674f15dc0effa667f22c2f3413', 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); +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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass. + +REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23. + +Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'in_progress', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 12:51:02.515', NULL, 'ea0e9be1f27619ca19bfc4e4c46ae317', 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); +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 ('06FSJWSX11WV3C1XXZEV88Q3P0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Biome/relief stipple-density layer on the terrain build (RimWorld technique 4)', 'Follow-up from T-1175''s assess-only item (2026-07-25, stig''s write-up): a stipple/texture-density layer carrying hills/mountains/forest on top of hue, driven by the already-wire-carried vegetation and elev_q L8 planes — presentation-only, D-255(e)-legal (texture-space dithering of already-derived per-cell values, deterministically seeded per cell coordinate + value so it is stable across cache hit/miss; never invents samples between server cells). Would live as a post-process in step_canvas_terrain_layer.gd::rebuild_from_canvas()''s Image.set_pixel build. Design questions to settle at pickup: (a) stipple dots inline in the existing per-cell loop (cheap, same O(wxh) pass) vs a second overlay pass (simpler code, doubles pixel-touch cost); (b) density from vegetation class directly vs a combination with elev_q — relief hachures and forest texture are two different visual grammars in the RimWorld reference, not one slider; (c) own legend toggle (TMP/MST/VEG overlay-bar pattern) vs always-on like the elevation lightness modifier. Reference: docs/design/references/rimworld-world-map-fluency.jpg. Related: T-1175, T-1162 (vegetation patchiness fields), D-255(e).', 'backlog', 'low', NULL, 'client', NULL, '2026-07-25 13:24:54.152', '2026-07-25 13:24:54.152', NULL, '97e85dfc6cf1fbf063b981f1876ac860', 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); +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 ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Mountain-peak name labels — peak positions onto the step-canvas wire + client draw', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-25 13:27:51.667', '2026-07-25 13:27:51.667', NULL, 'd49cb1026c2f99ecea326656dadad8c3', 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); +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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass. + +REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23. + +Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'review', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 13:28:36.332', NULL, '1abbef519d9d56c1ae18798ac7d349ca', 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); +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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate). + +Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', 'in_progress', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 13:40:57.886', NULL, '74173e1f6032a438e5bc5b0129e60ff0', 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); +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 ('06FSJXFJEBK5EW5WDGN6FTS4KG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Mountain-peak name labels — peak positions onto the step-canvas wire + client draw', 'Split from T-1169 (2026-07-25, dudley''s scope finding): river labels shipped end-to-end in T-1169 (names via the FeatureNamesRequest proxy, anchored at the course mouth ring positions already on the wire), and attach_feature_names is wired into the cascade with assignments stored server-side — but ALPINE PEAK positions are not on the canvas wire anywhere, so mountain labels cannot draw. This ticket: add peak positions to the StepCanvasResponse contract (likely shape: a sparse feature list mirroring courses/cliffs per D-255(c) — positions + name-pool keys, NOT a dense plane; decide which rungs carry it — probably Global/Region/District only, labels are noise deeper), thread server encode → client protocol decode → an additive label-draw anchor in step_canvas_annotation_layer.gd reusing T-1169''s label-draw function. Wire-shape change → project.yaml version bump for the disk-cache tag (D-192/T-1183 mechanism). The server-side data already exists after T-1169 (attach_feature_names assignments on the cascade output); this is transport + presentation only. Related: T-1169, D-255(c), D-236. + +EXPANDED (2026-07-25, lead ruling on dudley''s second scope finding): this ticket now covers RIVER labels as well as mountain-peak labels — T-1169''s implementation proved rivers have the identical missing link (the names pool is position-free, and client course polylines aren''t correlated with the server-side named attractors by construction; anchoring at the mouth ring has no matching basis). The single design decision to make FIRST, once, for both: does the name ride ON the course entry in the existing courses array (river-natural, but leaves mountains needing a second mechanism), or does a separate sparse feature-position list (positions + name-pool keys, mirroring courses/cliffs per D-255(c)) carry BOTH feature types uniformly? Preliminary lead lean: the uniform sparse list — one mechanism, one wire addition, mountains and rivers together, and course geometry stays name-free (names are annotation data, not hydrology). Decide at pickup with tyre/araminta input if contested. Server-side data all exists post-T-1169 (assignments on Layer1Output/BodyWorldState.feature_names; pool + proxy shipped). Transport + client draw + project.yaml bump remain.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-25 13:27:51.667', '2026-07-25 13:41:04.273', NULL, '9a84cb5be52956aaa82cf5786535bacf', 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); +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 ('06FRJD6EKPBQE08NA8VHP692W8', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Redesign the atlas visual-capture goldens for the zoom ladder (visual_scenarios/visual_capture)', 'T-1153 broke the T-1120/T-1121 golden capture harness: client/tests/visual_scenarios.gd and visual_capture.gd call app._regional_screen._viewer.get_heightmap_texture()/show_body() — both crash against AtlasWindowViewer (regional_screen now wraps the ladder viewer; AtlasViewer is unreachable from nav). Confirmed NOT wired into tests/run-godot or the pre-push hook (no reference in either), so nothing blocks, but the 12 curated atlas_* goldens and the --atlas-matrix capture path are dead until redesigned for the ladder (capture at defined rungs/zooms instead of the fixed heightmap frame; the new canonical orbital frame is the natural replacement for the old whole-body shot). Also decide the fate of the retired AtlasViewer code these harnesses were the last live consumers of — likely deletable once T-1156 (overlay port) settles what carries forward. + +Live round 4 addendum: client/tests/test_atlas_window_overlay_draw_smoke.gd (3 scenarios -> now 2 after removing a flaky harness-sanity check: single-window draw + tile-mosaic draw, both asserting real composited pixels via SubViewport render) is a standalone gdUnit file with its own headless-skip guard, added because this redesign hadn''t landed yet. When this ticket lands, migrate its 2 scenarios into the redesigned tests/visual.json harness (folding the standalone file away) for consistency with the rest of the real-rendering test suite — see docs/DEVOPS.md''s ''Real-rendering test exception'' note for the interim rationale. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): this becomes the capture harness for the STEPPED render architecture — goldens key on (body, step) canvases, not the retired continuous-zoom frames. The T-1152/T-1153 test suites (coverage-walk, continuous-zoom seams, compute_tile_grid mosaic) retire with their code in T-1182''s change; this re-scoped harness is their replacement and the eyeball-check substrate for every step canvas. More needed than ever — keep, don''t cancel. + +PR #203 fix-round inventory (2026-07-25): the retirement is now TOTAL — AtlasViewer and its whole cluster (atlas_viewer, atlas_marker_overlay, atlas_descend_geometry, atlas_legend_panel, atlas_generation_proxy, atlas_generation_state) are deleted. visual_scenarios.gd (_setup_atlas_golden_shot, 6 of 12 atlas_GJ* z2_0/z4_0 non-fit scenarios) and visual_capture.gd (_run_atlas_matrix) now call StepCanvasViewer methods that do not exist: get_heightmap_texture(), set_view(zoom, offset), get_view_offset() — the fit-zoom shots early-return before hitting them. Ruled in PR #203: NO shim (would resurrect the retired continuous-zoom API D-255 kills); the redesign here keys captures on (body, rung) via StepCanvasViewer''s real surface (enter/_scroll_rung/rung state + the echoed extent). get_overlay_defs()/set_overlay_visible() duck-type fine and survive. Until this lands, those 6 goldens + atlas_matrix are knowingly dead (manual make targets only, gate-invisible — confirmed again). + +Harness techniques inventory addition (from the PR #204 / T-1183 restart-persistence eyeball, 2026-07-25) — proven live-rendering eyeball patterns for shared-desktop sessions: + +1. InputSwallower pattern: root-level Node (added first, low process_priority), _input()+_unhandled_input() both call get_viewport().set_input_as_handled() unconditionally — neutralizes real desktop input reaching a `-s` SceneTree driver''s real window (X11/XWayland delivers pointer events to unfocused windows under the cursor). Verify via a per-capture-window intercept counter: N>0 intercepted AND zero resulting view drift is proof; 0 intercepted proves nothing. +2. In-process navigation via viewer methods, not OS input: call the same methods the app''s own gdUnit suite exercises (StepCanvasViewer.enter()/_scroll_rung(); RegionalScreen.enter() atlas-matrix precedent in visual_capture.gd) — identical production code path, no OS injection needed (Wayland/KWin blocks synthetic X11 injection for XWayland clients entirely). +3. View-transform logging at every capture: world_center, view_offset, the drawing Node2D''s canvas position, footprint_px alongside every screenshot — turns "pixels don''t match" into a root-causable mechanism (PR #204: drift matched PAN_SPEED_PX_S*frame_time*held-frames to 0.02px, identifying unfocused edge-scroll bleed, now T-1191). +4. Full-run-restart-on-anomaly, never partial retry: any mid-sequence assertion failure discards the whole run (cold-wipe for a cold-role attempt; fresh-process-only for a revisit-role attempt) — partial retries silently corrupt the state the comparison depends on. +5. Fixed-center revisit mode: record a prior run''s actual derived centers and re-request them literally on the comparison run, bypassing cursor-anchored re-derivation — required when run 2 must land on run 1''s exact cache keys, not merely repeat the gesture. + +Harness-discipline gap found 2026-07-25 (Jeroen spotted a live orphan): agent-side kill-on-completion does not cover an agent CRASHING mid-capture (API error killed a session between server spawn and its own cleanup step — the server ran on unnoticed for ~25 min on the shared machine). Inventory addition: (6) the LEAD runs an explicit orphan sweep (ps for godot4 + settled-reach-server) at every cycle checkpoint — commit, merge, and after any agent dies abnormally — not only inside agent protocols; pr-process step 1a''s >5-min gdUnit grep does not match live-capture servers, so the sweep needs the server binary name too.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-22 09:42:47.197', '2026-07-25 14:04:02.818', NULL, '9e2fc07e90e0969bb22d9fea2bc6caa9', 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); +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 ('06FRXKFEJMDEPQ0PZ5Q862DHKG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas map-reading polish pass — river/nature presentation refinements (single large pass)', 'Jeroen, 2026-07-23, reviewing the T-1170 course captures: ''there are a bunch of graphical cleanups I would want to suggest around line thickness and tapering off to a point at the source to make the map read more fluent, but I feel map polish like that is better off as a single large pass later.'' This ticket is that pass''s collector — park presentation refinements here rather than folding them into feature batches. Seeded items: (1) river line thickness tuning across rungs (beyond the functional per-class defaults Araminta confirmed in T-1170); (2) SOURCE TAPERING — courses should taper to a point at their upstream source rather than starting at full class width (the classic cartographic river grammar; likely per-vertex width via Polygon2D strip or per-segment width ramp, since draw_polyline is single-width); (3) candidates declined-as-scope during T-1170 review: class-tinted color ramp (Araminta: width+opacity sufficient for now, hue is solvable-later). Accumulate further items via pql ticket append as they come up in captures/reviews. Scope discipline: correctness issues (ruled values not rendering, clip bugs) are NEVER parked here — they get fixed in their own batches; this pass is aesthetics only. Schedule: after the nature layer stands (post T-1156 wave 2 at the earliest), as one coherent Araminta-led pass. + +REFERENCE (Jeroen, 2026-07-23): RimWorld''s world map as the fluency benchmark — ''the rimworld maps do roads, rivers, coasts and biomes quite well'' (hex substrate explicitly NOT us; the techniques transfer, the tiling doesn''t). Extracted techniques for this pass: (1) RIVERS AS NETWORKS — visible tributary-joins-trunk convergence, subtle width variation along the run, taper-to-source (his earlier item 2), thin/consistent/restrained overall; (2) ROADS VS RIVERS disambiguated by COLOR + STRAIGHTNESS (grey chord-like vs blue winding), not width — the wave-2 road-rendering rule; (3) COASTS as a shoreline GRADIENT — beach/transition band + subtle near-shore water shading (our coastal/transition morphology class already exists; presentation only); (4) BIOME/RELIEF TEXTURE — stipple/texture density carrying hills/mountains/forest on top of hue; strong fit for us since T-1162''s vegetation-patchiness and relief fields can DRIVE stipple density (texture as data, not decoration); (5) geographic LABELS do heavy fluency lifting — that half lives in T-1169, coordinate the passes. Screenshot reference in the session record 2026-07-23. + +Reference screenshot saved in-repo: docs/design/references/rimworld-world-map-fluency.jpg (+ README note in that dir) — supersedes the ''session record'' pointer in the previous append. + +RE-SCOPED by D-255 (2026-07-24, body-map-viewer workshop): the per-vertex river tapering/width-grammar work lands on the NEW screen-space annotation layer (T-1182) via Polygon2D strips — draw_polyline cannot taper. The c1 styling-engine blocker is DISCHARGED: measurement ⑥ landed (Image.set_pixel 77.5 ns/cell flat) and the call resolved CPU-first; shaders deferred, not a precondition. This ticket now depends only on the annotation layer landing (blocked-by T-1182, edge added). Not cancelled — re-homed onto the new component, unblocked otherwise.', 'done', 'low', 'stig', 'client', 'D-226', '2026-07-23 11:48:08.469', '2026-07-25 14:43:55.732', NULL, '2a49ead618d1cf2b3c839b5af57de781', 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); +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 ('06FRMPWFRCD3M8282XJ01YWGX0', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Orbital coastline-position audit — derive_orbital_at_metres skips coast_warp_px', 'PR #192 review (Araminta, follow-up she rated real-not-hypothetical): derive_orbital_at_metres (district_profile.rs:1617-1692) samples the raw heightmap ocean mask via bilinear/bilinear_bool with NO coast_warp_px, while District/Quarter both apply coast_warp_px on the same mask (district_profile.rs:1139). The orbital rung therefore draws a STRUCTURALLY DIFFERENT coastline position than every finer rung — not a coarser sample of the same line — echoing the mandate''s own Lendel evidence clause (the D-166 amendment''s headline failure mode) at the orbital-to-district seam. Design doc §9 R1/R3 flagged the class as unresolved; §6 never gated T-1152/T-1153 on it, hence follow-up not blocker. Audit: quantify the divergence at real coasts (the wrap-warped line vs raw-bilinear line at region spacing), then either apply coast_warp_px at orbital sampling positions (cost: one warp eval per coastal cell — measure) or record an accepted-divergence rationale with Araminta''s sign-off. Related: Araminta also flagged the capture set never demonstrates Quarter-vs-District visual differentiation (flat plains read flat, sanctioned) — the audit''s verification captures should use a coastal/mountainous Quarter zoom to validate the finest rung at the same time.', 'review', 'high', 'dudley', 'server', 'D-226', '2026-07-22 15:04:44.227', '2026-07-25 14:59:12.751', NULL, 'c01d217704c926147dd7429abe0031bf', 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); +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 ('06FRV2EV5QK4BCZ1P9H85JH2YM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Natural-feature name labels on the ladder (wire attach_feature_names, populate atlas_feature_names, feature-names proxy)', 'T-1156 wave 1''s ''labels if cheap'' clause, split out after Si''s audit (2026-07-23): cheap but not free — three dormant pieces exist and nothing connects them. (1) layer1::attach_feature_names (layer1.rs:213-262) is unit-tested with zero production callers — wire it into run_layer1''s call path with name pools; (2) atlas_feature_names (systems-schema.sql:537-544) is created-but-never-populated — add the INSERT step at regen time from the D-223 names-only markers.json pools, mirroring populate_atlas_city_names (generator change: import_economics/atlas.py — watchlist/regen-db implications); (3) add a feature-names read proxy mirroring atlas_data_proxy.rs handle_city_names_request (D-236 pattern — its own message type, outside the AtlasLayerResponse ceilings) + client label draw on the ladder (the retired _draw_named_features at atlas_marker_overlay.gd:239-264 reads the pre-D-223 dict shape and would silently no-op — rewrite, don''t reactivate). + +Scope adjudication (2026-07-25, PR round, lead ruling): the "client label draw" clause is RE-SCOPED OUT of this ticket into T-1195, for rivers as well as mountains. Implementation found the ticket''s cheap-anchor premise false: the FeatureNamesRequest pool proxy is position-free (correctly mirroring CityNamesRequest per this ticket''s own instruction), and the client''s course polylines (river_course::InventedCourse) are not correlated with layer1::GeographicAttractor positions by construction — so anchoring a pool name at a mouth ring has no matching basis without a wire-carried position, the identical dependency mountains have. Rather than two ad-hoc wire changes, the one design decision (name rides the course array vs a separate sparse feature-position list mirroring courses/cliffs) lands once in T-1195 for both feature types. This ticket''s delivered scope: atlas_feature_names populated at regen (17,891 rows), the FeatureNamesRequest/Response proxy end-to-end, attach_feature_names wired into the cascade with assignments stored on Layer1Output/BodyWorldState — the three dormant pieces connected.', 'review', 'low', 'dudley', 'server', 'D-226', '2026-07-23 05:54:09.837', '2026-07-25 14:59:12.758', NULL, 'd47d4abfd3e49b670904fde30490e75a', 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); +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 ('06FRMPTY517G7ZBMPAPMMCCMK8', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Retire the legacy window_granularity u32 wire field', 'PR #192 review (Tyre): window_granularity_v2 (the WindowGranularity enum) fully shadows the legacy u32 field — the server always echoes both, and the only conceivable legacy readers are pre-T-1152 clients, which do not exist (single-repo client/server pair, no external clients). Retire the u32 field + the WINDOW_GRANULARITY_REGION_KEY sentinel machinery from AtlasLayerRequest/DistrictWindowLayer once confirmed nothing reads it: server resolve_window_granularity legacy path, client encode path, the sentinel echo, and the five-touch-point key components that still carry the legacy value alongside the enum. Scheduled in the D-226 amendment refinement-semantics note (PR #192). Do AFTER T-1153''s follow-ups settle — no urgency, the dual-echo is cheap; the win is contract simplicity.', 'review', 'low', 'dudley', 'server', NULL, '2026-07-22 15:04:31.528', '2026-07-25 14:59:12.759', NULL, 'f4527851910bdab46dcee5569d9a06c1', 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);