test(simulation): upstream-cut Mouth-terminus regression pin (PR #197 acceptance follow-through)

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:22:27 +02:00
co-authored by Claude Fable 5
parent d256233faf
commit 270dba0147
+131
View File
@@ -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, &params, 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",
&params,
&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]