One commit for two tickets whose changes share the bridge/plugin
plumbing files. T-1169 connects the three dormant feature-name pieces:
atlas_feature_names populated at regen (17,891 rows — 15,190 mountain,
2,701 river — via populate_atlas_feature_names mirroring the city-names
importer; systems.db regenerated, stamp fresh), attach_feature_names
wired into the cascade's Topography block with name pools threaded
DB-free through AnalyzeBody (D-225 pattern) and assignments stored on
Layer1Output/BodyWorldState for future consumers, and a
FeatureNamesRequest/Response read proxy as the bridge's 7th tagged
envelope (D-236 pattern, both SimBridge impls). Client label DRAW is
deliberately NOT here — implementation proved both river and mountain
labels need a wire-carried position (the pool is position-free; course
polylines aren't correlated with the named attractors by construction) —
deferred to T-1195's single design pass. cascade_layer1 golden re-pinned
(additive feature_names field).
T-1159 retires the legacy u32 granularity field fully shadowed by
window_granularity_v2: AtlasLayerRequest.window_granularity,
DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and
resolve_window_granularity are gone server-side; client encode paths and
the caller-less atlas_window_cache legacy key component dropped;
msgpack fixtures regenerated; the T-1150 aliasing regression test now
drives through the surviving enum field. The district_window carrier
itself survives byte-compatible per D-255(c).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five findings from the araminta+hoshe round. Tapering now fires only on
a TRUE upstream source: the course's first raw world point is tested
against the canvas's own world bounds (conservative 1m epsilon) —
exact detection because the server crop keeps one point beyond the
window (layer_proxy crop_course_to_window lo = first_in-1, contract
documented), so crop passthroughs draw the old flat full-width cut and
never a false headwater. The averaged-normal joint is replaced by a
real mitre (half_w/cos(theta/2) recovered trig-free via the bisector
normal), clamped by a 2x mitre limit AND 0.45x the shorter adjacent
segment — restoring true perpendicular width at bends (the 29% pinch at
confluences is gone) and preventing the hairpin bowtie; the winding doc
now states the actual bounded guarantee. Antialiasing restored via the
hybrid: only the varying-width taper head draws as a ribbon; the
constant-width ~85% of every course keeps the original antialiased
draw_polyline (byte-identical for untapered courses), split at an
interpolated arc-length point sharing position and width — junction
capture evidence in .cache/screenshots/t1175-fix-round/. Flat-fill
single-element color array. Suite 24 -> 48 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The single large polish pass Jeroen requested off the T-1170 captures,
benchmarked against RimWorld's world-map fluency. Courses now draw as
source-tapered ribbons: per-vertex width ramps from a hairline at the
upstream source to full class width over 15% of the course's arc length
(arc-length parameterized, not vertex-indexed, so point density doesn't
change the read), built as one draw_polygon ribbon with mitred joins.
Class width table retuned 0.9/1.4/2.2 -> 0.6/1.2/2.4: a clean ~2x
per-class ladder so a tributary-joins-trunk confluence reads as a join,
trunk held at its visually-proven weight, stream thinner per the
benchmark's thin/consistent/restrained grammar. Opacity untouched
(Araminta's T-1170 ruling stands). Coast-gradient item resolved as
already-correct (the coastal transition zones + elevation lightness
render the shoreline band; capture-verified) — no wire change. Stipple
assessment filed as T-1194. Eight new taper-geometry tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fractional fit branch is deleted, resolving both review findings at
the root: tyre showed its comments cited D-255 for an exception the
record does not contain (the language came from the lead's ticket text,
not governance), and hoshe showed it returned sub-1x for a canvas
exceeding the viewport on one axis. Replacement: fit_scale_ratio()
chooses the largest integer pixels-per-gridunit R fitting both
legend-reserved axes, floored at 1 (over-viewport draws native and
crops like every fixed rung) — the fine-grained integer lattice (GJ1c
1080p -> 9px/gu = 1593x792, ~98% width; 4K -> 20) that makes the
fractional hatch unnecessary. center_offset() floors to whole pixels
(half-pixel centering would blur the texel grid). Doc comments cite the
real sanction (D-255 amendment 2026-07-25, this branch). Legend column
constant is now canonical in transport, read directly by the legend
(was an independently-typed literal); its test asserts real geometry.
New regression test proves _global_body_extent clears on body switch
and never caps another body's requests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lake edges rendered as hard step-edges while ocean coasts got multi-tone
transition bands: every coastal-transition morphology gate keys on
ocean_fraction_q, definitionally 0 inside a lake basin (hypothesis (b)
of the ticket; (a) disproven first — a shoreline-crossing sweep at
2048/512/128m plus a 10m fine sweep all land on the same continuous
crossing, so positional refinement was never broken). New
DistrictProfile.lake_margin_q (0-100 settled-hydrology depth band, from
the same bilinear filled/elevation pair the lake test already samples;
ceiling calibrated just above the observed p90 depth on GJ338Bd's 5,043
flooded cells), threaded through both derive paths onto
EncodedStepCanvas (serde-default for shape tolerance) and down the
client: protocol decode, terrain-layer plane, colorize shades Lake cells
by depth band instead of elev_q (bedrock-under-water, the wrong signal).
project.yaml 0.4.0 -> 0.4.1: the new wire field must invalidate the
client disk cache via its version tag (T-1183's D-192 mechanism).
Acceptance gates green with the new field (lossless round-trip,
cache-hit==cache-miss, every rung).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two client fixes for the Atlas frames Jeroen flagged. Region rung no
longer requests more planet than exists: cap_extent_to_body() caps the
viewport-fit extent at the body's own region grid (the Global canvas
extent echo — cols=regions_per_equator, rows=cols/2), matched by gridunit
SPACING not rung name, applied before the request/cache key is built;
cold-start scroll-before-Global-echo requests uncapped and lets the
server clamp (documented). Kills both the side-by-side continent repeat
and the past-the-pole stripe smear.
Global opener now fit-scales and centers through one shared letterbox
mechanism (center_offset/integer_fit_scale/fit_scale, 75%-coverage
integer-vs-fractional decision, NEAREST already forced on orbital rungs
per D-255's escape hatch) also used for the Region cap's letterbox
remainder. Legend column reserved before fitting — pinned to the legend's
own width by a cross-constant test, never overlapping the canvas.
Also fixes a latent crash: NOTIFICATION_RESIZED fires mid-_ready() before
children exist; null guard in the resize path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Atomic index writes (tmp+rename), orphan-payload reconciliation sweep
folded into the background sweep, payload shape guard mirroring the
protocol's own width/height discriminator, size_bytes via get_position()
instead of a full payload re-read, and a 64-bit SHA-256 payload filename
(String.hash()'s 31-bit space made a silent wrong-map filename collision
a ~1-in-16k event per cap-full body; migration self-heals via the orphan
sweep). Tier-3 class doc rewritten to name the real D-253 seam — the
glaciation/flooded_q wire fields exist but carry static values and no
staleness signal crosses the wire; T-1190 tracks threading a wire TTL
into put(sim_ttl_sec) when the driving clock lands — replacing the false
'no sim-state wire field exists' premise. Four new tests plus a
_stub_canvas fixture helper centralizing the canvas shape contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
step_canvas_disk_cache.gd: the Tier 2/3 store per D-255(d) and the
round-2 three-tier spec. Payload is the WIRE, pre-decode — store_var/
get_var round-trips the PNG-encoded PackedByteArrays natively, and
Image.load_png_from_buffer never runs in this file. One composite key
shared verbatim with Tier 1 (hash filenames for filesystem safety);
per-body index.json with malformed-index recovery (rebuild-or-discard,
never crash).
Three independent eviction mechanisms, exactly as ruled: rung-0 Global
carries a retention floor no sweep touches (now also threaded into
Tier 1 per the T-1182 handoff); Tier 2 geometry is byte-valid forever
and evicts only by time-since-last-visit (14d starting tunable, on
body-open) and LRU byte budget (256 MiB/body, 5-min coarse timer) —
two separate sweeps; Tier 3 sim-state TTL is wired and tested but has
no production caller yet (no sim-state field exists on
EncodedStepCanvas — the D-253 stub inheritance, documented).
Hardening per D-255(d), both mandatory: per-body deep-rung cap
(512 Block+Chunk entries, enforced synchronously in put(), floor- and
budget-independent — the ticket sanctions count-or-quota; count chosen
as the direct D-226(d) information-content proxy) and a schema/version
tag on every entry (project.yaml version via the existing
loading_screen line-scan idiom — D-192 co-ship makes the client
version the wire-schema version; exact-inequality mismatch = miss +
drop, NEVER decode, checked in both has() and get_canvas()).
Integration: request_now() checks Tier 2 on a Tier-1 miss (synchronous
promote), Ready responses write through to both tiers, Pending never
writes; viewer runs the visit sweep on body-open + the background
sweep on a 5-min timer.
30 new disk-cache tests + 7 request-integration + 3 sweep-wiring tests
(restart persistence, sweep independence both directions, cap
semantics, version-mismatch never-decode, corrupt-index recovery).
Full client suite 3,440/3,440, 0 orphans; cold-parse clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tyre finding: the orphaned AtlasViewer cluster is now actually deleted
(atlas_viewer, atlas_marker_overlay, atlas_descend_geometry,
atlas_legend_panel — a sixth orphan found beyond the review list —
atlas_generation_proxy, atlas_generation_state; ~2,497 lines), with
reachability re-verified across preload/class_name/res:// strings,
every .tscn, and the standalone companion app. Test suites triaged,
not blanket-deleted: 5 pure AtlasOverlayColors tests relocated into
test_atlas_window_colors, the live type-identity regression guard
relocated into test_step_canvas_viewer, dead coverage deleted. A real
harness gap surfaced during diligence and RULED, not patched:
visual_scenarios/visual_capture golden shots call retired
continuous-zoom API — no shim (would resurrect what D-255 kills);
inventory recorded on re-scoped T-1157 (gate-invisible, manual
targets only).
Hoshe finding 1: decode_png_field now detects the [Error,
PackedByteArray] bin-shape from messagepack.gd explicitly — a genuine
msgpack bin payload decodes correctly instead of silently collapsing
to [0,0]; test built from a real round-tripped bin decode.
Hoshe finding 2: the hard zoom-out reset is wired — ascend at rung 0
with a drifted view triggers _reset_to_global (the restored HARD
condition), behavioral tests through the real input path.
Notes folded: refloat + edge-scroll test coverage, legend smoke suite,
Vector2i narrowing-safety comment with computed headroom.
gdlint clean on touched files; full client suite 3364/3364.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two-layer client rebuild per D-255(a)(b)(e), replacing the
_canvas.scale continuous-zoom model with one viewer, one path, all six
rungs:
- step_canvas_protocol.gd: StepCanvasRequest/Response codec against
the T-1181 wire contract — incl. the discovered png_bytes subtlety
(rmp_serde without serde_bytes emits a msgpack int-array, not bin;
decode repacks via PackedByteArray before load_png_from_buffer) and
the extent-echo rule (read the server-clamped extent, never assume
the requested one).
- step_canvas/ component: transport (six-rung ladder, cursor-anchored
scroll steps, edge-scroll/WASD pan with re-request on edge crossing,
hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize
per the c1 measured ruling, texture.update reuse on step-cross,
NEAREST coarse / LINEAR fine per rung), unscaled screen-space
annotation sibling (courses + settlement markers at literal px),
in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath),
request lifecycle (pending retry, staleness gate, extent echo).
- Full _canvas.scale retirement in the same change: the zoom-scaled
canvas model, the _zs compensation family, select_rung /
MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window
two-path split, _view_zoom/_canonical_fit_zoom — 10 source files
deleted; their 14 test suites deleted with them (T-1157 dead-goldens
rule; replacement visual-capture coverage is re-scoped T-1157).
- Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU
shape (the ticket's named file atlas_window_tile_set.gd was the
retiring orchestrator; the real LRU shape lives in
atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay
colors, legend/overlay-bar chrome, AtlasViewer descend geometry.
Determinism boundary per D-255(e): the client interpolates only within
the closed server-supplied input set. 7 new gdUnit suites (164 cases)
incl. a real extent-echo bug caught by its own test during
implementation. Full client suite green (exit 0) with the live-gated
suites running against a worktree server build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two separable findings from the live A/B (real driver): (1) the
original zero-ring report was a viewport-framing crop — the ring
fired all along at screen (1755,-191), above the frame under the
COVER fit; recentering via set_view proved the path live. (2) The
real bug once in-frame: at Quarter fit zoom the compensated ring
radius (5.0/7.5=0.667 canvas) fell below the floored stroke (1.0
canvas) and draw_arc's stroke filled its own hole — a solid blob,
not a ring. Bracket: 1x stroke=blob, 1.5x=hollow recovers, 2x=clean.
Fix: zoom_compensated_ring_radius() floors the radius at 2x the
paired stroke, wired through _zs_ring_radius() for both ring and
halo arcs; verified live producing a clean hollow double-ring. The
third member of the Godot sub-canvas-unit rasterizer family (width
floor, stroke-vs-width sites, now radius-vs-stroke) — all recorded
for the T-1176 render-mechanism discussion. 10 pin tests in the
stroke-width suite; revert-verified (floor drop -> named failures);
full client suite 3956/3956; gdlint clean.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two _CountingNatureOverlay draw-spy tests (the PR #196 pattern):
window arrival and rung-swap arrival both advance the nature
overlay's draw count past baseline. Two isolation bugs caught before
reporting: (1) _on_window_ready has a second pre-existing redraw path
via _fit_and_center -> _apply_transform that fires on first arrivals
and masks a sabotaged line 507 — isolated by setting _user_adjusted
(the real pan/zoom guard, a reachable state); (2) simulating the swap
via _enter_at_rung resets _awaiting_first_window and takes the same
masked branch — the real production trigger is _maybe_reselect_rung's
request path, so the test drives request_now directly. Revert-
verified: dropping the queue_redraw line fails exactly both tests by
name; 82/82 restored. Viewer file itself zero net diff.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Her audit of the course captures found a uniform 1px alpha-255 line
where trunk should draw 2.2 screen px. Live A/B bracket (20/1.5/1.1/
0.6 forced widths + draw_line-vs-draw_polyline control) localized the
cause: Godot's line rasterizer floors stroke widths below ~1.0 canvas
units to hairline — the _zs arithmetic was correct (1.4/3.75=0.373
round-trips exactly); the value died at the driver. Fix: zoom_
compensated_stroke_width() (the _zs divide maxf'd at 1.0) via a
_zs_stroke() wrapper on EVERY stroke-width site (course polyline,
skeleton chords, mouth-ring arcs, basin boundary, attractor outlines
— same latent class everywhere even where not yet visible); radius
args proven unaffected and left on _zs. Honest degradation direction
documented: at high zoom effective width grows rather than pinning.
Class question resolved with printed ground truth: the original
window is genuinely single-drawable-class (trunk course degenerate at
1 point); a confluence window confirms real multi-class rendering.
The drive now prints per-course class/width/effective-px tables every
run. New 14-test stroke-width suite (own file, line-cap split) pins
the exact floor-engagement numbers and the PR#195-shape effective-
width >= table-value invariant; revert-verified (floor drop -> 4
named failures). Full client suite 3942/3942; gdlint clean.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_draw() splits into two independent gates: the Layer-1-gated skeleton
path (Region chords, clip retained) and the NEW DistrictWindowLayer-
gated course path — build_course_render_plan() (pure, render-free-
testable) consumed by draw_polyline with _zs-compensated widths and
opacities from the Araminta revisit tables; mouth double-rings at
Mouth termini only (EdgeDrain/ContinuesBeyondWindow/None: three
meanings, one presentation — draw to last point, stop, documented);
zero water clip on the course path by construction (courses carry
rung-consistent termini). CourseTerminus wire vocabulary kept re-
pointable pending A2's real serde names; synthetic Ruling-3h fixtures
mean the suites need zero changes when the server payload lands. Real
gap found and fixed: window arrival never redrew the nature overlay
after the first fit (one line in _on_window_ready — courses would
miss every window swap post-pan). Water-clip header rewritten to
RESTRUCTURED status (retired on course rungs; permanent at Region
until Region goes windowed, T-1143 ruling 2). Revert-verified
(visibility-gate bypass -> 4 named failures). geometry-nature
112/112, nature-overlay 58/58, viewer 78/78, zero collateral; full
sweep 3928/3928 after the full-import bootstrap; gdlint clean (viewer
1016->1017, pre-existing overage rides T-1158).
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
B1 (Ruling 5c): nature block split out of atlas_window_geometry.gd
(954/1000 cap pressure) into atlas_window_geometry_nature.gd; RIVER_
CLASS_VISIBLE_BY_RUNG replaced by SKELETON_CLASS_VISIBLE_BY_RUNG
(Region-only now) + COURSE_CLASS_VISIBLE_BY_RUNG (District trunk+
tributary; Quarter all three — the pre-announced Quarter-rivers-
return) with width/opacity companion tables as Araminta's single
revisit point; deliberately opposite unknown-tag fallbacks per reader
(skeleton->full, course->empty), documented.
B2 (Ruling 5a): Region dot-scatter upgraded to connected chords via
river_downstream — D8 direction decode (0-7 into drainage.rs's
(row,col) delta table, antimeridian wrap-aware), sentinel chain ends
(MOUTH=8 ring-on-land, EDGE_DRAIN=9 no ring, TERMINAL=10 reserved,
decodes like EDGE_DRAIN so the future endorheic server needs no
client change). Pure build_skeleton_chords() split from drawing for
render-free testability. Chord clip rule (3g pick): segment clips if
either endpoint OR midpoint is drawn water — three-point catches both
narrow-inlet and long-chord failure modes at one extra lookup;
documented. Self-caught during build: first draft misdecoded the
pointer as a river_cells INDEX; rewired to direction decode against
A1's real convention before leaving the branch. Dual revert-verified
(direction sabotage -> 6 named failures incl. the chain-threading
pin; midpoint-drop -> exactly the 1 named clip test). Suites:
geometry-nature 86/86, geometry 130/130, nature-overlay 24/24; full
sweep 3892 with only the 6 known pre-existing garment/gait failures
untouched by this batch; gdlint clean.
Tickets: T-1170
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live round: the first clip removed only 133 pixels. Root cause (dossier-
traced): resolve_morphology_zone wrapped the QUERY toward a tile's raw
canonical center — the inverse of the painter's draw_col =
nearest_wrap_image(tile_center, held_center) — so seam tiles tested
containment against the wrong wrap-image and read real-but-wrong-
location land cells. Fix mirrors the painter exactly: wrap the tile's
own center toward held_center, test the (already held-wrapped) query
against that. Index math itself was confirmed correct end-to-end and
is now factored into shared AtlasWindowGeometry.cell_index_for_local_
offset() (full painter unification not applicable — the painter only
iterates forward, never reverse-looks-up; documented). Post-fix: 1082
traced positions cross-checked against actual painted pixels, 0 real
mismatches; regenerated captures show every dot/mouth on land. The one
pre-existing test encoding the buggy direction as correct was replaced
by positive+negative wrap-semantics tests (the negative one is the
reliable revert discriminator) plus a wire-accurate seam-tile fixture
with an honestly-documented proof limit (a single-tile fixture cannot
distinguish the wrap directions; the multi-tile scan tests can).
Also: two harness traps found and fixed in the lead's drive scratch —
canvas_items stretch factor (root.size now matches the 1920x1080 base
viewport) and mid-arrival snapshots (explicit is_fully_arrived wait;
the round-1 faint result was partly a 4-of-6-tiles capture). 46/46 +
50/50 + five sibling suites regression-free; gdlint clean.
Tickets: T-1172
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Jeroen's hands-on report: rivers continuing under the ocean. Tyre's
ruling implemented: the skeleton is rung-independent, the drawn coast
is rung-indexed (warp cutoff admits more octaves per rung), so
reconciliation is a presentation-frame operation — a draw-time clip
against the SAME per-cell morphology verdict the terrain painter used,
at the rung on screen. New pure module atlas_window_water_clip.gd:
cell resolution across both paths (single-window direct; tile mode
selects the containing tile by each tile's OWN echoed n with nearest-
wrap re-expression against that tile's canonical center — response-is-
source-of-truth + wrap discipline reused, not reinvented). Strict drop
(no snap); offshore mouths suppressed (return with real termini in
T-1170 — retirement markers at the clip sites); basins untouched;
fail-open wherever no composite data has arrived (the clip refines
presentation, never gates data). The _pos split feeds the SAME wrap-
resolved district to both draw position and clip test so they can
never disagree about the wrap image. 49 new tests incl. antimeridian
and mid-progressive-arrival fail-open; revert-verified with precise
attribution (breaking water detection fails exactly the 3 water tests,
fail-open/land tests stay green); smoke-suite stub crash under a real
driver caught and fixed (headless skip-gating masked it). 7 suites
regression-free; gdlint clean.
Tickets: T-1172
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RVR-on-by-default is the single most player-visible behavior wave 1
ships (rivers appear with no toggle hunt) and was only exercised by
live captures; gen_basins' default was pinned incidentally inside the
toggle-redraw spy test and gen_attractors not at all. One named test
now asserts all three fresh-construction defaults per Araminta's
ruling (RVR on, BAS off, ATR off). Revert-verified: flipping the
gen_rivers init line fails exactly this test by name (1 failure total
in the 78-test suite). Suite 78/78; gdlint clean.
Tickets: T-1156
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
I1: _draw_attractor_shape's three stroke-width args (Confluence arc,
Coastal/NaturalHarbor arc, Oasis spokes) were raw screen-space literals
— Godot multiplies stroke widths by canvas scale exactly like radii, so
at the Region orbital fit zoom the outlines rasterized at ~0.01px, the
identical sub-pixel class the dot/ring compensation fixed, missed on
glyph internals (and attractors are Region-only — precisely where it
bites). Widths now arrive pre-compensated via a px_w param, keeping the
primitive pure. Regression pin: a source-scan test asserting no
draw_arc/draw_line in the function carries a bare numeric width (the
draw-smoke suite documents its own vacuous-pass mode, so source-scan is
the environment-independent gate); revert-verified by name. I2: D-226
visibility-direction sentence — Araminta's fade-down inversion recorded
as pre-T-1170 with its single revisit point named. I3: class-header
call-site claim corrected (enter() funnels through _enter_at_rung).
Tickets: T-1156
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
At 1.6px/60% the trunk dot under the mouth ring read as 'mouth glyph
over plain terrain' — invisible without knowing where to look,
underselling the ruling's own 'a major river crosses near here'
intent. 2.0px/80% preserves the fade-down ladder vs Region's
2.2px/100% without reading as accidentally-erased. Doc comment
records the revision provenance.
Tickets: T-1156
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live round 2 (the real bug): every nature-overlay marker size was a raw
screen-space constant drawn inside _canvas, whose scale IS view_zoom —
at Lendel's orbital fit zoom (0.0063) a 2.2px trunk dot rendered at
~0.014px, invisible; the same code at District's 3.75 zoom produced the
correctly-visible mouth ring, which is why one capture worked and the
headline rung didn't. Fixed via AtlasWindowGeometry.zoom_compensated_
size() (pure, floor-guarded) wired through every radius/line-width;
basin FILL points are positions and correctly stay unscaled. Suspect
tile-mode-rung-detection was ruled out live (granularity_v2=Region
confirmed in tile mode) but pinned with a named regression test anyway.
+8 pure-function tests incl. a numeric pin of the pre-fix magnitude
(<0.02px at orbital zoom); revert-verified by name. Draw-smoke suite
documented as supplementary (the shared SubViewport background harness
can pass vacuously under X11 BadMatch — the pure suite is the gate).
Live round 3 (drive-script bug, no product change): the lead's scratch
drive passed the button LABEL to set_overlay_visible() and the unknown-
id guard silently no-op'd — but the chase banked a real pin:
test_set_overlay_visible_gen_basins_flips_gate_and_redraws_nature_
overlay (draw-counting spy per the cold-start precedent; is_queued_for_
redraw does not exist in this build). Revert-verified.
Basins verified live: 7 Lendel watershed boundaries render at the
ruling's alphas. Suites: viewer 76/76, geometry-nature 42/42, nature-
overlay 22/22, zoom-ladder 50/50, no regressions across the cluster.
Tickets: T-1156
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New AtlasWindowNatureOverlay (Node2D on the viewer canvas, above
terrain): self-connects to the shared atlas_layers_received broadcast
(the established one-signal-N-consumers shape) and consumes the whole-
body layer1 skeleton per Tyre's carrier ruling — no windowed wire
touched. Coordinate chain layer1_pixel_to_world_m -> world_m_to_
district -> canvas-local lives in atlas_window_geometry as pure tested
functions; vertical convention (row 0 = North pole, wy increases
south) verified against the server's own pixel_to_world_m, pinned by
pole tests and revert-verified (sign flip fails them by name).
Araminta's per-rung presentation table implemented exactly: Region
full skeleton (radii 0.9/1.4/2.2, confluence 3.5, mouth double-ring),
District trunk-only 1.6px at 60% with mouths at full landmark styling,
Quarter off until T-1170 course invention; retired palette reused
verbatim; RVR on / BAS off / ATR off defaults; missing river_class
falls back to trunk. 58 new tests (34 geometry, 22 lifecycle, 2 real-
driver draw smoke actually rendered); 11 existing atlas suites
regression-free. atlas_window_viewer.gd runs 16 lines past the
advisory 1000-line cap on trivial wiring — accepted, rides T-1158's
decomposition.
Tickets: T-1156
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Araminta's ruling (PR #192 follow-up): filter keyed on rung IDENTITY via
the window's own echoed granularity_v2 — Region (incl. the orbital tile
mosaic, whose tiles are all Region-rung requests) samples NEAREST because
GPU bilinear at 204.8 km/cell reads as smoothing-over-absence; District/
Quarter keep LINEAR where cell density earns the blend. One shared helper
(_filter_for_granularity_v2) at both draw call sites; unknown/missing
wire tags fall back to LINEAR (never trusted into NEAREST). COMPOSITE_
SMOOTH survives as the independent compile-time pipeline axis — the
two-axes split is documented in the file header. Washes/border fades
untouched per the ruling. Focused suite 44/44; revert-verified (helper
hardcoded LINEAR -> exactly the three Region-NEAREST tests fail).
Tickets: T-1161
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins on_response()'s guard ordering: body_id check BEFORE the status
branch, so another body's cold Pending can never burn one of our 30
retries or reschedule our timer (multi-body browsing / shared-broadcast
tile fan-out). Revert-verified: reordering the guards makes this test
fail; final own-body Ready assertion proves the request is genuinely
untouched, not just un-retried. 44/44 in the file.
Tickets: T-1163
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tyre's APPROVE items (T-1163): record the two-legal-wire-shapes-for-one-
logical-state protocol invariant (whole-response Pending AND Ready+null
district_window both mean 're-poll'; only NotFound/Error are terminal) as
a D-226 note under the T-1124 §4 amendment area, so a future server
refactor of the asymmetry must migrate every consumer in the same change.
Drop the duplicated 5-line stagger comment in atlas_window_tile_set.gd.
Follow-up tickets filed on main: T-1164 (tiled terminal-recovery),
T-1165 (queue_redraw edge root-cause), T-1166 (cold-launch test tier).
Tickets: T-1163
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The true cause of the black first launch, server-confirmed after four
disproven theories: a stone-cold body answers the FIRST window request
with a whole-response status 'Pending' (only the whole-body cache-hit
branch sets Ready), and on_response()'s very first check — status !=
Ready -> return — swallowed it before the retry machinery could run.
retries stayed 0 forever; the DERIVING state never resolved. Second
connections worked by luck (the first request warms the whole-body
cache, so they read Ready and take the healthy path). Both tile fan-out
AND single-window first-descents were affected — one shared function,
one fix: branch on the outer status first (the atlas_generation_proxy
reference shape): Pending -> retry, Ready -> existing null-window retry,
NotFound/Error -> give up immediately (the principled give-up policy,
replacing the elapsed-retries ceiling).
Hardening in the same round: deterministic exponential backoff (0.5s
doubling, 4s cap) + per-tile stagger (0.1s * index — six tiles retry at
0.5/0.6/0.7/0.8/0.9/1.0s, strictly-increasing asserted, not jittered);
MAX_RETRIES 20->30 (~110s horizon under backoff).
Regressions are wire-accurate by construction: whole-response Pending
(body_id only, no center/n/granularity — verified against the server's
own response construction) delivered through the REAL fan-out
(tile_set._on_atlas_layers_received, never tile.on_response directly),
mirrored at single-window level. Two first-draft tests that passed with
the bug reverted were caught and strengthened before reporting; every
fix and hardening piece revert-verified independently (7+1 failing
tests without them).
Item 1: while zero tiles have arrived, the viewer draws a centered
screen-space 'DERIVING TERRAIN…' label (text_dim role, no new hue),
dropping the instant the first tile lands — a cold wait now reads as
loading, not broken. New has_any_tile_arrived() predicate (distinct
from has_pending_tiles(): both true mid-arrival, tested exactly there).
Item 2: legend re-fit root-caused empirically, two plausible fixes
disproven by trace before the real one: a manually-positioned Control's
size NEVER tracks a shrinking minimum in this parenting shape, and
RichTextLabel.fit_content reports degenerate minimums until laid out at
real width once — so reset must be DEFERRED and run after refill, not
inside clear(). ImplantPanel.reset_to_content_size() (call_deferred),
wired into both legend refresh()es. The load-bearing test compares
size.y to get_minimum_size().y — a size-to-size comparison passed
trivially with both numbers equally stuck (caught on first draft).
Item 3: make atlas now builds the RELEASE server and passes
SR_SERVER_BIN (a cold DEBUG server delivers zero tiles for >10s on a
new body — live-measured — vs 210ms warm; release serves cold in well
under a second). atlas_standalone._server_binary_path() honors the env
override per the SR_PORT two-tier precedent, debug path unchanged
when unset.
All fixes revert-verified; nine suites green collateral-checked;
gdlint clean.
Jeroen hit a black mosaic zooming into a body from a fresh make-atlas
spawn. Live diagnosis showed all six tiles held with colored textures
and no repaint; the original line-specific diagnosis (unpaired
queue_redraw in _on_tile_ready) turned out WRONG — the pairing already
existed (lead's truncated grep misread the function; Stig verified via
git log -p before acting). Rather than chase the exact dropped signal
edge, _process() now self-heals: both viewer and overlay redraw every
frame while the tile set has pending tiles (has_pending_tiles(), new) —
a strict superset that closes the black regardless of which edge drops,
pinned by a _draw()-counting real-subclass spy test.
Legend stacking root cause found by trace, not guess: ImplantApp.
_on_screen_changed() re-runs enter() unconditionally on repeat
same-screen pushes — each re-entry tore down and rebuilt all six tile
requests (the round-6 orphaning fingerprint via a new trigger) and
stacked another legend (~10 deep, full-height dark panel). Fixed both
ends: ImplantPanel.clear() frees immediately (same-frame re-entrant
refresh can never observe stale children — protects every implant app),
and RegionalScreen.enter() no-ops for the same body (different body
still re-enters fresh). The load-bearing regression asserts an ARRIVED
TILE'S DATA survives a repeat push — node identity would not catch the
teardown (the tile-set Node is a fixed field; only its internals reset).
Cold-start UX: pending tiles now draw the single-window path's
COLOR_BORDER_FADE wash instead of raw background — a deriving mosaic
reads as loading, not broken.
Suites green (zoom_ladder 48, viewer 74, tile_set 20, overlay 30,
overlays 46 + 2 new files), gdlint clean, revert-verified throughout.
All Hoshe/Araminta findings addressed (Tyre approved outright), none
retracted — plus a Dudley stop-and-flag discovery that improved on the
asked-for fix:
- Legend 100x lie (Araminta, blocking): subtitle computed via
spacing_for_rung() and refreshed at all three _held_granularity_v2
write sites. Region test asserts 204.800 km/cell; the District
direction needed a stale-header-aware helper — a District-only test
spuriously passes against the old literal by coincidence.
- Region coalescing coverage (Hoshe 1): both directions tested
(Region-vs-District separate slots; Region-vs-Region coalesces).
- Clamp boundary tests + dangling citations (Hoshe 2): writing the
requested halving-loop-fires test surfaced that the loop is PROVABLY
UNREACHABLE at current constants (per-axis clamp forecloses it —
brute-forced independently on both server and client sides). Ruling:
the loop stays as defensive code; the test became a property sweep
pinning both the wire-cap invariant and the loop's no-op status (a
future constant change breaks it loudly); doc comments on both sides
drop the load-bearing framing and state the truth; the old client
mirror test that claimed the loop fires (passing on the per-axis
clamp alone) is replaced the same way. Client citations now name the
real server tests verbatim.
- Governance (Tyre): D-226 amendment note — progressive cross-rung
refinement EXTENDS T-1124 §4 (not supersedes); legacy u32 field
scheduled for retirement (T-1159).
Server: 1818 lib tests green, clippy/fmt clean. Client: zoom_ladder
48/48, window_request 26/26, viewer 74/74; gdlint clean. Every fix
revert-verified.
The round-5 per-tick zoom comparison inside the reset guard was level-
triggered: at the canonical frame every continued zoom-out tick drifted
_view_zoom a hair below fit, read as 'not there', and re-fired
enter_orbital — tile set torn down/recreated per tick, in-flight
responses orphaned (black entry), server flooded (889 of 897 responses
in one drive phase). The drift concern now lives in _zoom_at as a ZOOM
FLOOR (once at the canonical frame, zoom-out clamps at fit); the guard
keys only on fields that never drift (_held_center/_held_granularity_v2/
_tile_mode) — genuinely edge-triggered. Three shared helpers keep the
guard and the floor on one definition of 'canonical frame'.
Anti-storm regression drives 100 real _zoom_at ticks past the first
reset and asserts tile-set child node identities never change; the
black-entry repro interleaves Lendel's six wire-accurate tile responses
with continued ticks and asserts all six held. Both revert-verified with
the exact live symptoms. Round-5 reset tests pass unchanged. Live-
verified twice: exactly 6 requests at entry and at reset, no storm.
The wrapped tile column (Lendel: -6400 canonicalized to 12739) drew at
its canonical column — off-canvas right — leaving the mosaic's left
third black. nearest_wrap_image() re-expresses a tile column as the
wrap-image closest to held_center for DRAWING only (requests/cache keys
stay canonical). The draw-position test asserts overlap FRACTION, not
bare intersects() — the buggy placement still clipped ~2px of viewport
edge at Lendel scale, so intersects() alone would false-pass.
The full-zoom-out reset restored center but not the fit zoom, and its
'already there' guard keyed on a lagging field so it could only ever
fire once. The guard now also matches tile mode and compares _view_zoom
against the freshly computed fit — restoring the FULL canonical
transform (center, offset, fit zoom) and re-firing as a continued
gesture keeps zooming out (Jeroen's hard condition, both live shapes).
+8 revert-verified tests incl. the continued-gesture reset repro; smoke
stub gained get_body_radius_km (crash confirmed real under a real
driver before fixing). Suites 286 green; gdlint clean.
Three live-found rendering bugs, all invisible to green unit suites:
- _draw_tile_mosaic placed tiles from absolute district (0,0); canvas-
local (0,0) is held_center - held_n/2 everywhere else, and tile-mode
held_n is the whole-body extent — the entire mosaic drew tens of
thousands of px off-canvas. New pure district_to_canvas_local() +
viewer accessors route every tile through the shared frame.
- _maybe_reselect_rung updated held_n across crossings without
recomputing _view_offset — the single-window composite landed off-
canvas the moment any crossing happened (why District/Quarter were
black too). New pure recompute_offset_for_held_n_change().
- _build_tile_texture created an unstored ImageTexture per _draw,
racing the RenderingServer's deferred upload — CPU pixels correct,
screen white. Per-tile-index texture cache, same reference-identity
discipline as the single-window _cached_texture.
Structural close of the twice-bitten 'nothing asserts pixels' gap:
test_atlas_window_overlay_draw_smoke.gd renders overlay output into a
SubViewport and asserts visible pixels for both modes — runs under a
real driver (invocation documented in DEVOPS.md, visual_capture
precedent; migration to the T-1157 harness noted on that ticket), skips
loud-but-green under the gate's headless run (verified green-with-skips
AND genuinely red with detection forced off). +7 geometry/crossing
tests, all revert-verified. Targeted suites 270 green; gdlint clean.
Bug A (zoom reselection dead): select_rung() redesigned from the two-gate
split to a unified per-rung coverage-ceiling walk (MAX_COVERAGE_M,
finest-first) — which also restores District as a reachable rung
(33-131km extent band; the two-gate model had made it structurally
unreachable). Plus the THIRD instance of the PR #191 C1 clamp-mirror bug
class: _maybe_reselect_rung fed the old rung's _held_n raw into the new
rung's request — the server clamped, the held value didn't, every
cross-rung response stale-dropped. Re-clamped at the boundary; the
leaving_tile_mode flag also fixes a stale request-granularity edge case.
Bug B (orbital = whole body): genuine tile-set model. compute_tile_grid
composes ceil-divided Region-tile centers (canonicalized column wrap,
row clamp, Dictionary-set dedup; Lendel = 3x2 = 6 tiles);
AtlasWindowTileSet owns one AtlasWindowRequest per tile (reusing all
tested request/cache/debounce machinery), fans responses out, per-tile
tile_ready — genuinely progressive; the overlay draws arrived tiles as a
mosaic under the one view transform. enter_orbital branches to tile mode
when >1 tile; single-window path unchanged below the coverage ceiling.
E/W wrap at tile seams live-confirmed (col -6400 wraps to 12739). Pole-
row dedup verified correct but flagged honestly as currently-unreachable
defensive logic (no real radius triggers row collision).
All fixes + fallout verified via revert/confirm-fails/restore. Targeted
suites 256/256; gdlint clean; new 20-test tile-set suite.
Second live-round blocker: the T-1150 legacy granularity comparison
stayed armed alongside the v2 check, and the server ALWAYS sends the
u32::MAX Region sentinel in the legacy slot — which can never equal the
client's pinned legacy value, so every Region response was stale-dropped
after the v2 check passed. _echoed_granularity_matches() now branches on
PRESENCE of granularity_v2: present -> v2 is the only comparison; absent
(old server) -> legacy fallback.
Mock-fleet audit while fixing: three tests had responses diverging from
the real wire — the oversized-orbital round-trip omitted the legacy
sentinel (passed for the wrong reason), the Region-accept test used
legacy=1, and the mismatch-drop test left v2 at a masking default that
would have inverted under the new rule. All wire-accurate now with a
named SERVER_LEGACY_GRANULARITY_REGION_SENTINEL const; +2 tests (legacy-
only old-server acceptance; v2-wins-regardless-of-legacy precedence
proof). All three verified to fail against the reverted fix. Full suite
3524/3524.
The live round against a real server caught what 3518 unit tests missed:
enter_orbital stored the UNCLAMPED n (Lendel: 19139 raw circumference
cols) into _held_n while AtlasWindowRequest clamped independently to
6400 before sending — every orbital response on a real-sized body was
dropped by the viewer's own w_n != _held_n staleness check. Permanently
blank ladder. The PR #191 C1 clamp-mirror lesson, one layer up: the
mirror must apply wherever held/expected state is recorded, not only at
the request layer.
_enter_at_rung now clamps via _clamp_window_n_mirror_v2 before storing
(the file's only _held_n write site — reselect/refloat read and forward).
Regression closes the coverage hole: the oversized-orbital RESPONSE
round-trip (enter_orbital on GJ380c -> deliver echo n=6400 Region ->
assert accepted). Both new tests verified to fail against the reverted
fix before trusting green. Full suite 3520/3520.
The atlas 'regional' screen now opens the LADDER at the canonical orbital
frame (Region granularity, whole body fitted and centered) and wheel zoom
descends continuously — cursor-anchored, unclamped across rungs, with
progressive refinement (held composite keeps drawing, finer rung swaps in
place on arrival; no blank frame, no mode flip). Full-zoom-out resets to
the canonical planetary frame per Jeroen's HARD condition
(is_fully_zoomed_out = extent >= body circumference, not a zoom-value
heuristic). The district_screen nav hop is deleted — D-013 restored:
descent is a zoom gesture, not a nav push. AtlasViewer's heightmap-texture
path is unreachable from nav (code intact; overlay surface deferred, see
report/tickets).
Rung selection: design doc §5's literal formula has NO legal District band
at any real viewport (visual-tolerance band and n=64 coverage ceiling
never overlap — pinned by executable boundary tests at 1600x900);
select_rung() splits it into a coverage ceiling (decides Region) then the
2x visual tolerance (District vs Quarter), documented at the function.
In practice the ladder steps Region -> Quarter directly.
Wire: window_granularity_v2 encoded (omitted at District for byte-compat),
granularity_v2 echoed value keyed + staleness-guarded end to end; Region
clamp mirror replicates the server's bounded halving loop (no closed
form). MIN/MAX_ZOOM widened to [0.0005, 64] — the old 0.5 floor would
have clamped a real body's canonical fit zoom, violating the reset
condition.
Real pre-existing bug fixed in atlas_window_overlay.gd: the draw path used
echoed n as both cell-grid dimension and district extent — only
coincidentally correct at District granularity; Quarter/Region would have
read wrong array offsets. cell_grid_side_for_window() now mirrors the
server's WindowGranularity::cell_grid_side.
Tests: +26 pure-function geometry tests, new 30-test zoom-ladder suite,
extensions across the window cache/request/overlay/delivery suites.
Full suite 3518 green; cold-parse clean.
R1 measured first: a capped Region tile through the real production path
(build_district_window_layer + T-1151 par_iter) at the 64x64 wire cap
costs 0.40-0.48ms — faster than the shipped district n=64 window, so
Jeroen's progressive capped-density tiling ruling is comfortably
interactive on-demand. Raw orbital derive ~0.9µs/cell (~2.3x faster than
full derive; region_baseline dominates, not invent_primitives).
R5 redesign: WindowGranularity enum (Quarter/District/Region), serde
named-variant per the RoadNodeKind precedent, spacing from D-243 scale::
constants — the single source of truth. Additive serde-default
window_granularity_v2 request field (None = legacy u32 path; v2 wins when
Some); DistrictWindowLayer.granularity_v2 always echoed. Legacy u32 echo
for Region uses reserved WINDOW_GRANULARITY_REGION_KEY = u32::MAX (never
a legal input) so the old slot cannot lie about aliasing. Cache and
coalescing keys carry the enum itself (Ord by declaration order, D-010).
n stays district-extent at every rung; Region's cell grid is a DIVISION
(round(n/100), min 1) with its own per-axis ceiling
DISTRICT_WINDOW_MAX_N_REGION=6400 and a bounded halving-loop clamp (no
closed form under the rounding division — the client mirror must
replicate the loop).
derive_orbital_at_metres: bilinear envelope reads + region_baseline
temperature, NO invent_primitives (proven by test — slope_q pinned 0),
routed through the shared build_district_profile classification tail so
the existing colorizer family renders orbital cells unchanged. R2
stepped-categorical behavior documented at the function, not implied.
Region aliasing + clamp/echo tests mirror the T-1150 discipline. 1813
lib tests green; clippy clean; fixture regenerated (254->278 bytes, new
echoed field).
All seven Hoshe/Tyre findings addressed, none retracted:
- n-clamp/echo/staleness triangle (Tyre C1): client _clamp_window_n_mirror
(bit-for-bit twin of the server clamp, canonicalize_district_center
precedent) applied before _n is stored/sent; server test pins the
quarter n=32 -> echo 16 contract.
- min_wl band quantization (Hoshe 1/Tyre C3): quantize_min_wl_m snaps to
MIN_WL_BANDS_M {0, 32768, 16384, 8192, 4096} before cache key and echo
(design doc §5's unbounded-key fix), reusing the one true
OCTAVE_WAVELENGTHS_M array; docstrings now state the server-quantizes/
client-sends-raw split; same-band cache-sharing test.
- coalescing granularity axis (Hoshe 2): two tests pin different-
granularity requests as separate in-flight slots and same-granularity
coalescing unchanged.
- orphaned fixture (Hoshe 3): test_protocol.gd consumer decodes
atlas_response_ready_with_window.msgpack through the real IPC path and
asserts the new fields.
- atlas_window_request coverage (Hoshe 4): new test file — stale-drop on
granularity mismatch, old-server-shape defaults accepted, clamp mirror
formula + wiring. First draft's quarter-via-request_now test would have
passed for the wrong reason (request_now resets granularity by design
until T-1153) — split into formula pin + reachable-path wiring proof.
- granularity type seam (Tyre C2): field + resolver docstrings state
finer-only integer multiples with resolve_window_granularity as the
single widening point; matching contract note added to the D-226
T-1143-rulings amendment.
cargo --lib 1807/1807; goldens bit-identical; gdlint clean.
T-1151: build_district_window_layer dispatches one Rayon task per row
(pure derive_window_cell via derive_at_metres), scattered row-major into
the flat arrays; a cfg(test) serial path backs the bit-identical
parallel-vs-serial golden.
T-1150: serde-default window_granularity (1=district, 4=quarter) +
window_min_wl_m on AtlasLayerRequest — additive, no sixth demux shape,
old frames decode unchanged (tested). Quarter mode = full
reclassification at 512m spacing over the same world rect ((4n)x(4n)
cells); WIRE_CAP_CELLS=4096 enforces n*granularity <= cap (quarter
clamps n to 16, the design doc's worked example). Granularity + min_wl
key ALL five touch points: DistrictWindowLayer echo, server FIFO-256
cache key (now a 5-tuple), per-connection coalescing key, client
request codec (omitted-at-default wire fields), client LRU key.
Mandatory aliasing regressions on both ends: identical (body, center, n)
at granularity 1 vs 4 produce distinct cache entries and correct
per-granularity payload shapes (server, 3-thread queue to avoid the
AnalyzeBody thread contention found while writing it) and distinct
client cache keys (gdUnit). Replay fixture regenerated — the layer
struct grew two echoed fields (231->254 bytes, content verified).
Client requests stay district-granularity by default — quarter requests
arrive with T-1153's rung selection.
PR #190 review (Hoshe + Tyre), all five findings addressed:
- Tyre: queue_move_step/queue_stance_toggle (the T-1088 click-to-move seams)
now share the full suppression predicate — all three input_queue producers
in the module are gated, making the header's 'every action this file can
produce' claim true (previously the seams relied on the sandbox caller's
own freeze). +3 regression tests.
- Hoshe: the live suite's settle=baseline timeout fallback compared baseline
to itself, vacuously passing the no-movement claim — a timeout now fails
loudly (both tests).
- Hoshe: the unit suite's real open_app/close_app transitions fire the
global auto-pause handler; with ambient SimBridge.state left CONNECTED by
earlier suites (test_hub_teleport has no after_test) each transition
silently appended AutoPause/AutoResume to the shared _outbound_buffer.
before_test now forces DISCONNECTED (restored in after_test) and clears
the buffer.
- Hoshe: documented why the live suite's reset_test_state() call is inert
but still correct.
Full suite: 3374/3374.
Unit suite: pin facing to North via the T-1088 facing_angle_provider seam.
D-054 makes movement facing-relative, and _update_facing_from_mouse() derives
facing from engine/session globals (viewport mouse position vs the
GameState.player_position anchor) — in full-suite order 'press W' resolved to
MOVE_NORTHWEST (the gate failure at line 187).
Live suite, three independent leaks from the shared autoloads, each of which
passed in isolation and failed in gate order:
- snapshot starvation: poll_snapshot() is consume-and-clear; a node leaked
into the gdUnit tree by an earlier suite steals every snapshot during our
awaits. Capture snapshot_received (fires on every consume, ours or theirs)
alongside a direct poll + explicit SimBridge._process pump.
- tick poisoning: SimBridge left test_mode=true + CONNECTED lets the leaked
poller apply TestHarness mock snapshots (~tick 15+) into
GameState.current_tick during our spawn awaits; the fresh server's own
ticks (1..5) then stamp backward and its InputQueue panics ('tick ordering
violated', last=16 new=3). Neutralize SimBridge before the first await,
zero current_tick, clear _last_snapshot/_outbound_buffer both directions.
- crash-on-assert-failure: gdUnit asserts don't halt; guard the player-entity
dereferences so a missing Player reports cleanly instead of a runtime error.
Full suite: 3368/3368.
InputMapper polled the D-054 move_* actions (and queued discrete
gameplay actions) unconditionally — WASD with a fullscreen implant
open walked the character blind. Poll-site gate (both _process and
_unhandled_input, the file's existing dialogue/free-camera early-return
idiom): nothing is enqueued while occluded, so no backlog can flush on
close.
Gate signal is HudGroups.gameplay_occluded, deliberately NOT
is_implant_active(): that flag is also true for INSERT mode (economics
monitor panel), where gameplay stays visible and playable by design —
the naive gate would have broken WASD there. Local _gameplay_occluded
mirror via the existing signal; regression tests pin the INSERT
distinction.
Held-state semantics: open-while-held stops on the next poll;
close-while-held requires release-then-repress (a
_suppress_move_until_release latch armed on the close transition,
cleared only when EVERY movement key is released — one-of-two released
does not clear, tested). Facing exempt from the latch (re-sync, not
lurch).
Audited action set: movement/facing-send/INTERACT/perception/pause/
stance/teleport/quicksave-load gated; OPEN_MENU exempt (Esc must close
the implant), BUG_REPORT/OPEN_JOURNAL exempt (client-only, never reach
send_input); dialog-driven direct SimBridge sends (settings, dialogue
pause, quit-to-menu save) out of scope by design. The pre-existing
server-side AutoPause defense observed firing correctly alongside.
Drive-by: pre-existing gdlint class-definitions-order violation in
input_mapper.gd fixed (public/private var ordering) — file lint-clean
for the first time.
Tests: 16 unit/state (real autoloads, real open_app/close_app
transitions, Input.action_press engine state) + 2 LIVE against a real
spawned server through the REAL client pipeline (position frozen for
20 held-W ticks while occluded; still frozen 10 ticks after close
without re-press; resumes on re-press) — port-retry/wall-clock/
COOLDOWN_TICKS conventions reused from test_input_roundtrip. 12-suite
regression sweep of every InputMapper consumer green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cover-fit: fit_window_view zooms from the viewport's LARGER dimension,
no margin factor (any factor under 1.0 leaves a long-axis gap — checked
numerically) — the composite fills edge to edge, overhanging the short
axis into pan-space; the refloat center-equality early-return already
prevents refetch churn at rest (proved, not just tested).
Input model (Jeroen: drag breaks click semantics with map objects):
LMB-drag pan REMOVED from the regional window; clicks are
object-reserved. Pan = held WASD/arrows polled in _process (delta- and
zoom-scaled, camera-pans-toward-key convention verified numerically)
plus edge-scroll within 24px of the viewport border; both suppressed
over UI and on OS focus loss; both set _user_adjusted; wheel zoom and
Esc unchanged. Reads RAW physical keycodes deliberately — independent
of the shared D-054 move_* InputMap actions bound to the same keys
(whose occlusion-leak is pre-existing and now ticketed as T-1146).
Pole wall + east-west wrap unchanged, re-driven through the new
inputs; drag tests replaced, not kept.
Smoothed composite (interim pending T-1143): per-cell colors bake into
an n x n Image/ImageTexture (exact existing colorizer incl. overlay +
ice tint) drawn once with LINEAR filtering — GPU bilinear reads as
terrain, the planetary heightmap's own treatment. Crisp per-cell path
preserved behind COMPOSITE_SMOOTH for T-1143 A/B. Rebuild only on
reference-identity change of window/toggle (is_same — verified true
reference equality; value-equal distinct dicts DO rebuild).
Governance: T-1145 amendment paragraph on D-226 T-1124 SS5 (all three
supersessions); pql decisions validate ok.
Suites: window_viewer 74/74, window_geometry 32/32, window_overlay
(new) 16/16; gdlint clean on all six files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe's finding: the flag introduced so auto-fit never fights a manual
view had zero coverage on exactly that branch. Two tests drive the
REAL _gui_input path (synthetic drag), then fire NOTIFICATION_RESIZED:
user-adjusted view survives a resize untouched (zoom AND offset);
an unadjusted view re-fits to the new viewport. 52/52.
Non-blocking doc note also taken: layer_proxy's normalization comment
claimed the twins would otherwise 'coalesce independently' — the
coalescing key is (ConnectionId, body_id) and never carried center;
rewritten to say what normalization actually buys on that path (the
work item derives and echoes the canonical center).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bounds gate: AtlasDescendGeometry.is_on_texture() — ONE helper feeding
both the reticle guard and the click fall-through (the T-1140 lesson:
the visible affordance always matches the click); half-open
[0,tex_w)x[0,tex_h) boundary pinned at the exact edge. Letterbox
clicks no longer show a reticle or descend.
Fit-and-center: pure fit_window_view() (new atlas_window_geometry.gd)
wired into enter(), the FIRST window arrival, and NOTIFICATION_RESIZED
— gated by a _user_adjusted flag so the fit never fights manual
zoom/pan (flag clears only on a fresh enter). Found-own-bug: RESIZED
can fire mid-_ready() before _canvas exists — null-guarded like the
sibling panels.
Pole wall (Jeroen's ruling): clamp_pan_offset_to_pole_wall() clamps
the WINDOW EDGE, not the center, in screen space from the fitted
transform — Y only; wired into the drag handler and every fit (a
fresh fit can itself need the wall on a tiny body — the
window-taller-than-planet case is handled and tested). Three numeric
hand-traces preceded the code; a first-draft test using GJ380c's huge
radius silently never exercised the clamp — replaced with a synthetic
small radius.
East-west wrap (Jeroen's ruling): canonicalize_district_center() —
posmod column wrap (verified against a live Godot process to match
Rust rem_euclid bit-for-bit), clamped row; district_extent() shares
the exact formula (incl. .max(1)) with the server's
normalize_window_center so echoes and cache keys agree on canonical
form. Canonicalization applies only to the FINAL refetch center — the
edge-crossing decision stays in absolute district space (first-pass
math error caught by hand-trace). Seam-adjacent cache-key sharing
tested. Pan offset itself has no x wall — circumnavigation is
seamless.
Header: body proper_name/body_id ahead of the coordinates (the cheap
half of T-1141, noted in code). Drag-pan verified through the REAL
DistrictScreen-to-viewer chain and pinned by test (no fix needed).
140 tests across three suites, 0 failures; 94 sibling tests no
ripple; gdlint clean (atlas_viewer.gd at the 1000-line cap a second
round — structural extraction flagged for maintenance).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hoshe's blocking find: district_pos_at divided the latitude axis by
tex_h where the server forward map uses ta.h.saturating_sub(1)
(district_profile.rs:1436; ground-truth inverse aliveness_probe.rs:511)
— every off-equator click descended into the wrong district, up to
~20km drift, live-repro'd at 10.2km. Fixed to /(tex_h - 1.0) with the
reference implementation's degenerate-texture guard; verified against
the repro (row 50: old formula recovered 41, fixed recovers 50 exact).
Columns were already correct (longitude wraps — the asymmetry the
docstring documented but the code didn't implement).
Regression tests transcribe the server forward formula (source cited)
and round-trip three off-equator rows — one per hemisphere plus
near-pole — asserting EXACT recovery; a dedicated drift guard is
framed as Tyre's C2 (a client-side twin of a server mapping owns its
own round-trip proof). Root-caused the coverage hole: the old
equator test sampled tex_h*0.5 believing it was the equator pixel —
the true equator is (tex_h-1)*0.5, so the test never verified what it
claimed; corrected with the why documented.
Araminta's find: the descend reticle drew unconditionally on hover —
over a city the marker flared white (city-click signal) while the
reticle+extent label promised descent, though the click correctly
resolved to city data. Extracted _should_draw_descend_reticle() with
the missing _hovered_city.is_empty() clause; three state tests pin
shows-over-map / hides-over-city / hides-when-idle.
test_atlas_descend_entry 32/32; adjacent clusters no ripple; gdlint
clean (atlas_viewer.gd at exactly the 1000-line cap).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entry per Jeroen's 2026-07-21 revision: planetary heightmap is now
FIXED — all drag-pan/wheel-zoom input removed (set_view/get_view_*
capture API survives for the golden harness); hover shows a
not-to-scale bracket reticle with the real extent labeled (a true
n=32 rectangle is sub-pixel on the planetary canvas — the honest
representation given the morph transition is deferred), and a click
that misses every city marker descends (city-click wins — one
gesture, two contextual reads, no modifier). Descent pushes a new
'district' nav screen centered on the click point's DistrictPos via
atlas_descend_geometry.district_pos_at (the pixel-to-district inverse
of the server mapping, verified against scale.rs).
Regional mode: atlas_window_viewer draws the composite (morphology x
elev_q lightness base; temp/moisture/veg toggles — temp reuses the
region-ramp colorizer exactly; Marine=6 transparent; glaciation
always-on tint matching apply_ice_tint's REAL gate, None|Light no-op,
over the amendment's looser prose — documented); pan-on-held-composite
with edge-crossing refetch + border-fade during the queue-based
derive wait; zoom never refetches. atlas_window_cache: LRU keyed
(body_id, center, n), touch-on-read, evict-only, no freshness (D-227).
atlas_window_request mirrors the generation-proxy pending-retry shape
for None-until-derived. Codec: window params omitted from the wire
when absent — byte-identical for every existing caller.
Live-verified against the T-1137 server in-worktree: real round-trip
on a GJ380c coastal district (6 fields x 1024 cells), echo staleness
guard, genuine ~1.4s background-derive wait, pan-edge refetch to an
adjacent window, cache-hit on re-descent with zero network. Full
client suite 3194/3194; gdlint clean on all 18 files.
Open follow-ups flagged in-code: header location label always falls
back to coordinates (nearest-settlement needs a join the district
window does not carry); atlas_standalone.gd's 'atlas_app.gd is never
modified' doc line is now imprecise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>