feat(simulation): T-1151 window par_iter + T-1150 granularity carrier (five touch points + aliasing tests)

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.
This commit is contained in:
2026-07-22 00:27:40 +02:00
parent afff859a2c
commit 3e87fd5b4f
14 changed files with 1027 additions and 118 deletions
+28
View File
@@ -214,6 +214,32 @@ func test_encode_atlas_layer_request_carries_window_params() -> void:
assert_that(decoded.value.get("window_n")).is_equal(32)
## T-1150: window_granularity/window_min_wl_m are OMITTED (not sent as 0)
## when at their default — a windowed request that doesn't pass them (every
## pre-T-1150 window caller) is byte-identical to pre-T-1150 wire traffic,
## same contract as window_center/window_n's own default-omission above.
func test_encode_atlas_layer_request_omits_granularity_and_min_wl_by_default() -> void:
var bytes := Protocol.encode_atlas_layer_request(
"GJ1c", "Topography", Vector2i(140, 260), 32
)
var decoded = Messagepack.decode(bytes)
assert_bool(decoded.value.has("window_granularity")).is_false()
assert_bool(decoded.value.has("window_min_wl_m")).is_false()
## T-1150: a quarter-granularity request with an octave cutoff carries both
## new fields verbatim, unclamped (the server owns
## resolve_window_granularity()/clamp_window_n() — never trusted from the
## wire, same posture as window_n).
func test_encode_atlas_layer_request_carries_granularity_and_min_wl() -> void:
var bytes := Protocol.encode_atlas_layer_request(
"GJ1c", "Topography", Vector2i(140, 260), 32, 4, 512
)
var decoded = Messagepack.decode(bytes)
assert_that(decoded.value.get("window_granularity")).is_equal(4)
assert_that(decoded.value.get("window_min_wl_m")).is_equal(512)
## §2: district_window is a distinct payload (echoes center/n for the
## client's staleness guard) but the codec passthrough is the same shape as
## every sibling layer — raw.get(), no reshaping. Field types follow the
@@ -224,6 +250,8 @@ func test_atlas_response_district_window_passthrough() -> void:
var window := {
"center": [140, 260],
"n": 32,
"granularity": 4, # T-1150: quarter granularity, passed through same as every other field
"min_wl_m": 512,
"morphology": PackedByteArray([8, 14, 0, 5]),
"elev_q": PackedByteArray([40, 62, 5, 88]),
"temp_dc": [120, 95, AtlasOverlayColors.REGION_TEMP_NONE_DC, 60],
+61
View File
@@ -119,3 +119,64 @@ func test_clear_empties_the_cache() -> void:
cache.clear()
assert_int(cache.size()).is_equal(0)
assert_bool(cache.has("GJ1c", Vector2i(0, 0), 32)).is_false()
# =============================================================================
# granularity / min_wl_m (T-1150, zoom ladder design doc §3 aliasing risk)
# =============================================================================
## **MANDATORY aliasing regression (client half, T-1150):** a granularity-4
## (quarter) key and a granularity-1 (district) key at the IDENTICAL
## (body_id, center, n) must be DISTINCT cache keys — this is what prevents a
## quarter-spacing request from silently reading (or overwriting) a
## district-spacing window's cache entry, and vice versa.
func test_make_key_distinguishes_granularity_at_identical_body_center_n() -> void:
var k_district := AtlasWindowCache.make_key(
"GJ1c", Vector2i(10, 20), 32, AtlasWindowCache.DISTRICT_GRANULARITY
)
var k_quarter := AtlasWindowCache.make_key("GJ1c", Vector2i(10, 20), 32, 4)
assert_str(k_district).is_not_equal(k_quarter)
## Same aliasing risk, the other new axis: two requests identical except for
## `min_wl_m` (the octave cutoff) must not collide either — different cutoffs
## are different derived payloads (T-1149/T-1150).
func test_make_key_distinguishes_min_wl_m_at_identical_body_center_n_granularity() -> void:
var k_uncut := AtlasWindowCache.make_key(
"GJ1c", Vector2i(10, 20), 32, AtlasWindowCache.DISTRICT_GRANULARITY, 0
)
var k_cut := AtlasWindowCache.make_key(
"GJ1c", Vector2i(10, 20), 32, AtlasWindowCache.DISTRICT_GRANULARITY, 512
)
assert_str(k_uncut).is_not_equal(k_cut)
## Omitting granularity/min_wl_m (every pre-T-1150 call site) must produce the
## SAME key as passing the explicit district/no-cutoff defaults — byte/string
## compatibility for existing callers, not just "doesn't crash".
func test_omitted_granularity_and_min_wl_m_match_explicit_district_defaults() -> void:
var k_omitted := AtlasWindowCache.make_key("GJ1c", Vector2i(10, 20), 32)
var k_explicit := AtlasWindowCache.make_key(
"GJ1c", Vector2i(10, 20), 32, AtlasWindowCache.DISTRICT_GRANULARITY, 0
)
assert_str(k_omitted).is_equal(k_explicit)
## End-to-end through put()/get_window()/has() (not just make_key() in
## isolation): a quarter-granularity window and a district-granularity window
## at the identical (body, center, n) must both be independently retrievable,
## neither one clobbering or masking the other.
func test_district_and_quarter_windows_coexist_at_identical_body_center_n() -> void:
var cache := AtlasWindowCache.new()
var district_window := {"granularity": AtlasWindowCache.DISTRICT_GRANULARITY, "id": "district"}
var quarter_window := {"granularity": 4, "id": "quarter"}
cache.put("GJ1c", Vector2i(10, 20), 32, district_window, AtlasWindowCache.DISTRICT_GRANULARITY)
cache.put("GJ1c", Vector2i(10, 20), 32, quarter_window, 4)
assert_int(cache.size()).is_equal(2)
assert_that(
cache.get_window("GJ1c", Vector2i(10, 20), 32, AtlasWindowCache.DISTRICT_GRANULARITY)
).is_equal(district_window)
assert_that(cache.get_window("GJ1c", Vector2i(10, 20), 32, 4)).is_equal(quarter_window)