Commit Graph
2821 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Fable 5 1eb596ec78 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:11:12 +02:00
jpmschweitzerandClaude Fable 5 8da9670e0f feat(simulation): feature-name pipeline wired + legacy window_granularity u32 retired (T-1169, T-1159)
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>
2026-07-25 16:10:27 +02:00
jpmschweitzerandClaude Fable 5 befdb689c1 fix(simulation): orbital rung applies coast_warp_px — one coastline at every rung (T-1160)
The Global/Region rungs sampled the heightmap ocean mask raw while every
finer rung samples it at the coast-warped position — a structurally
different coastline at the orbital-to-district seam. Audited on all 267
real body heightmaps at region spacing, coastal band only: 5.01%
land/ocean classification disagreement (2,911 of 58,073 cells), mean
displacement ~6.0 km, max ~18.9 km. Fork taken: APPLY the warp —
derive_orbital_at_metres now runs invent_primitives' steps 1-3 (driver
climate -> coast character -> coast_warp_px) before sampling, still
skipping detail-scatter (its octave ceiling, 32.8 km, is below region
spacing; the WARP's octaves reach 262 km, which is why skipping it was
wrong). Cost measured: ~784 ns/cell added (~15 ms on an Earth-class
Global canvas; low hundreds of ms parallel at the 8.3M-cell ceiling).
The ignored audit test preserves the pre-fix baseline for the record.
Golden re-pinned — region rows only, district/quarter byte-identical.
project.yaml 0.4.2 -> 0.4.3: orbital canvas bytes changed, client disk
caches must miss. Awaiting Araminta's review-seat sign-off per ticket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:09:46 +02:00
jpmschweitzerandClaude Fable 5 0d8984fe53 chore(meta): pql changelog — visual-defect batch closed (T-1186/88/89/92 done, T-1193 filed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 14:46:52 +02:00
jpmschweitzerandClaude Fable 5 4a9567c669 style(simulation): gate bounce — fmt wrap + clippy needless_range_loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 14:24:45 +02:00
jpmschweitzerandClaude Fable 5 39f0fd8c51 fix(simulation): lake_margin_q normalizes per basin — full-range depth gradient (T-1188)
The PR #206 eyeball caught what every numeric gate passed: the depth
signal was visually flat on both test lakes (GJ1c lmq=0 across the whole
basin; GJ338Bd 0-13 of 100). Two compounding causes: a fixed absolute
ceiling (one body's p90 cell depth) compressing skewed depth
distributions into single digits, and heightmap-pitch depth variation
being sub-texel-tiny within most basins. lake_margin_q is now
depth / the basin's own maximum settled depth: HydrologyResult grows
basin_max_depth_scaled (computed in solve() from existing basin_cells
membership, broadcast per basin), threaded through
HydrologySample.basin_max_depth, normalized in lake_from_hydrology_at
with a degenerate-basin epsilon guard (a genuinely uniform pond shades
flat — honest, not forced). Lake EXISTENCE (filled > original) is
untouched — only tone changes. Measured at district spacing:
GJ1c min=0 p50=33 max=84; GJ338Bd min=8 p50=38 max=70 — full-range
shore-to-deep ramps on both. No perceptual curve added: the linear
per-basin ramp is already well-quartiled. project.yaml 0.4.1 -> 0.4.2
(0.4.1-tagged canvases carrying flat-lmq semantics reached real disk
caches during eyeball runs and must miss). Acceptance gates green;
zero golden churn (lake_margin_q not captured by either golden shape).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 14:23:11 +02:00
jpmschweitzerandClaude Fable 5 b39dd49950 docs(simulation): update step_canvas row-space doc — T-1186 is fixed (PR #206 review)
Hoshe's review finding: the build_step_canvas row-space convention block
still described region_centre_latitude_deg as pole-anchored/bugged and
said 'T-1186 stays open' — both false after this PR's own first commit.
Rewritten to reflect the fixed state; the deliberate signed-convention
rationale and the layer_proxy deferral (still true, still T-1181's scope)
are preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 13:44:35 +02:00
jpmschweitzer 324a318b17 Merge remote-tracking branch 'origin/main' into atlas-latitude-fix
# Conflicts:
#	CHANGELOG.md
2026-07-25 13:30:16 +02:00
jpmschweitzerandClaude Fable 5 4c44786f99 docs(governance): D-255 amendment — rung-0 integer viewport-fit display ratio
Records the T-1192 presentation ruling: the Global opener's display
ratio is viewport-fitted per body to the largest integer px/gridunit
that fits the legend-reserved viewport (floored at 1x1, letterbox
centred), texel-exact by construction; fixed rungs keep their tuned
constants. Explicitly notes that non-integer resting-state scaling
remains unsanctioned and that a PR #205 review finding removed a
fractional-fit branch which had cited this record for an exception it
does not contain — the mis-citation episode is part of the record so it
cannot recur silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 13:14:26 +02:00
jpmschweitzerandClaude Fable 5 374b195594 fix(ui): PR #205 review round — integer px-per-gridunit fit replaces fractional (T-1189, T-1192)
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>
2026-07-25 13:14:12 +02:00
jpmschweitzerandClaude Fable 5 562dbc5b21 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 13:09:07 +02:00
jpmschweitzerandClaude Fable 5 dec5b0bc43 feat(simulation): lake_margin_q depth band — lake shorelines gain gradient vocabulary (T-1188)
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>
2026-07-25 13:08:44 +02:00
jpmschweitzerandClaude Fable 5 07f0fa307f fix(simulation): region baseline latitude — equator-anchored signed rows (T-1186)
region_centre_latitude_deg mapped row 0 to the north pole with lat_frac
clamped [0,1] while the derive core floor-divides equator-anchored signed
world metres — every northern-hemisphere region clamped to +90 (polar
everywhere) and the southern hemisphere read as compressed northern
tropics. Now mirrors the derive core's exact inverse mapping (lat_frac =
centre_y/meridian clamped [-0.5,0.5], lat = -frac*180). The ticket's
in-the-wild datapoint flips as predicted: GJ338Bd region (136,43), old
baseline +13.94N/26.8C -> new -76.06S/10.3C; the 76S district cell now
derives Lake/2.07C/Light glaciation. Goldens regenerated (believability +
window derivation, stable on rerun); two region_profile unit tests
repositioned — their probe coordinates meant different places under the
old convention. step_canvas rung-0 already used the correct signed
convention; layer_proxy's pole-anchored pseudo-grid stays deferred to
T-1181's rung-0 rebuild per D-256(f).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 13:08:26 +02:00
jpmschweitzerandClaude Fable 5 915a6790f0 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 12:52:28 +02:00
jpmschweitzerandClaude Fable 5 c1a97166c5 feat(ui): cap rung extents to the body + fit/center the Global canvas (T-1189, T-1192)
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>
2026-07-25 12:52:06 +02:00
jpmschweitzerandClaude Fable 5 7ab15baed5 chore(meta): pql changelog — T-1183 done, T-1191/T-1192 filed, T-1189 escalated
T-1191: atlas edge-scroll pans on unfocused mouse motion (eyeball nit).
T-1192: Global rung canvas fit/center presentation.
T-1189: raised to high — Region-rung extent exceeds body on both axes at
standard viewports (continent repeats + south-pole stripe smear), per the
T-1183 eyeball captures Jeroen flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 12:18:55 +02:00
jpmschweitzer 2fffec866c Merge remote-tracking branch 'origin/t1183-client-cache' 2026-07-25 12:12:37 +02:00
jpmschweitzerandClaude Fable 5 4d43a41acd chore(meta): pql changelog — T-1190 filed, T-1183 to review
T-1190: step-canvas wire staleness signal + client sim_ttl threading
(the D-253 clock seam raised in PR #204 review, adjudicated future-seam).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 11:37:40 +02:00
jpmschweitzerandClaude Fable 5 51fe35d00d fix(ui): harden step-canvas disk cache — PR #204 review round (T-1183)
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>
2026-07-25 11:36:21 +02:00
jpmschweitzerandClaude Fable 5 6702e6c725 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 11:09:18 +02:00
jpmschweitzerandClaude Fable 5 6b76e365bb feat(ui): client disk cache — FileAccess tiers beneath the LRU (D-255, T-1183)
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>
2026-07-25 11:08:48 +02:00
jpmschweitzer 83986dbb90 Merge remote-tracking branch 'origin/t1182-client-map'
# Conflicts:
#	CHANGELOG.md
2026-07-25 10:31:03 +02:00
jpmschweitzer b2d2128b92 Merge remote-tracking branch 'origin/t1185-outlet-wiring' 2026-07-25 10:30:24 +02:00
jpmschweitzerandClaude Fable 5 9a296de797 chore(meta): pql changelog — T-1182/T-1185 cycle (T-1188/T-1189 filed, T-1157 inventory)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 10:30:12 +02:00
jpmschweitzerandClaude Fable 5 76d82a6a51 fix(ui): PR #203 review round — total retirement + bin decode + wired reset
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>
2026-07-25 09:32:52 +02:00
jpmschweitzerandClaude Fable 5 1d2cac9e65 fix(simulation): PR #202 review round — spill cell always wired + golden truth
Hoshe finding 1 (live-firing on GJ1c: 2 spill collisions + 1 i==1
collision among 51 Overflow basins): adjacency adjudicated
INSUFFICIENT for the cue — a course's visible anchor is its upstream
cell, so nothing pre-existing belongs to the lake unless wired. The
spill cell (outlet_path[0]) now always gets a real entry: appended
when new, OVERWRITTEN IN PLACE when it collided with an existing river
cell (append would duplicate edge_id; the hydrology solve is the more
authoritative downstream answer for that cell than flat D8
extraction). Interior stop-on-collision stays, now provably safe.
Internal lookup is a dense Vec<Option<usize>>, never iterated (D-010).
Two non-vacuous regression tests prove the cue through build_edges
output; end-to-end on GJ1c all 51 Overflow basins now build a readable
edge (was: one silently missing).

Hoshe finding 2: both doc sites now state the fallback-vs-production
split explicitly (fallback moisture 55: 51/2; production GJ1c moisture
80: 53/53 all-Overflow) — the golden's Endorheic pair is a
fallback-constant artifact, not a fact about GJ1c.

Golden re-regenerated: river_cells 143->192, position-identity diff
purely additive (zero removed, one legitimate in-place overwrite at
the spill-collision cell); attractors/basins/mouths/confluences
byte-identical. Suites: hydrology 26/26, full lib 1943, cascade_golden
1/1, window goldens + believability untouched green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 09:04:54 +02:00
jpmschweitzerandClaude Fable 5 d7fc26bd2a chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 08:54:29 +02:00
jpmschweitzerandClaude Fable 5 d28d24fd26 feat(ui): step-canvas map component — RTT terrain + stepped zoom (D-255, T-1182)
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>
2026-07-25 08:53:48 +02:00
jpmschweitzerandClaude Fable 5 adceb60865 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 08:41:56 +02:00
jpmschweitzerandClaude Fable 5 58cb1fec71 feat(simulation): basin-outlet course wiring — the endorheic cue (D-227, T-1185)
extend_river_network_with_basin_outlets threads each Overflow basin's
resolved outlet_path into RiverNetwork as real D8 downstream
continuations (spill cell skipped as lake interior; Sea target ends at
RIVER_DOWNSTREAM_MOUTH with a seaward entry, Basin/OpenSpillway at
RIVER_DOWNSTREAM_EDGE_DRAIN); Endorheic basins contribute nothing —
the ruled zero-bit cue is outflow-course PRESENCE (D-227 amendment
(4)): a lake with no outgoing course reads as closed. Additive per
T-1170 Ruling 7b; no wire migration, no new zone, no endorheic bit.

Wired in run_layer1_with_moisture strictly AFTER attractor extraction
so outlet cells never perturb settlement placement (tested). Edge
identity is the existing pack_cell_id (row,col) convention; wiring
determinism proven by three independent double-solve byte-identity
tests. 13 new tests across hydrology_equilibrium and layer1.

Goldens: cascade_layer1.json re-pinned (third deliberate re-pin,
documented in cascade_golden.rs) — river_cells 93->143 on GJ1c,
append-only prefix byte-identical, 51/53 real basins Overflow with
short OpenSpillway stubs (the dominant honest pattern: most basins
spill onto adjacent open ground, not a long channel to sea).
window_derivation_golden and river_course_golden verified structurally
unreachable and unchanged.

Known scope boundary (documented in-code): outlet cells carry
river_class 0 (stream) — outlet-channel classification/meander width
scaled to catchment is a named follow-up, not guessed at here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 08:40:07 +02:00
jpmschweitzer e0a3ab35cf Merge remote-tracking branch 'origin/t1181-step-canvas' 2026-07-25 03:35:38 +02:00
jpmschweitzerandClaude Fable 5 2f45292563 chore(meta): pql changelog — T-1181 wire-shape report + review status
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:35:34 +02:00
jpmschweitzerandClaude Fable 5 8af28f317b fix(simulation): PR #201 review round — wire extent clamp + coalescing tests
Hoshe finding 1: StepCanvasRequest.extent is no longer wire-trusted —
clamp_step_canvas_extent enforces the D-255(b) canvas budget at the
request boundary (per-axis cap 3840 defeating u32::MAX before any
multiplication, then an aspect-preserving total-cell ceiling at the
measured 3840x2160 = 8,294,400-cell workshop budget), mirroring the
legacy carrier's clamp_window_n_v2 discipline; the Global rung ignores
the wire extent entirely. StepCanvasResponse gains the extent echo
field so a client can detect the clamp (the DistrictWindowLayer.n
precedent — a pre-existing gap closed in passing, recorded on the
ticket for T-1182). Seven new tests including an end-to-end u32::MAX
request proving actual allocation respects the cap.

Hoshe finding 2: submit_step_canvas coalescing now has the same two
regression tests its submit_window sibling always had (same-key
collapses to one pending item, different-key does not), exercising
step_canvas_supersede_key.

Targeted suites green: 1928 lib, acceptance gate 5/5, bridge_tcp 22/22,
window_derivation_golden 6/6 byte-green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:34:09 +02:00
jpmschweitzerandClaude Fable 5 a64701645b style(simulation): clippy nits on the step-canvas batch (gate bounce)
Two needless mutable borrows in the serve system, one spurious
markdown list marker from a doc-comment line wrap (a leading '- '
turned the whole T-1186 convention note into a lazy continuation), and
a named row/col pair replacing the identity-op grid index in the
settlement-id tie test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:19:25 +02:00
jpmschweitzerandClaude Fable 5 ab52897d66 style(simulation): cargo fmt on the step-canvas batch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:15:58 +02:00
jpmschweitzerandClaude Fable 5 51f927de5d chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:15:28 +02:00
jpmschweitzerandClaude Fable 5 c6f0bd6da2 fix(simulation): thread body_params through DeriveStepCanvas terrain derive
The T-1184 merge seam: TerrainAnalysisCache::get_or_derive gained a
body_params parameter (real moisture ceiling for the hydrology solve);
the DeriveStepCanvas arm now passes its own body_params so step-canvas
requests get the same hydrology-carrying TerrainAnalysis the legacy
DeriveWindow path does. Acceptance gate + window goldens green
post-merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:14:52 +02:00
jpmschweitzer b7f54a4de0 Merge remote-tracking branch 'origin/main' into t1181-step-canvas 2026-07-25 03:13:18 +02:00
jpmschweitzerandClaude Fable 5 cd2f342421 feat(simulation): step-canvas serving — tagged envelope (D-255, T-1181)
The D-225-discharging wire migration per D-255(c): a new
server/src/atlas/step_canvas.rs subsystem (six-rung StepCanvasRung
ladder — Global variable-extent rung 0 through Chunk 64m — the
StepCanvasRequest/StepCanvasResponse tagged envelope extending the
proven ShapeProbe discriminated-shape pattern, PNG-per-field dense +
MessagePack-native sparse encoding per the T-1179 measured table, and
both server cache tiers: the structurally keep-always GlobalTierCache
and the dual-axis StepCanvasCache with storage TTLs per rung and
SIM_STATE_TTL clock-bucket staleness per the D-227 amendment (1)
formula). Wired through bridge/{mod,tcp,local}.rs (sixth demux shape,
send_step_canvas_response mirroring the five existing senders),
gen_queue.rs (DeriveStepCanvas work item with per-connection-per-rung
coalescing, reusing the shared TerrainAnalysisCache), and plugin.rs
(serve/complete systems, lazy D-206 rung-0 population).

Acceptance gate (mandatory per D-227 amendment (3)):
tests/step_canvas_acceptance_gate.rs — cache-hit == cache-miss
byte-identical for every rung, lossless encode round-trips, cache
round-trips vs fresh derive, distinct-center sanity. 5/5 pass.

Station-spacing cap ADOPTED: course stations floored to District
spacing (2,048 m) at finer rungs — the S2-measured +38-87% chunk/block
course cost had zero display benefit at the same station density
(COURSE_STATION_SPACING_FLOOR_M).

Documented honest gaps, not shortcuts: settlement_id is a proximity
approximation (no footprint polygons exist yet); glaciation/flooded_q
sim-state planes are wire-shape-ready D-253 stubs; rung-0 uses signed
equator-anchored rows (the canonical D-256 core convention — T-1186's
wrong-latitude behavior applies unchanged and unfixed here, by
instruction).

Legacy district_window carrier byte-unchanged: window_derivation_golden
6/6 byte-identical, all district_window suites pass unmodified. Full
suite at implementation time: 2127 passed across 45 binaries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:13:08 +02:00
jpmschweitzerandClaude Fable 5 08b85bf233 chore(meta): pql changelog — T-1184 cycle (deviation note, T-1186 evidence, T-1187 filed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 03:10:17 +02:00
jpmschweitzerandClaude Fable 5 b222cec9be style(simulation): cargo fmt on the review-round test (gate bounce)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:43:05 +02:00
jpmschweitzerandClaude Fable 5 4ef79b6b45 fix(simulation): PR #200 review round — cache sizing truth + endorheic assertion
Hoshe finding 1: dropping HydrologySample.elevation was PROVEN unsafe
(TerrainAnalysis.elev_pct is a rank percentile, not raw elevation;
HydrologyResult carries no elevation) — the copy stays, and the truth
moves into the docs instead: gen_queue's TerrainAnalysisCache sizing
comment corrected to real 512x256 working-grid numbers (~1.57 ->
~2.62 MB/entry, capacity-8 worst case ~21 MB), clone-on-hit cost
documented, byte-safety proof recorded on HydrologySample itself.
Arc<TerrainAnalysis> follow-up filed as T-1187.

Hoshe finding 2: the endorheic-split test now asserts the bowl basin's
BasinOutcome actually diverges (Endorheic at moisture 0, Overflow at
100, straddling ENDORHEIC_MOISTURE_CEILING=60) plus basin-count sanity
— mutation-verified by stubbing is_endorheic and watching it fail.

Doc/test-only round: goldens byte-unchanged, full suite 2114 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:41:55 +02:00
jpmschweitzerandClaude Fable 5 3f4c868daa chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:26:50 +02:00
jpmschweitzerandClaude Fable 5 24fad7090f feat(simulation): lakes from settled hydrology (D-227, T-1184)
Productionizes the T-1177 equilibrium solver: run_layer1 now solves
hydrology once per body (~24ms, mirrors drainage::analyze) and carries
it as TerrainAnalysis.hydrology; run_layer1_with_moisture threads the
real body moisture ceiling (extracted derive_moisture_ceiling_q), with
the T-1177 population-survey default as fallback. The resident rung-0
global tier does not exist yet (T-1181's scope) — hydrology rides
TerrainAnalysis and lands in that tier for free when it is built
(deviation recorded on the ticket).

MorphologyZone::Lake is now sourced from the settled solver at derive
time: a gridunit is Lake when bilinear-sampled filled surface exceeds
bilinear-sampled original elevation at the sample's own (px, py) — the
continuous comparison, so lake edges refine with rung like coastlines;
never a discrete basin-cell projection. The gate sits strictly between
OpenOcean (>= 80) and the old ocean_fraction heuristic (>= 60), which
survives as the derive-fresh fallback when no solve is attached —
byte-identical to pre-T-1184 output in that case. Static
classification, distinct from the sim-state flooded plane; no
endorheic bit (the drains-vs-closed cue is T-1185's outlet-course
presence, per the D-227 amendment (4) sequencing). Zero new wire
bytes.

Acceptance: lake_classification_cache_hit_equals_cache_miss (solve
twice independently, byte-identical zones, non-vacuous Lake hit) plus
hydrology determinism tests. Golden fidelity: the window golden
fixture now builds TerrainAnalysis through the production entry point
(run_layer1_with_moisture, per-body), and a dedicated lake_bowl golden
body pins the hydrology-sourced Lake path (morphology 1 at
ocean_fraction_q 0 — provably not the heuristic); the 108 pre-existing
golden rows are byte-identical (pure append). believability.json moved
by one lake-shaped line (GJ338Bd voxel_relief_m 27->28, a correctly
reclassified lake district leaving the dry-relief sample set).
river_course and derivation-harness goldens unchanged. Full suite:
2114 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 02:26:22 +02:00
jpmschweitzerandClaude Fable 5 5151c9010f chore(meta): pql changelog — T-1174 cycle (D-256 ruling, T-1186 filed, T-964 note)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:33:11 +02:00
jpmschweitzerandClaude Fable 5 236725eb3d fix(simulation): pre-build the true-keyed region cache (PR #199 review)
Hoshe finding 1: derive_all_districts passed a permanently-empty
region_cache, so every survey cell paid four on-the-fly baseline
derivations — and derive_district_profile's docstring (build the cache
before calling in a loop) was contradicted by its sole production
caller. The cache is now pre-built on the TRUE region keys the shared
core looks up (survey-cell centre metres -> containing district ->
region ±1 ring, BTreeSet-deduped), restoring the per-body build-once
cost model. Cache-hit == cache-miss byte-identity holds by D-227 purity
(build_region_profile and the miss branch share the same expressions) —
verified by the believability harness passing against the UNCHANGED
golden. Finding 2: the cascade.rs call-site comment and the stale
module header now state the pre-build truthfully.

Found in passing, filed as T-1186 (pre-existing, out of scope here):
region_centre_latitude_deg is pole-anchored while the derive core keys
equator-anchored signed regions — northern-hemisphere baselines clamp
to +90.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:31:06 +02:00
jpmschweitzerandClaude Fable 5 9068fcbc0b style(simulation): cargo fmt on the T-1174 batch (gate bounce)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 01:16:43 +02:00
jpmschweitzerandClaude Fable 5 630d735d13 chore(meta): update changelog
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 00:40:48 +02:00
jpmschweitzerandClaude Fable 5 933e1f4ee4 fix(simulation): one absolute-metre derive core (D-256, T-1174)
derive_district_profile is now a thin wrapper over the shared
derive_at_metres_with_riparian core at survey-cell-centre world metres
— one derive core, two position sets. The batch pseudo-grid and the
true D-243 district grid were two coordinate systems sharing one bare
(i32,i32) type; the new SurveyCellPos newtype re-keys every batch
product (BodyWorldState.districts, Layer1Output.survey_basin_dirs) so
the compiler rejects cross-namespace passing.

Fixes two latent same-position divergences the T-1174 investigation
surfaced: three inconsistent latitude conventions collapse into the
core's single inverse mapping, and the region-climate baseline now
floor-divides true world metres instead of collapsing the whole body
onto region (0,0)'s baseline — batch climate becomes latitude/region
graded (D-245 direction: every changed believability metric increased).

Binding preservations per D-256(c): basin_direction rides a post-call
override with the true L1 D8 survey-cell aggregate (layer1's map
re-keyed to SurveyCellPos, identity lookup — a floor-divide lookup
against the pseudo-keyed map would have silently defaulted every cell
North); the riparian verdict comes from near_perennial_water_at, never
the empty-slice default (which would have flipped riverside
vegetation_class).

Quarter-skeleton morphology_zone now resolves at the settlement's
exact world position via derive_at_metres at work-item execution
(where TerrainAnalysisCache lives), replacing the survey-cell-centre
map lookup (D-256(d)); settlement_district_pos fixed to true-district
floor-division in passing (same doc/impl mismatch class). Second
pixel-vs-metre conflation fixed in aliveness_probe's anchor-walk math.

Window path byte-unchanged (window_derivation_golden 6/6 byte-
identical); derivation_harness golden untouched; believability golden
regenerated. Full lib + integration suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 00:40:13 +02:00
jpmschweitzerandClaude Fable 5 35a4137304 docs(meta): D-256 — canonical sampling convention ruling
Records the T-1174 ruling: derive(seed, absolute world metres) via the
derive_at_metres family is the only derive core; the batch 64x32
pseudo-grid is re-scoped as a survey raster with a real SurveyCellPos
newtype (role name, not a D-243 rung); derive_district_profile becomes
a thin wrapper at survey-cell-centre positions with binding basin and
riparian preservation; point-feature terrain judgments move to exact
world positions; D-255 step canvases are fenced to the window family.
LayerRegionOutput rebuild deferred to T-1181 (tripwire verified: sole
reader is the region_grid overlay); voxel carrier deferred to Phase 5.

Consults: Tyre APPROVE-WITH-CHANGES, Dudley FEASIBLE-WITH-CHANGES —
both incorporated. Discharges the T-1174 blocker on D-255's T-1181
step-canvas envelope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 00:39:48 +02:00