From 270dba01473a7e50c1182089b239f2f9a8294e5c Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 23 Jul 2026 15:22:27 +0200 Subject: [PATCH] test(simulation): upstream-cut Mouth-terminus regression pin (PR #197 acceptance follow-through) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live acceptance question (Mouth flag apparently not surviving crop) resolved to hypothesis (b): the terminus genuinely lay outside both test windows — the Lendel mouth chord is ~108.5km, not the ~38km estimated, so a District n=32 window centered on the cell OR the midpoint misses the bisected waterline; a seaward-cell-centered window provably ships Mouth. Hypothesis (a) — upstream-only crops collapsing the flag — was FALSIFIED directly with a constructed window (upstream anchor cut, true terminus in range -> Mouth ships; crop reads only last_in). This test converts that probe into a permanent pin with construction-sanity asserts, closing the blind spot where the existing GJ1c acceptance test's bbox-derived window always contains the whole course. Revert-verified: reintroducing a first_in==0 requirement fails this test by name while the whole- course test blindly passes — exactly the gap. Probe file deleted. cargo test --lib 1877/1877. Tickets: T-1170 Co-Authored-By: Claude Fable 5 --- server/src/atlas/layer_proxy.rs | 131 ++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/server/src/atlas/layer_proxy.rs b/server/src/atlas/layer_proxy.rs index 564f99dbc..37bec5662 100644 --- a/server/src/atlas/layer_proxy.rs +++ b/server/src/atlas/layer_proxy.rs @@ -3105,6 +3105,137 @@ mod tests { ); } + /// **T-1170 PR #197 review round 2 (coordinator's live GJ380c/Lendel + /// repro) — the upstream-cut coverage gap in the test above, closed.** + /// + /// `all_real_gj1c_mouths_resolve_to_mouth_terminus_not_none` derives its + /// window rect from the invented course's OWN min/max point bbox, so by + /// construction that window always contains the WHOLE course (both + /// `first_in == 0` and `last_in == n-1`) — it never exercises a window + /// that cuts the UPSTREAM anchor while the true downstream terminus + /// still falls inside. `crop_course_to_window`'s terminus branch is + /// driven entirely by `last_in`/`hi` (the downstream side); this test is + /// the direct proof that an upstream cut (`first_in > 0`, i.e. `lo > 0`) + /// does NOT collapse the terminus flag to `ContinuesBeyondWindow` — the + /// coordinator's hypothesis (a) from the live-server investigation, + /// falsified here as a permanent regression case rather than only a + /// throwaway probe (`mouth_repro_probe.rs`, deleted after this landed). + /// + /// Window construction: take one real GJ1c mouth edge's full invented + /// course, find the TRUE (uncropped) terminus point, then build a window + /// rect deliberately offset upstream along the course's own tail + /// direction so its near edge sits well past the upstream anchor (cutting + /// it out of range) while its far edge still comfortably contains the + /// true terminus. + #[test] + fn mouth_terminus_survives_an_upstream_only_crop() { + use crate::atlas::drainage; + use crate::atlas::heightmap::load_heightmap_png; + use crate::atlas::river_course; + + let src = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../wiki/star-systems/GJ-1/bodies/GJ1c/heightmap.png"); + let heightmap = + load_heightmap_png(&src, "GJ1c", 0.3).expect("decode committed GJ1c heightmap"); + let small = heightmap.downsample(256, 128); + let dr = drainage::analyze(&small.data, small.width, small.height, small.sea_level); + let ta = crate::atlas::features::TerrainAnalysis::analyze(&small, &dr); + let rn = &dr.river_network; + + let params = crate::atlas::district_profile::BodyParams { + hydrosphere: Some("ocean".into()), + atmosphere: Some("breathable".into()), + planet_class: Some("temperate".into()), + body_radius_km: Some(6371.0), + ..Default::default() + }; + let climate = crate::atlas::district_profile::ClimateConstants::default(); + let seed = SeedChain::root(0xC0FFEE_u64).derive(SeedDomain::Body, 1); + let station_spacing_m = DISTRICT_M as f64; + + let edges = river_course::build_edges(rn); + let edge = edges + .iter() + .find(|e| e.terminus == river_course::EdgeTerminusKind::Mouth) + .expect("GJ1c fixture must have at least one Mouth edge"); + + let course = + river_course::invent_course(seed, edge, &ta, ¶ms, station_spacing_m, 0.0); + assert!( + course.points.len() >= 2, + "need a non-degenerate course to construct a meaningful upstream cut" + ); + let upstream_anchor = course.points[0]; + let true_end = *course.points.last().unwrap(); + + // Window offset upstream along the course's own tail direction (the + // last segment), far enough that the upstream anchor falls outside + // the window but the true terminus stays comfortably inside. + let second_last = course.points[course.points.len().saturating_sub(2)]; + let (dx, dy) = (true_end.0 - second_last.0, true_end.1 - second_last.1); + let len = (dx * dx + dy * dy).sqrt().max(1e-9); + let (ux, uy) = (dx / len, dy / len); + // Half the upstream->downstream distance keeps the window's near + // edge well clear of the upstream anchor for any real mouth chord + // (mouth edges are short, ~one D8 step), while the far edge margin + // below still comfortably covers the terminus. + let chord_m = ((true_end.0 - upstream_anchor.0).powi(2) + + (true_end.1 - upstream_anchor.1).powi(2)) + .sqrt(); + let offset_m = (chord_m * 0.5).max(5_000.0); + let center = (true_end.0 - ux * offset_m, true_end.1 - uy * offset_m); + let half_extent_m = (chord_m * 0.5).max(5_000.0); + let window_rect = ( + center.0 - half_extent_m, + center.1 - half_extent_m, + center.0 + half_extent_m, + center.1 + half_extent_m, + ); + + // Sanity on the window construction itself (not the code under + // test): the upstream anchor must genuinely be cropped out, and the + // true terminus must genuinely be inside — otherwise this test + // isn't exercising the branch it claims to. + let inside = |p: (f64, f64)| { + p.0 >= window_rect.0 + && p.0 <= window_rect.2 + && p.1 >= window_rect.1 + && p.1 <= window_rect.3 + }; + assert!( + !inside(upstream_anchor), + "test construction error: upstream anchor {upstream_anchor:?} must be OUTSIDE \ + the window {window_rect:?} for this to be a real upstream-cut case" + ); + assert!( + inside(true_end), + "test construction error: true terminus {true_end:?} must be INSIDE the \ + window {window_rect:?} for this to test the terminus-survives claim" + ); + + let wire = crop_course_to_window( + &course, + window_rect, + seed, + "GJ1c", + ¶ms, + &ta, + &climate, + 0.0, + station_spacing_m, + ) + .unwrap_or_else(|| panic!("course cropped to nothing despite containing the terminus")); + + assert_eq!( + wire.terminus, + CourseTerminus::Mouth, + "an upstream-only crop (anchor cut, true terminus still in-window) must NOT \ + collapse the terminus flag — got {:?} for edge {:?}", + wire.terminus, + edge.edge_id + ); + } + /// Clamped-window edge: `n = 1` is the minimum valid window (a single /// district) — no panic, no empty output, exactly one cell per array. #[test]