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:
+115
-223
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user