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>
This commit is contained in:
2026-07-25 16:10:27 +02:00
co-authored by Claude Fable 5
parent befdb689c1
commit 8da9670e0f
28 changed files with 1244 additions and 467 deletions
+115 -223
View File
@@ -83,46 +83,22 @@ pub const DISTRICT_WINDOW_MAX_N_REGION: u32 =
const WIRE_CAP_CELLS_SQRT: u32 = 64;
const _: () = assert!(WIRE_CAP_CELLS_SQRT * WIRE_CAP_CELLS_SQRT == WIRE_CAP_CELLS);
/// [`AtlasLayerRequest::window_granularity`] encoding (T-1150, zoom ladder
/// design doc §3/§5): the number of derived cells per district side. `0` on
/// the wire (the `#[serde(default)]` absent case) and `1` both mean district
/// spacing (2,048 m/cell, [`DISTRICT_WINDOW_MAX_N`]'s existing behavior,
/// byte-compatible with every pre-T-1150 caller). `4` means quarter spacing
/// (512 m/cell, D-243) — Option B from the design doc: full reclassification
/// at the finer spacing via `derive_at_metres`, not a coarser-cell
/// interpolation. No other values are legal; `resolve_window_granularity`
/// clamps unrecognized values down to district (never trust the wire, same
/// discipline as `window_n`).
/// Finer-than-district spacing multipliers (T-1150, zoom ladder design doc
/// §3/§5): the number of derived cells per district side. `1` = district
/// spacing (2,048 m/cell, [`DISTRICT_WINDOW_MAX_N`]'s existing behavior).
/// `4` = quarter spacing (512 m/cell, D-243) — Option B from the design doc:
/// full reclassification at the finer spacing via `derive_at_metres`, not a
/// coarser-cell interpolation.
///
/// **T-1159:** these used to also be legal VALUES of the wire-facing
/// `AtlasLayerRequest::window_granularity: u32` field (resolved via the
/// now-removed `resolve_window_granularity`) — that field is retired, fully
/// shadowed by [`WindowGranularity`] since T-1152. These constants remain as
/// internal spacing-multiplier values (see [`WindowGranularity::spacing_multiplier`],
/// [`clamp_window_n`]).
pub const WINDOW_GRANULARITY_DISTRICT: u32 = 1;
pub const WINDOW_GRANULARITY_QUARTER: u32 = 4;
/// The `granularity: u32` value [`DistrictWindowLayer`]'s echo (and the
/// internal cache/coalescing keys) use for [`WindowGranularity::Region`] —
/// **a key-space value, never a legal WIRE INPUT.** [`resolve_window_granularity`]
/// (the legacy field's resolver) never produces this value, and a client
/// sending it on the wire in the legacy `window_granularity` field is
/// indistinguishable from any other unrecognized value — it still resolves
/// to `District` (§ `resolve_window_granularity`'s exhaustive fallback), NOT
/// `Region`. The only way to actually request `Region` is
/// `window_granularity_v2 = Some(WindowGranularity::Region)`.
///
/// **Why this exists at all, given `Region` has no legacy representation:**
/// the aliasing discipline (T-1150 design doc §3, the mandatory
/// granularity-4-vs-1 test) requires that every distinct [`WindowGranularity`]
/// occupy a distinct slot in [`DistrictWindowKey`]/the coalescing key, both of
/// which carry a `u32` granularity component for wire back-compat. Reusing
/// `0` (today's "absent" sentinel, mapped to `District`) or any value
/// `resolve_window_granularity` could legally receive would silently alias a
/// `Region` window onto a `District` or `Quarter` cache slot depending on
/// what a future caller happened to pass — exactly the bug class §3 exists
/// to close. `u32::MAX` can never collide with a real multiplier (multipliers
/// are small integers by construction — 1, 4, and any future finer rung),
/// so it's the natural "this is a key-space tag, not a spacing multiplier"
/// value. A T-1152-aware client reads `granularity_v2` and never looks at
/// this number for `Region` responses; it exists purely so the legacy `u32`
/// slot in the key tuple stays total and never lies about aliasing.
pub const WINDOW_GRANULARITY_REGION_KEY: u32 = u32::MAX;
/// Server-side wire-size ceiling (T-1150, design doc §3 "Cell-count cap"):
/// `window_n² × granularity² ≤ WIRE_CAP_CELLS`. At `WIRE_CAP_CELLS = 4,096`,
/// district `n=64` (the existing [`DISTRICT_WINDOW_MAX_N`] cap) sits exactly
@@ -161,20 +137,19 @@ pub const WIRE_CAP_CELLS: u32 = 4_096;
/// reasoning `MorphologyZone`'s exhaustive-match discipline already
/// established for this codebase (D-239 §6).
///
/// **Precedence over the legacy `u32` field (documented here, the single
/// place both fields are reconciled):** [`AtlasLayerRequest::window_granularity_v2`]
/// wins whenever present and non-`None`; the legacy `u32`
/// [`AtlasLayerRequest::window_granularity`] is consulted ONLY when
/// `window_granularity_v2` is absent (`#[serde(default)]`, every pre-T-1152
/// client). This is a strict either/or, not a merge — a client sending BOTH
/// fields (a mixed old/new build, or a future client hedging compatibility)
/// gets the `v2` field's answer, silently ignoring the legacy `u32`. See
/// [`resolve_window_granularity_v2`], the single widening point for this
/// enum (mirroring `resolve_window_granularity`'s role for the legacy field).
/// **Resolution (T-1152, simplified T-1159):** [`AtlasLayerRequest::window_granularity_v2`]
/// resolves directly via [`resolve_window_granularity_v2`], the single
/// widening point for this enum. Absent (`#[serde(default)]`, `None`)
/// resolves to `District`.
///
/// **T-1159:** this used to also reconcile against a legacy
/// `AtlasLayerRequest::window_granularity: u32` field (whenever THIS field
/// was absent) — that field is retired, fully shadowed since T-1152 and
/// never sent as anything but its byte-compatible default by any caller in
/// this codebase (no external client exists, single-repo client/server pair).
///
/// **Unknown → District** at every resolution boundary (never trust the
/// wire) — same posture as the legacy `u32` path and every other wire-decoded
/// enum in this module.
/// wire) — same posture as every other wire-decoded enum in this module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum WindowGranularity {
/// 512 m/cell (D-243 `QUARTER_M`) — finer than district, T-1150 Option B.
@@ -207,14 +182,18 @@ impl WindowGranularity {
}
}
/// The legacy `window_granularity: u32` value this variant maps to/from
/// for finer-than-district rungs — `None` for `Region`, which the legacy
/// field cannot express by construction (Tyre's note; this is precisely
/// the gap the enum exists to close). Used only by
/// [`resolve_window_granularity_v2`]'s legacy-fallback branch and by
/// [`DistrictWindowLayer`]'s echo, which still carries the legacy `u32`
/// unchanged for wire back-compat (see that struct's doc).
fn legacy_u32(self) -> Option<u32> {
/// The finer-than-district spacing multiplier ([`WINDOW_GRANULARITY_DISTRICT`]
/// / [`WINDOW_GRANULARITY_QUARTER`]) this variant corresponds to — `None`
/// for `Region`, which has no such multiplier (its spacing is coarser,
/// not a finer subdivision of a district). Used only by
/// [`clamp_window_n_v2`]'s District/Quarter branch to reuse
/// [`clamp_window_n`]'s per-axis-cap math rather than re-deriving it.
///
/// **T-1159:** this used to double as the wire-back-compat value for the
/// now-retired `window_granularity: u32` echo (`DistrictWindowLayer.granularity`)
/// — that role, and the `key_u32()` method that served it, are gone; this
/// is purely an internal spacing-multiplier lookup now.
fn spacing_multiplier(self) -> Option<u32> {
match self {
WindowGranularity::District => Some(WINDOW_GRANULARITY_DISTRICT),
WindowGranularity::Quarter => Some(WINDOW_GRANULARITY_QUARTER),
@@ -222,19 +201,6 @@ impl WindowGranularity {
}
}
/// The `u32` this variant occupies in [`DistrictWindowKey`]/the
/// coalescing key/`DistrictWindowLayer.granularity`'s echo — total over
/// every variant (unlike [`Self::legacy_u32`], which is partial). Finer-
/// than-district variants echo their real legacy multiplier (so a
/// T-1150-only client — one that reads `granularity` but has never heard
/// of `granularity_v2` — still sees the correct, meaningful number for
/// District/Quarter); `Region` echoes the reserved
/// [`WINDOW_GRANULARITY_REGION_KEY`] key-space tag (see that const's doc
/// for why `0` or any real multiplier would be unsafe here).
fn key_u32(self) -> u32 {
self.legacy_u32().unwrap_or(WINDOW_GRANULARITY_REGION_KEY)
}
/// The derived cell-grid side length (in CELLS, at this granularity) for
/// a window whose extent is `n` DISTRICTS (T-1152 step 4: "work out what
/// n means at region granularity against D-243's region=100-district
@@ -276,55 +242,22 @@ impl WindowGranularity {
}
}
/// Resolve a wire-supplied `window_granularity` value to one of the two legal
/// granularities, clamping anything else down to district spacing — **never
/// trust the wire** (same posture as `window_n`/`normalize_window_center`).
///
/// **This is THE single widening point (Tyre C2, PR #191 review) for the
/// LEGACY `u32` field only.** The field only ever expresses finer-than-district
/// integer multiples (see [`AtlasLayerRequest::window_granularity`]'s doc for
/// the full type-seam contract); adding a future finer rung means adding its
/// legal value here and nowhere else. Do NOT add a value < 1 or attempt to
/// encode coarser-than-district rungs (region/orbital) through this function
/// — that direction is [`WindowGranularity::Region`]'s job via
/// [`resolve_window_granularity_v2`], never a new magic `u32` value (the R5
/// redesign this enum performs is EXACTLY the alternative to doing that).
fn resolve_window_granularity(raw: u32) -> u32 {
if raw == WINDOW_GRANULARITY_QUARTER {
WINDOW_GRANULARITY_QUARTER
} else {
WINDOW_GRANULARITY_DISTRICT
}
}
/// Resolve a request's granularity to a [`WindowGranularity`] — **the single
/// widening point for the full (finer- and coarser-than-district) vocabulary**
/// (T-1152, mirroring [`resolve_window_granularity`]'s role for the legacy
/// `u32` alone). Precedence (documented once, here — see
/// [`WindowGranularity`]'s struct doc for the rationale):
/// (T-1152). Absent (`None`, `#[serde(default)]`) resolves to
/// [`WindowGranularity::District`] — unknown/malformed variants can't reach
/// this function at all (`rmp_serde` rejects an unrecognized enum variant
/// name at decode time, so "unknown" for this field means "absent", never
/// "present but garbage").
///
/// 1. `window_granularity_v2` present → resolved directly (unknown/malformed
/// variants can't reach this function at all — `rmp_serde` rejects an
/// unrecognized enum variant name at decode time, so "unknown" for THIS
/// field means "absent", not "present but garbage"; the legacy `u32`
/// path is what actually needs the value-level fallback because a `u32`
/// has no closed vocabulary).
/// 2. `window_granularity_v2` absent → fall back to the legacy `u32` path via
/// [`resolve_window_granularity`], mapped onto the two variants it can
/// express.
///
/// A future coarser-than-region rung is added by widening this function's
/// match AND [`WindowGranularity`]'s variant list together — never by
/// smuggling a new value through the legacy `u32` (that field's ceiling is
/// permanent per Tyre's note, not a restriction this function works around).
/// **T-1159:** this function used to fall back to the legacy `window_granularity: u32`
/// field (via the now-removed `resolve_window_granularity`) when
/// `window_granularity_v2` was absent — that fallback is retired along with
/// the field itself (fully shadowed since T-1152, no pre-T-1152 client
/// exists). A future coarser-than-region rung is added by widening this
/// function's match AND [`WindowGranularity`]'s variant list together.
fn resolve_window_granularity_v2(req: &AtlasLayerRequest) -> WindowGranularity {
match req.window_granularity_v2 {
Some(g) => g,
None => match resolve_window_granularity(req.window_granularity) {
WINDOW_GRANULARITY_QUARTER => WindowGranularity::Quarter,
_ => WindowGranularity::District,
},
}
req.window_granularity_v2.unwrap_or(WindowGranularity::District)
}
/// Clamp `window_n` against BOTH the existing per-axis cap
@@ -392,7 +325,7 @@ fn clamp_window_n_v2(raw_n: u32, granularity: WindowGranularity) -> u32 {
WindowGranularity::District | WindowGranularity::Quarter => clamp_window_n(
raw_n,
granularity
.legacy_u32()
.spacing_multiplier()
.unwrap_or(WINDOW_GRANULARITY_DISTRICT),
),
WindowGranularity::Region => {
@@ -520,36 +453,19 @@ pub struct AtlasLayerRequest {
/// from the wire** (D-226 T-1124 amendment §4).
#[serde(default)]
pub window_n: u32,
/// Window derivation granularity (T-1150, zoom ladder design doc §3/§5).
/// `0` (absent, `#[serde(default)]`) or `1` = district spacing (2,048 m,
/// today's behavior, byte-compatible with every pre-T-1150 caller); `4` =
/// quarter spacing (512 m, D-243). See [`WINDOW_GRANULARITY_DISTRICT`] /
/// [`WINDOW_GRANULARITY_QUARTER`]. Resolved via
/// [`resolve_window_granularity`] — **never trusted from the wire**,
/// unrecognized values fall back to district.
/// Window derivation granularity (T-1152, R5 redesign — see
/// [`WindowGranularity`]'s doc for the full rationale). `#[serde(default)]`
/// (`None`) resolves to [`WindowGranularity::District`] (today's default
/// behavior, byte-compatible with every pre-T-1150 caller) via
/// [`resolve_window_granularity_v2`] — **never trusted from the wire**,
/// unrecognized/absent values fall back to district.
///
/// **Type seam (Tyre C2, PR #191 review):** this field expresses ONLY
/// finer-than-district integer multiples of the district spacing — `1`
/// and `4` are legal today, and each new finer rung (e.g. a future
/// block/tile value) is a deliberate widening of
/// [`resolve_window_granularity`]'s whitelist, the single point where
/// that widening happens. It CANNOT express coarser-than-district rungs
/// (region/orbital, granularity < 1) — reusing this field for those is
/// explicitly out of scope; design doc §5/§9 R5 requires a
/// signed/log-scale value or an explicit rung enum instead. Do not smuggle
/// a "granularity 0 means region" convention into this `u32` — that is
/// the redesign R5 already flags, not a value to add here.
#[serde(default)]
pub window_granularity: u32,
/// The R5-redesigned granularity vocabulary (T-1152), able to express
/// coarser-than-district rungs the legacy `window_granularity: u32`
/// cannot (Tyre's wire-contract note — see [`WindowGranularity`]'s doc
/// for the full rationale). `#[serde(default)]` (`None`) is the absent
/// case: every pre-T-1152 client (and every T-1150 client that only ever
/// sends the legacy `u32`) omits this field entirely and is byte-compatible
/// — [`resolve_window_granularity_v2`] falls back to the legacy field
/// when this is `None`. When BOTH fields are present, THIS field wins
/// (documented once, on [`WindowGranularity`], not duplicated here).
/// **T-1159:** the legacy `window_granularity: u32` field this superseded
/// (T-1150's finer-than-district-only encoding) is retired — this enum
/// fully shadowed it since T-1152 landed, and no pre-T-1152 client exists
/// (single-repo client/server pair). See D-255(c): the `district_window`
/// carrier itself stays alive byte-unchanged for its existing consumer;
/// only the redundant `u32` alongside this enum is gone.
#[serde(default)]
pub window_granularity_v2: Option<WindowGranularity>,
/// Octave cutoff for the invented-terrain scatter (T-1149's
@@ -805,25 +721,14 @@ pub struct DistrictWindowLayer {
/// (T-1150 design doc §2: "the window's `n` stays the DISTRICT extent").
/// The derived cell grid's actual side length is `n * granularity`.
pub n: u32,
/// Derivation granularity (T-1150): [`WINDOW_GRANULARITY_DISTRICT`] (1)
/// or [`WINDOW_GRANULARITY_QUARTER`] (4) for finer-than-district rungs;
/// [`WINDOW_GRANULARITY_REGION_KEY`] (a reserved key-space tag, NOT a
/// spacing multiplier) when [`Self::granularity_v2`] is `Region` — see
/// that constant's doc. Kept unchanged (never repurposed or removed) for
/// wire back-compat with every pre-T-1152 client, which reads only this
/// field and has no concept of `granularity_v2`. Echoed so the client's
/// cache key and staleness guard can distinguish windows at different
/// finer-than-district rungs requested at the identical `(center, n)`.
pub granularity: u32,
/// The R5-redesigned granularity (T-1152) — see [`WindowGranularity`]'s
/// doc. Always populated (never `None`): the server always resolves a
/// concrete rung internally via [`resolve_window_granularity_v2`]
/// regardless of which wire field the request used, so the response
/// always carries the enum echo alongside the legacy `u32` one. A
/// T-1152-aware client reads THIS field for staleness/cache-key
/// comparison; `granularity` (above) exists only for pre-T-1152 clients,
/// who never see `Region` responses in the first place (they have no way
/// to request one).
/// concrete rung internally via [`resolve_window_granularity_v2`].
///
/// **T-1159:** the legacy `granularity: u32` echo this field used to sit
/// alongside (a wire-back-compat value for pre-T-1152 clients, which do
/// not exist — single-repo client/server pair) is retired. This enum
/// echo has been the sole granularity signal since T-1152.
pub granularity_v2: WindowGranularity,
/// The `min_wavelength_m` octave cutoff (T-1149) this window was derived
/// with, in whole metres (`0` = no cutoff). Echoed for the same reason as
@@ -1616,7 +1521,6 @@ pub fn build_district_window_layer(
DistrictWindowLayer {
center,
n,
granularity: granularity.key_u32(),
granularity_v2: granularity,
min_wl_m,
morphology,
@@ -1713,7 +1617,6 @@ fn build_district_window_layer_serial(
DistrictWindowLayer {
center,
n,
granularity: granularity.key_u32(),
granularity_v2: granularity,
min_wl_m,
morphology,
@@ -2295,6 +2198,11 @@ pub fn handle_atlas_request(
// in DistrictProfile.basin_direction (BodyWorldState.districts) and is
// not needed again here. Supply an empty map.
survey_basin_dirs: std::collections::BTreeMap::new(),
// T-1169: mirrors state.attractors.clone() above — feature_names
// is stored on BodyWorldState (see cascade::into_body_world_state)
// and cloned back out here, same reconstruction discipline as
// every other Layer1Output field on this cache-hit path.
feature_names: state.feature_names.clone(),
};
let district_grid = build_district_grid(state);
let road_graph = build_road_graph_layer(state);
@@ -2346,6 +2254,36 @@ pub fn handle_atlas_request(
}
None => (Vec::new(), None),
};
// Pre-resolve this body's reserved river/mountain name pools so
// the Rayon work item stays DB-free (T-1169, D-223, same D-225
// pattern as `cities` above). Read failure is non-fatal: log and
// fall back to no names (attach_feature_names degrades gracefully
// — every attractor simply gets no name).
let (river_names, mountain_names) = match city_reader {
Some(reader) => match reader.read_body_feature_names(&req.body_id) {
Ok(rows) => {
let mut rivers = Vec::new();
let mut mountains = Vec::new();
for row in rows {
match row.feature_type.as_str() {
"river" => rivers.push(row.name),
"mountain" => mountains.push(row.name),
_ => {}
}
}
(rivers, mountains)
}
Err(e) => {
tracing::warn!(
body_id = %req.body_id,
error = %e,
"feature name read failed; attaching no names"
);
(Vec::new(), Vec::new())
}
},
None => (Vec::new(), Vec::new()),
};
// Pre-resolve body physical params so the Rayon work item stays
// DB-free (D-225 pattern). Read failures are non-fatal: log and
// fall back to None (cascade stops at Settlement, pre-T-1032
@@ -2373,6 +2311,8 @@ pub fn handle_atlas_request(
cities,
dominant_faction,
body_params,
river_names,
mountain_names,
},
GenPriority::Immediate,
);
@@ -2457,6 +2397,7 @@ mod tests {
river_network: RiverNetwork::default(),
drainage_basins: vec![],
attractors: vec![],
feature_names: vec![],
placements: vec![],
road_graph: crate::atlas::road_graph::RoadGraph::default(),
quarters: std::collections::BTreeMap::new(),
@@ -2636,7 +2577,6 @@ mod tests {
assert_eq!(layer.center, (10, -5));
assert_eq!(layer.n, n);
assert_eq!(layer.granularity, WINDOW_GRANULARITY_DISTRICT);
assert_eq!(layer.granularity_v2, WindowGranularity::District);
assert_eq!(layer.min_wl_m, 0);
let cells = (n * n) as usize;
@@ -3894,7 +3834,6 @@ mod tests {
let mk = |center, n| DistrictWindowLayer {
center,
n,
granularity: WINDOW_GRANULARITY_DISTRICT,
granularity_v2: WindowGranularity::District,
min_wl_m: 0,
morphology: vec![0; (n * n) as usize],
@@ -3943,7 +3882,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((0, 0)),
window_n: DISTRICT_WINDOW_MAX_N * 10, // wildly over the wire — must clamp, not trust
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -4004,8 +3942,7 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((0, 0)),
window_n: 32,
window_granularity: WINDOW_GRANULARITY_QUARTER,
window_granularity_v2: None,
window_granularity_v2: Some(WindowGranularity::Quarter),
window_min_wl_m: 0,
};
@@ -4035,7 +3972,8 @@ mod tests {
});
let layer = window_completion.expect("DeriveWindow must complete for GJ1c");
assert_eq!(
layer.granularity, WINDOW_GRANULARITY_QUARTER,
layer.granularity_v2,
WindowGranularity::Quarter,
"granularity must echo back as requested (4 is within budget on its own)"
);
assert_eq!(
@@ -4045,36 +3983,9 @@ mod tests {
}
// -------------------------------------------------------------------
// resolve_window_granularity / clamp_window_n (T-1150)
// clamp_window_n (T-1150)
// -------------------------------------------------------------------
#[test]
fn resolve_window_granularity_maps_known_values() {
assert_eq!(resolve_window_granularity(0), WINDOW_GRANULARITY_DISTRICT);
assert_eq!(
resolve_window_granularity(WINDOW_GRANULARITY_DISTRICT),
WINDOW_GRANULARITY_DISTRICT
);
assert_eq!(
resolve_window_granularity(WINDOW_GRANULARITY_QUARTER),
WINDOW_GRANULARITY_QUARTER
);
}
/// Never trust the wire: an unrecognized granularity value (garbage, or a
/// future rung not yet implemented) falls back to district, never panics
/// or propagates un-vetted.
#[test]
fn resolve_window_granularity_unknown_value_falls_back_to_district() {
for garbage in [2, 3, 5, 100, u32::MAX] {
assert_eq!(
resolve_window_granularity(garbage),
WINDOW_GRANULARITY_DISTRICT,
"unrecognized granularity {garbage} must fall back to district"
);
}
}
/// District granularity: the per-axis DISTRICT_WINDOW_MAX_N cap alone
/// governs (64² × 1² = 4,096 = WIRE_CAP_CELLS exactly, so the cap is
/// never tighter than DISTRICT_WINDOW_MAX_N at granularity 1).
@@ -4283,7 +4194,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((10, -5)),
window_n: 4,
window_granularity: WINDOW_GRANULARITY_DISTRICT,
window_granularity_v2: None,
window_min_wl_m: 4_000,
};
@@ -4292,7 +4202,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((10, -5)),
window_n: 4,
window_granularity: WINDOW_GRANULARITY_DISTRICT,
window_granularity_v2: None,
window_min_wl_m: 4_300,
};
@@ -4510,7 +4419,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((12276, 3021)),
window_n: 4,
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -4567,7 +4475,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((4, 383)), // the hand-computed canonical twin
window_n: 4,
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -4650,7 +4557,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: center,
window_n: n,
window_granularity: WINDOW_GRANULARITY_DISTRICT,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -4659,8 +4565,7 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: center,
window_n: n,
window_granularity: WINDOW_GRANULARITY_QUARTER,
window_granularity_v2: None,
window_granularity_v2: Some(WindowGranularity::Quarter),
window_min_wl_m: 0,
};
@@ -4750,8 +4655,6 @@ mod tests {
.district_window
.expect("quarter request must hit its own cached entry");
assert_eq!(district_layer.granularity, WINDOW_GRANULARITY_DISTRICT);
assert_eq!(quarter_layer.granularity, WINDOW_GRANULARITY_QUARTER);
assert_eq!(district_layer.granularity_v2, WindowGranularity::District);
assert_eq!(quarter_layer.granularity_v2, WindowGranularity::Quarter);
// n echoes the DISTRICT extent unchanged at both granularities
@@ -4810,7 +4713,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: center,
window_n: n,
window_granularity: WINDOW_GRANULARITY_DISTRICT,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -4819,9 +4721,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: center,
window_n: n,
// Legacy field is irrelevant here — window_granularity_v2 takes
// precedence per resolve_window_granularity_v2's documented rule.
window_granularity: 0,
window_granularity_v2: Some(WindowGranularity::Region),
window_min_wl_m: 0,
};
@@ -4911,12 +4810,7 @@ mod tests {
assert_eq!(district_layer.granularity_v2, WindowGranularity::District);
assert_eq!(region_layer.granularity_v2, WindowGranularity::Region);
// The legacy u32 echo must NEVER collide with a real multiplier —
// WINDOW_GRANULARITY_REGION_KEY is the reserved key-space tag (see
// that constant's doc), distinct from both WINDOW_GRANULARITY_DISTRICT
// (1) and WINDOW_GRANULARITY_QUARTER (4).
assert_eq!(region_layer.granularity, WINDOW_GRANULARITY_REGION_KEY);
assert_ne!(region_layer.granularity, district_layer.granularity);
assert_ne!(region_layer.granularity_v2, district_layer.granularity_v2);
// n echoes the DISTRICT extent unchanged (design doc §2), same as
// every other rung — the derived CELL GRID is what differs.
@@ -4959,7 +4853,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((0, 0)),
window_n: DISTRICT_WINDOW_MAX_N_REGION * 10,
window_granularity: 0,
window_granularity_v2: Some(WindowGranularity::Region),
window_min_wl_m: 0,
};
@@ -5019,7 +4912,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: Some((12276, 3021)), // raw, out-of-range
window_n: 4,
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -5100,7 +4992,6 @@ mod tests {
let window = DistrictWindowLayer {
center: (10, -5),
n: 2,
granularity: WINDOW_GRANULARITY_DISTRICT,
granularity_v2: WindowGranularity::District,
min_wl_m: 0,
morphology: vec![0, 8, 14, 16],
@@ -5161,8 +5052,8 @@ mod tests {
assert_eq!(decoded.window_center, None);
assert_eq!(decoded.window_n, 0);
assert_eq!(
decoded.window_granularity, 0,
"T-1150: absent window_granularity decodes to 0 (district), byte-compatible"
decoded.window_granularity_v2, None,
"T-1152/T-1159: absent window_granularity_v2 decodes to None (district), byte-compatible"
);
assert_eq!(
decoded.window_min_wl_m, 0,
@@ -5514,6 +5405,7 @@ mod tests {
river_network: RiverNetwork::default(),
drainage_basins: vec![],
attractors: vec![],
feature_names: vec![],
placements: vec![],
road_graph: crate::atlas::road_graph::RoadGraph::default(),
quarters: std::collections::BTreeMap::new(),
@@ -5784,7 +5676,6 @@ mod tests {
up_to: CascadeLayer::Topography,
window_center: None,
window_n: 0,
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
}
@@ -5861,6 +5752,7 @@ mod tests {
river_network: RiverNetwork::default(),
drainage_basins: vec![],
attractors: vec![],
feature_names: vec![],
placements: vec![],
road_graph: crate::atlas::road_graph::RoadGraph::default(),
quarters: std::collections::BTreeMap::new(),