fix(simulation): PR #197 review round — real seaward chord for Mouth edges (Hoshe #1/#2, Tyre 1/2)

The batch's real bug: extract_river_network computed the seaward
neighbor (nr,nc) to decide the MOUTH sentinel then discarded it, and
build_edges placeholder-pointed mouth edges at themselves — zero
chord, invent_course's degenerate 1-point return, resolve_mouth_
terminus dead code on real data, ALL real mouths resolving None, and
(because Ruling 3g retired the D/Q clip on the promise of real
termini) mouths vanishing at District/Quarter. The second instance of
the threw-away-the-answer anti-pattern Ruling 2b fixed for interior
pointers. Fix: river_seaward: Vec<(u16,u16)> on RiverNetwork
(additive, serde-default, parallel array; meaningful only at MOUTH
entries), captured in the same extraction pass; build_edges gives
Mouth edges the real one-D8-step chord. Permanent acceptance:
all_real_gj1c_mouths_resolve_to_mouth_terminus_not_none — 3/3, revert-
verified failing at the golden's first mouth (38,47). Mouth golden
coverage added (river_course_golden gains district_mouth/quarter_mouth
samples + non-degeneracy test; the previously Interior-only filter
gap closed). Tyre 1: the land-probe's dead dx/len*len arithmetic
replaced — station_spacing_m threaded through the crop path, probe
steps one real cell spacing, comment reconciled. Tyre 2: boxing
comment reattributed to variant-size balancing (Layer1Output retention
lives on TerrainAnalysisCache, not BodyWorldState). Hoshe #4:
cascade_golden's doc now states the attractor cascade accurately
(count-parity, not byte-identity — water_dist seeds from mouths).
Full cargo test green; goldens re-pinned deliberately; bench +3.0%.

Tickets: T-1170

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:02:16 +02:00
co-authored by Claude Fable 5
parent 581be31549
commit d256233faf
10 changed files with 2325 additions and 53 deletions
+45 -26
View File
@@ -351,33 +351,51 @@ fn extract_river_network(
// `RIVER_THRESHOLD`, but handled the same as `EDGE_DRAIN` (no downstream
// neighbor to point at, not a sea mouth) rather than crashing the
// pointer's "always points somewhere real" contract.
//
// river_seaward (T-1170 PR #197 review, Hoshe #1): captured in the SAME
// pass, parallel to river_downstream — `(nr, nc)` is already computed
// here to decide the MOUTH sentinel; discarding it after the elevation
// check (the former code) was the second instance of the exact
// discard-then-need-it-later anti-pattern Ruling 2b fixed for
// river_downstream itself. Non-mouth entries get the `(0, 0)` placeholder
// (documented on the field as unreadable outside the MOUTH case).
let mut mouths: Vec<(u16, u16)> = Vec::new();
let river_downstream: Vec<u8> = (0..n)
.filter(|&i| is_river[i])
.map(|i| {
let r = i / w;
let c = i % w;
let k = fdir[i];
if k < 0 {
// No outflow at all — edge/flat-peak. Not a mouth (Ruling 3f).
return RIVER_DOWNSTREAM_EDGE_DRAIN;
}
let (dr, dc) = D8[k as usize];
let nr = r as i32 + dr;
let nc = (c as i32 + dc).rem_euclid(w as i32) as usize;
if nr < 0 || nr >= h as i32 {
// Flow direction points off the polar edge — a grid artifact,
// not a mouth (Ruling 3f, the pole-edge-drain fix).
return RIVER_DOWNSTREAM_EDGE_DRAIN;
}
if elevation[nr as usize * w + nc] < sea_level {
// Flows into a sub-sea-level cell — a real mouth.
mouths.push((r as u16, c as u16));
return RIVER_DOWNSTREAM_MOUTH;
}
k as u8
})
.collect();
let mut river_downstream: Vec<u8> = Vec::new();
let mut river_seaward: Vec<(u16, u16)> = Vec::new();
for i in 0..n {
if !is_river[i] {
continue;
}
let r = i / w;
let c = i % w;
let k = fdir[i];
if k < 0 {
// No outflow at all — edge/flat-peak. Not a mouth (Ruling 3f).
river_downstream.push(RIVER_DOWNSTREAM_EDGE_DRAIN);
river_seaward.push((0, 0));
continue;
}
let (dr, dc) = D8[k as usize];
let nr = r as i32 + dr;
let nc = (c as i32 + dc).rem_euclid(w as i32) as usize;
if nr < 0 || nr >= h as i32 {
// Flow direction points off the polar edge — a grid artifact,
// not a mouth (Ruling 3f, the pole-edge-drain fix).
river_downstream.push(RIVER_DOWNSTREAM_EDGE_DRAIN);
river_seaward.push((0, 0));
continue;
}
if elevation[nr as usize * w + nc] < sea_level {
// Flows into a sub-sea-level cell — a real mouth. Capture the
// seaward neighbor position (Hoshe #1) alongside the sentinel.
mouths.push((r as u16, c as u16));
river_downstream.push(RIVER_DOWNSTREAM_MOUTH);
river_seaward.push((nr as u16, nc as u16));
continue;
}
river_downstream.push(k as u8);
river_seaward.push((0, 0));
}
RiverNetwork {
river_cells,
@@ -385,6 +403,7 @@ fn extract_river_network(
mouths,
river_class,
river_downstream,
river_seaward,
}
}