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
+30 -12
View File
@@ -342,21 +342,23 @@ fn single_tick_drains_all_ready_inbound_frames() {
use settled_reach_server::atlas::layer_proxy::AtlasLayerRequest;
use settled_reach_server::bridge::{
receive_bridge_inputs, AtlasRequestBuffer, BridgeResource, BrowseRequestBuffer,
CityNamesRequestBuffer, HandshakeState, ServerRunning, StarMapRequestBuffer,
StepCanvasRequestBuffer,
CityNamesRequestBuffer, FeatureNamesRequestBuffer, HandshakeState, ServerRunning,
StarMapRequestBuffer, StepCanvasRequestBuffer,
};
use settled_reach_server::simulation::input::InputQueue;
let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind");
let server_addr = listener.local_addr().expect("failed to get local address");
// Client: five frames back-to-back in one tick window — two input
// batches plus one of EACH request shape (atlas, star-map, city-names:
// the full D-225/T-949 demux surface over the real framing/poll path —
// PR #176 review H6). Returns the stream so it stays open until
// assertions complete (no EOF race).
// Client: six frames back-to-back in one tick window — two input
// batches plus one of EACH request shape (atlas, star-map, city-names,
// feature-names: the full D-225/T-949/T-1169 demux surface over the real
// framing/poll path — PR #176 review H6). Returns the stream so it stays
// open until assertions complete (no EOF race).
let client_handle = thread::spawn(move || {
use settled_reach_server::atlas::atlas_data_proxy::{CityNamesRequest, StarMapRequest};
use settled_reach_server::atlas::atlas_data_proxy::{
CityNamesRequest, FeatureNamesRequest, StarMapRequest,
};
let mut stream = TcpStream::connect(server_addr).expect("failed to connect");
for tick in [20u64, 21] {
@@ -372,7 +374,6 @@ fn single_tick_drains_all_ready_inbound_frames() {
up_to: CascadeLayer::Topography,
window_center: None,
window_n: 0,
window_granularity: 0,
window_granularity_v2: None,
window_min_wl_m: 0,
};
@@ -387,6 +388,13 @@ fn single_tick_drains_all_ready_inbound_frames() {
};
let payload = rmp_serde::to_vec_named(&cn).expect("failed to serialize city names");
write_framed(&mut stream, &payload).expect("write city names frame");
let fn_req = FeatureNamesRequest {
feature_names: true,
body_id: "GJ1c".into(),
};
let payload =
rmp_serde::to_vec_named(&fn_req).expect("failed to serialize feature names");
write_framed(&mut stream, &payload).expect("write feature names frame");
stream
});
@@ -406,6 +414,7 @@ fn single_tick_drains_all_ready_inbound_frames() {
world.init_resource::<CityNamesRequestBuffer>();
world.init_resource::<BrowseRequestBuffer>();
world.init_resource::<StepCanvasRequestBuffer>();
world.init_resource::<FeatureNamesRequestBuffer>();
world
.run_system_once(receive_bridge_inputs)
@@ -433,6 +442,13 @@ fn single_tick_drains_all_ready_inbound_frames() {
"the city-names request must drain in the same tick (H6: real wire path)"
);
assert_eq!(city_names[0].1.body_id, "GJ1c");
let feature_names = &world.resource::<FeatureNamesRequestBuffer>().0;
assert_eq!(
feature_names.len(),
1,
"the feature-names request must drain in the same tick (T-1169: real wire path)"
);
assert_eq!(feature_names[0].1.body_id, "GJ1c");
assert!(
world.resource::<ServerRunning>().0,
"draining must not shut the server down"
@@ -502,8 +518,9 @@ fn new_multi_connection_world() -> (bevy_ecs::world::World, std::net::SocketAddr
use settled_reach_server::bridge::{
AtlasRequestBuffer, AtlasResponseBuffer, BridgeResource, BrowseRequestBuffer,
BrowseResponseBuffer, CityNamesRequestBuffer, CityNamesResponseBuffer, ConnectionListener,
HandshakeState, PendingConnections, ServerRunning, SnapshotBuffer, StarMapRequestBuffer,
StarMapResponseBuffer, StepCanvasRequestBuffer, StepCanvasResponseBuffer,
FeatureNamesRequestBuffer, FeatureNamesResponseBuffer, HandshakeState, PendingConnections,
ServerRunning, SnapshotBuffer, StarMapRequestBuffer, StarMapResponseBuffer,
StepCanvasRequestBuffer, StepCanvasResponseBuffer,
};
use settled_reach_server::simulation::input::InputQueue;
@@ -531,6 +548,8 @@ fn new_multi_connection_world() -> (bevy_ecs::world::World, std::net::SocketAddr
world.init_resource::<BrowseResponseBuffer>();
world.init_resource::<StepCanvasRequestBuffer>();
world.init_resource::<StepCanvasResponseBuffer>();
world.init_resource::<FeatureNamesRequestBuffer>();
world.init_resource::<FeatureNamesResponseBuffer>();
world.init_resource::<SnapshotBuffer>();
(world, addr)
}
@@ -1515,7 +1534,6 @@ mod connection_zero_window_delivery {
up_to: CascadeLayer::Topography,
window_center: Some(center),
window_n: 4,
window_granularity: 0,
window_granularity_v2: Some(WindowGranularity::Region),
window_min_wl_m: 0,
}
+18 -2
View File
@@ -112,6 +112,14 @@
//! additive at the position level, exactly as designed. No duplicate
//! `(row, col)` positions exist in the final array (verified — the
//! `edge_ids_are_unique` invariant `build_edges` depends on holds).
//!
//! **Fourth deliberate re-pin (T-1169, D-223):** `Layer1Output` gained an
//! additive `feature_names: Vec<FeatureNameAssignment>` field (river-mouth/
//! alpine-peak name assignments from the new `attach_feature_names` cascade
//! wiring). This fixture calls `run_cascade_from_heightmap` with empty
//! `river_names`/`mountain_names` pools (no DB access from this test), so
//! the field serializes as `[]` — a pure additive-shape change, zero effect
//! on every pre-existing field.
use std::path::PathBuf;
@@ -149,8 +157,16 @@ fn cascade_layer0_to_1_matches_golden() {
// ── Layer 1 — downsample, then run the cascade to topography. ───────────
let small = heightmap.downsample(DOWNSAMPLE.0, DOWNSAMPLE.1);
let body_seed = SeedChain::for_body(WORLD_SEED, "GJ1c");
let snapshot =
run_cascade_from_heightmap(body_seed, small, &[], None, None, CascadeLayer::Topography);
let snapshot = run_cascade_from_heightmap(
body_seed,
small,
&[],
None,
None,
&[],
&[],
CascadeLayer::Topography,
);
let layer1 = snapshot.layer1.expect("Layer 1 ran");
let actual = json!({
+1 -2
View File
@@ -7,7 +7,6 @@ use settled_reach_server::atlas::layer_proxy::{
AtlasLayerResponse, AtlasLayerStatus, DistrictWindowLayer, QuarterFootprintEntry,
QuarterFootprintLayer, RegionGridLayer, RoadGraphEdge, RoadGraphLayer, RoadGraphNode,
SettlementEntry, SettlementLayer, SettlementSizeClass, WindowGranularity, REGION_TEMP_NONE_DC,
WINDOW_GRANULARITY_DISTRICT,
};
use settled_reach_server::atlas::region_profile::{SeasonPhase, WeatherState};
use settled_reach_server::atlas::road_graph::RoadNodeKind;
@@ -579,6 +578,7 @@ fn generate_atlas_layer_response_fixtures() {
grid_w: 512,
grid_h: 256,
survey_basin_dirs: std::collections::BTreeMap::new(),
feature_names: vec![],
};
// T-960 §1/§2: a small populated RoadGraphLayer + SettlementLayer, one
// settlement (a capital) connected to one waypoint-free short edge.
@@ -729,7 +729,6 @@ fn generate_atlas_layer_response_fixtures() {
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], // OpenOcean, AlluvialPlain, Alpine, Wetland
+1
View File
@@ -7761,6 +7761,7 @@
"territorial_status": "FrontierUnclaimed"
}
],
"feature_names": [],
"grid_h": 128,
"grid_w": 256,
"river_network": {