Files
settled-reach/docs/architecture/river-courses-t1170.md
T
jpmschweitzerandClaude Fable 5 18c731a5de docs(meta): D-255 body-map-viewer stepped render architecture + deprecation sweep
D-255 filed (server-determines-content / client map-art function / stepped
six-level ladder Global+Region→Chunk / tagged-envelope wire discharging D-225 /
three-tier cache with distinct staleness-storage axes / cache-accelerated-pure-
function seed-chaining / lakes from settled hydrology, endorheic cue =
outflow-course presence). Twelve in-place amendments: D-166 corollary repoint,
D-192 persistent-cache boundary, D-225 deferral discharged, D-226 consolidated
(ceiling re-scope, select_rung→step-index, stepped transport, item-(d)
per-request + retention cap + chunk-floor partial restore, courses gloss +
cliff sparse-list), D-227 four-part (TTL-split, version tag, seed-chaining,
lakes), D-243 gridunit vocabulary. Clerk-audit fixes: atlas-zoom-ladder-t1143
superseded banner, CLAUDE.md Phase-4 cascade row updated to the stepped model,
Q-093 prior-art cross-ref. river-courses-t1170 carrier gloss. governance
README index regenerated (109 decisions through D-255); validate clean, sync
1344 refs 0 broken. Ticket chain T-1181–T-1185 + reconciliations in changelog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 10:58:02 +02:00

26 KiB
Raw Blame History

title, description, type, status, round, created
title description type status round created
River Course Invention — T-1170/T-1168 Design Ruling Binding design ruling for course invention + riparian vegetation (Tyre, from the three-audit design pass) design binding T-1170 2026-07-23

Note (2026-07-24, body-map-viewer workshop — D-255): the carrier three-way rule below (Ruling 1c / Ruling 6) survives unchanged in substance. Two terminology/scope updates from D-255: (1) "the windowed payload" is repointed to "the per-step data-canvas payload — formerly the district_window field, now the tagged step-canvas envelope" (D-226 2026-07-23 amendment / D-255 §c). (2) Rule (iii) — rung-indexed invented detail rides the windowed payload regardless of geometric kind — gains a second vector member alongside courses: cliffs: Vec<CliffSegment> (carved-gorge geometry: dominant elevation + per-segment channel_depth + cliff_edge), a sparse list zero-length when nothing is carved (T-1177 population survey: zero carved cells across all 267 real bodies), Phase-4 Atlas scope. Governance capture is in the D-226 and D-227 body-map-viewer amendments; this note only glosses the design doc so its "windowed payload" language stays current.

cracks knuckles — I've read both tickets in full, the D-226 amendment block (wave-1 carrier note, two-waterline note, T-1137 queue ruling, windowed-family ceiling), D-227, D-243, and the T-1143 zoom-ladder serving model. The audits are code-true and the tension called out in point 1 is real — I created it, and it resolves cleanly once the carrier rule is split on the right axis. Ruling follows.


TYRE — Binding Design Ruling: T-1170 (river course invention) + T-1168 (riparian vegetation)

Date: 2026-07-23. Status: binding for this batch; implementers code from this directly. Governance capture text in Ruling 6.

The keystone insight, stated once up front: the framework tension between my wave-1 carrier note ("discrete geometry rides whole-body") and my two-waterline note ("invention detail is rung-indexed") dissolves when you split on the correct axis. The wave-1 rule was written for the skeleton — a fixed, finite, rung-independent graph, computed once, valid forever. Course geometry is not skeleton; it is invention — the linear sibling of the coast crinkle, refined per rung by construction. Skeleton rides whole-body; invention rides the window. The rule wasn't wrong, it was under-specified, and this ruling refines it rather than reversing it.


Ruling 1 — Invention locus and wire carrier: server-side, per-window, inside DistrictWindowLayer

1a. Locus: server, per-window, at serve time — Dudley's option (b). Binding. Course geometry is invented inside the window derive (GenWorkItem::DeriveWindow, background queue per T-1137 — queue discipline [HARD], unchanged), at the window's granularity and min_wavelength_m. Dudley's numbers close this: +0.090.21 ms against a measured ~5 ms window baseline, under 5% — noise. Whole-body precompute at Quarter detail is rejected on payload grounds (~4.4 MB dense-body for geometry 99% never viewed — categorically the wrong shape under D-227 derive-don't-store). Client-side GDScript invention is rejected on the determinism surface, not CPU: a byte-exact two-language mirror of splitmix64/value_noise/octave tables is a standing D-010/D-227 liability the project has deliberately avoided everywhere else, purchased for a compute saving option (b) proves unnecessary.

1b. Carrier: a new vector field inside the existing windowed payload. The windowed-family ceiling is untouched — and here is why, precisely. The ceiling ([HARD], D-226 T-1124 §2) counts windowed-query fields on AtlasLayerResponse. It exists to prevent a second concurrently-in-flight windowed query needing per-field request correlation. Course geometry is not a second query: it is content of the one windowed payload, keyed by the same (body, center, n, granularity) echo, arriving in the same response, stale-discardable by the same rule. DistrictWindowLayer gains courses: Vec<RiverCourse> (#[serde(default)]), exactly as it holds six dense arrays today. No ceiling impact, no tagged-envelope migration, no new request class.

1c. The carrier rule, refined (this is the governance-grade restatement — capture text in Ruling 6):

Three-way, replacing the wave-1 two-way rule: (i) rung-independent discrete features — skeletons, graphs, point markers, computed once and valid forever — ride the whole-body overlay family, filtered per-rung client-side. (ii) Continuous per-metre fields ride the windowed per-cell arrays. (iii) Rung-indexed invented detail rides the windowed payload regardless of geometric kind — raster or vector — because rung is a request parameter, and only the windowed query carries one. The coast crinkle has always followed (iii) implicitly (it arrives baked into windowed cell verdicts); course polylines are its vector sibling, arriving alongside them.

1d. The two-waterline problem dissolves under this carrier. A whole-body course would need a rung-parametrized terminus (the fork the audit flagged as sharpest). A windowed course does not: the server invents the course at a known rung, in the same pass that computes that rung's per-cell water verdicts — so the terminus is resolved against the drawn coast at that rung, server-side, consistently. Nothing whole-body ever carries a terminus. The skeleton stays rung-independent, as the two-waterline note requires.

1e. Window-independence invariant (binding, tested). The geometry is a pure function of (seed, body, edge, rung)never of the window rect. Stations are generated at deterministic global arc-length positions along the edge; the window crops, it never re-parametrizes. Two overlapping windows at the same granularity must produce byte-identical points for the shared stretch. This is the course analog of the coast warp's absolute-world-metre keying and it is a mandatory determinism test (see Discipline).


Ruling 2 — Adjacency: persisted downstream pointer. Reconstruction rejected.

2a. RiverNetwork gains river_downstream: Vec<u8> — additive, #[serde(default)], parallel to river_cells (the exact river_class precedent). Captured in extract_river_network (drainage.rs:247-349) where fdir[i] is already in scope and currently discarded after its boolean use. One .map(), zero new grid passes, ~1 byte/river cell. This is the opening move of the batch — everything else consumes it.

2b. Reconstruction is rejected on the Si audit's confluence finding: adjacency alone cannot distinguish inflow from outflow at a 3-river-neighbor confluence, and re-deriving direction from elevation is re-running D8 badly. We had the true answer in hand and threw it away; stop throwing it away. The D-203 memory-frugality rationale for discarding covered the 131 KB full grid, not the river-cell projection.

2c. Sentinel vocabulary (values in the u8 space above 7): MOUTH (flow reaches raw-sea) · EDGE_DRAIN (flow exits the grid's top/bottom edge — a grid artifact, not a mouth, per Ruling 3f) · TERMINALreserved now, unused in round 1 — flow ends in an interior sink (future endorheic basin / inland delta, see Ruling 7b). Reserving the value costs one enum arm and keeps the future option additive instead of a wire migration.

2d. Each edge is uniquely identified by its upstream cell (every river cell has exactly one downstream pointer), so edge_id = pack(u16,u16) → u32. This is the seeding key (Ruling 3a).


Ruling 3 — Course algorithm

3a. Seeding (D-227/D-010 discipline). SeedChain::for_body(...).derive(SeedDomain::RiverCourse, edge_id as u64), new SeedDomain variant, with a distinct RIVER_COURSE_WARP_SALT on the noise stream (the COAST_WARP_SALT pattern verbatim) — never correlated with coast warp, terrain scatter, or vegetation massif fields at the same position. Pure (seed, body, edge, rung).

3b. Two-stage shape — and the stage split is load-bearing for cross-rung stability:

  • Stage A — coarse valley-seeking control path. Rung-INDEPENDENT. Between the two anchor points (upstream/downstream cell centres — skeleton truth, never moved), place control stations at chord/8 (~9.6 km). At each, evaluate k=5 candidate perpendicular offsets within the amplitude envelope; score = bilinear ta.elev_pct at the candidate (plus a small continuity penalty against the previous chosen offset to prevent zigzag); pick minimum. The elevation proxy is the bilinear TerrainAnalysis read — the same tradeoff the coast warp already made at district_profile.rs:1152-1157 — NOT derive_at_metres per candidate. Dudley's numbers make this affordable (+µs/segment); the Si audit's unmeasured-derive-cost gap is thereby not load-bearing for this design (we still close the measurement gap — see Discipline). Stage A is computed identically at every rung, so the coarse course never moves as you refine.
  • Stage B — fine warp octaves. Rung-INDEXED. Perpendicular scalar displacement of intermediate stations via salted multi-octave value noise keyed on global arc-length, octave band from ~chord/2 (~38 km) down to the window's min_wavelength_m hard-truncate (the warp_fbm cutoff idiom, T-1162 rung discipline, reused exactly). Station spacing = the rung's sample spacing (2,048 m District / 512 m Quarter — Dudley's ~37/~150 points per segment), at global arc-length multiples so windows agree (Ruling 1e).

Cross-rung invariant that falls out: the Quarter course is the District course plus octaves in the (1,024 m..4,096 m) band — displacement between rungs is bounded by the truncated-octave amplitude sum, sub-cell at District spacing. And at Region spacing no octaves survive at all, so the rung-truncated course degenerates to the straight chord — which means the client's Region-rung skeleton-chord drawing (Ruling 5a) IS the course at that rung, not an approximation. That's the kind of unification I get excited about: one function, every rung, no special cases.

3c. Amplitude envelope. Sine-shaped taper to zero at both endpoints — this is what makes confluences work for free: every edge into and out of a cell meets exactly at the cell-centre anchor, C0-continuous, and the confluence marker draws at the anchor. Peak amplitude ≤ ~8% of chord (~6 km), scaled down by local ta.slope_deg (steep terrain → straighter) and up modestly by river_class (trunks meander wider). All tunables; taper-to-zero and the ≤ half-cell hard cap are binding.

3d. Valley preference is IN for round 1 (costed at noise level by Dudley), via Stage A only. This is the round-1 answer to the wave-1 "rivers may cross invented hills" gap: courses prefer invented-terrain valleys at coarse wavelengths; residual fine-octave conflicts (a course crossing a voxel_relief hillock) are accepted and documented. The converse mechanism — terrain invention becoming course-aware (valley carving along courses) — is explicitly deferred; it belongs at the block/tile rungs and Phase 5 fill, noted in governance as the future direction, not built now.

3e. Termination at the invented coast (the T-1172 retirement mechanism). For an edge whose downstream sentinel is MOUTH: walk stations upstream→downstream, sampling the same rung-consistent morphology water verdict the window's own cells use (the open_water/morphology path, at the window's min_wavelength_m — not raw ocean_frac, so the terminus agrees with what is drawn). First water station → bisect against the previous land station (fixed 6 iterations) → terminus point, flagged Mouth on the wire. If the final anchor still samples land at this rung (drawn coast receded past the raw-sea cell — the inland-mouth oddity, fixed by construction here), extend along the D8 direction up to one cell length probing; if still no water, terminate at the anchor with no mouth flag (degenerate, rare, bounded). A few dozen probes per mouth edge, on the background queue — within the T-1137 discipline by construction.

3f. Pole-edge drains are not mouths. Binding. EDGE_DRAIN termini get no mouth flag and no double-ring; the course ends at the last in-grid station. Server-side, extract_river_network stops classifying grid-edge exits into mouths (deliberate Layer-1 golden re-pin, see Discipline). They are grid artifacts, exactly as Jeroen's capture question suspected.

3g. T-1172 clip: restructured, not blanket-deleted. The three marker sites in atlas_window_nature_overlay.gd (lines 288/311/347) retire for the course-drawing rungs (District/Quarter) — courses carry real rung-consistent termini, the clip's job is done there, and suppressed offshore mouths return as real geometry. The Region-rung skeleton path keeps a clip: Region still draws whole-body skeleton against a rung-dependent drawn coast, which is precisely the presentation-frame reconciliation the two-waterline note prescribes. Rewrite the marker comments: the surviving clip is documented as permanent-until-Region-goes-windowed (T-1143 ruling 2's progressive tiling), at which point Region inherits windowed courses and the last clip retires.

3h. Wire shape. RiverCourse { edge_id: u32, class: u8, points: Vec<(i32,i32)> /* world-metres */, terminus: CourseTerminus /* None | Mouth | EdgeDrain | ContinuesBeyondWindow */ }. Only edges whose chord-inflated-by-max-amplitude bounding box intersects the window ship; points cropped to window + one station beyond each edge of it. ~12 KB typical per window. Well inside every budget.


Ruling 4 — T-1168: shape and sequencing

4a. The mechanism: a scale-free point-sample distance test. This dissolves the blotch problem rather than mitigating it. near_perennial_water(sample_pos) = distance(sample_pos, nearest invented course polyline) ≤ band_m, where band_m defaults to D-239 §8's governed 13 tiles (class-scaled: thicket band for trunks, scrub band for streams — tunable constants, governed default). Because the test is a pure point predicate against real course geometry, it is automatically correct at every sampling density: at 512 m Quarter spacing it essentially never fires (the band is sub-cell — honest, no over-fattening, the exact Lendel-failure discipline), and at 1 m tile spacing it fires on exactly the 13 tile bank strip. No per-rung riparian policy exists or is needed. The audit's skeleton-disc model — any "within N km of a 76 km-spaced skeleton point" test — is rejected outright: it is off by 34 orders of magnitude from the governed band and cannot be tuned into anything but green blotches.

4b. Consequently the 512×256 distance-field-over-grid proposal is also rejected — a grid quantized at 76 km cannot express a metres-scale band. The test runs against course polylines resident in the derive context: in the window path, T-1170's already-invented courses (coarse bounding-box cull, then point-to-segment distance — most cells cull to zero edges); in the batch path, courses for edges near the district, invented on demand via the same pure function (cachable per derive batch). The Si audit's observation that gen_queue.rs:626 discards the Layer1Output still gets fixed — the window path needs RiverNetwork (cells + downstream pointers) resident to know which edges exist; retain it in TerrainAnalysisCache instead of re-deriving-and-discarding.

4c. Hard sequencing: T-1168's riparian test lands AFTER T-1170's course inventor, full stop. The test is meaningless without course geometry (4a). The threading scaffolding is exempt and proceeds in parallel — it is mechanical and fully mapped by the audit: build_district_profile gains the riparian signal parameter consumed at district_profile.rs:1469; threaded via derive_all_districtsderive_district_profile (batch, sourced from cascade.rs's already-unpacked river_network, the road_graph precedent) and build_district_window_layerderive_window_cellderive_at_metres/derive_orbital_at_metres (window, sourced from the retained Layer1Output). Until the course inventor lands, the provider returns "no course in range" — wired but inert, zero behavioral change, zero golden churn.

4d. T-1162 composition contract, re-affirmed as binding: near_perennial_water is a separate boolean/geometry signal into derive_vegetation; it never touches moisture_q, and moisture_perturb_q never reads river data. The existing parameter separation already enforces this — the implementer's only job is not to break it. Climate gating stands: extreme-cold Barren beats riparian (ice is geology per D-227); Marine/airless precedence unchanged.

4e. Experience honesty (what Jeroen will see): after this batch, District/Quarter windows show continuous meandering rivers with real coast mouths. They will not show visible green riparian corridors — under the governed 13 tile band that is sub-cell at every Atlas rung, and honestly so. T-1168's visible payoff arrives at the block/tile rungs (opened by T-1143 ruling 1) and Phase 5 ground truth. If Atlas-visible green corridors are wanted, that is a design widening of D-239 §8 — flagged to Jeroen in Ruling 7a, one constant away, not an architecture change.


Ruling 5 — Per-rung presentation surface

5a. Region and coarser (skeleton path, whole-body layer1): the client upgrades dots to connected straight chords via river_downstream — which per Ruling 3b is the rung-truncated course, so this is not an approximation, it is the course at Region truncation. Clip retained (Ruling 3g). Confluence/mouth markers from the skeleton lists (mouths now excluding pole drains).

5b. District and Quarter (course path, windowed): polylines from DistrictWindowLayer.courses via the existing _pos()/draw_polyline plumbing (_draw_basins precedent — no new transform code). Mouth markers at Mouth termini. No clip. Quarter rivers return — the streams class draws at Quarter, executing exactly the pre-announced fade-down revisit from the wave-1 visibility note.

5c. The revisit point is preserved and split: RIVER_CLASS_VISIBLE_BY_RUNG is replaced by two tables in atlas_window_geometry.gdSKELETON_CLASS_VISIBLE_BY_RUNG (Region+, path 5a) and COURSE_CLASS_VISIBLE_BY_RUNG (District/Quarter, path 5b), with per-class width/opacity companion tables. This is where Araminta's forthcoming presentation ruling plugs in — implementers land functional defaults (trunk widest, streams thinnest, class-filtered per rung: District trunk+tributary, Quarter all three), and Araminta's ruling edits those tables and only those tables. Same single-revisit-point discipline as wave 1.


Ruling 6 — Governance captures (exact text)

Append to the D-226 amendment block (governance/decisions/architecture.md), dated 2026-07-23:

Course-invention carrier note (T-1170, 2026-07-23 — Tyre): river course geometry below D8 resolution is invention, not skeleton, and rides the windowed payload as a vector field inside DistrictWindowLayer (courses, additive) — invented server-side per window on the background queue (T-1137 discipline) at the window's rung, terminated server-side against the same rung's morphology water verdict the window's cells carry. This does not count against the windowed-family ceiling (§2 [HARD]): the ceiling counts windowed query fields on AtlasLayerResponse; courses are content of the single existing windowed payload, same echo key, same staleness semantics. The wave-1 carrier rule is hereby refined three-way: (i) rung-independent discrete features (skeletons, graphs, markers — computed once, valid forever) ride the whole-body family, filtered per-rung client-side; (ii) continuous per-metre fields ride the windowed per-cell arrays; (iii) rung-indexed invented detail rides the windowed payload regardless of geometric kind, because rung is a request parameter and only the windowed query carries one — the coast crinkle has always implicitly been (iii); courses are its vector sibling. The two-waterline note's reconciliation resolves per-rung server-side for courses (the terminus is windowed content at a known rung); the Region-rung skeleton path retains the presentation-frame clip until Region itself goes windowed (T-1143 ruling 2), which retires the last clip site. The skeleton itself gains river_downstream (additive per-river-cell D8 pointer with MOUTH/EDGE_DRAIN/reserved-TERMINAL sentinels) on RiverNetwork — whole-body, rung-independent, per rule (i). Pole-edge drains are grid artifacts, not mouths — excluded from mouths at extraction.

Append to D-227's amendment trail:

Amended 2026-07-23 (T-1170 — river courses join the invention family): the "invented deterministically" clause now covers linear features: river courses between D8 cells are pure (seed, body, edge, rung) functions — a rung-independent valley-seeking coarse path (bilinear TerrainAnalysis proxy, never full re-derivation) plus rung-indexed perpendicular warp octaves under the min_wavelength_m truncation discipline, distinct salted stream, amplitude tapered to zero at cell-centre anchors (confluence continuity), stations at global arc-length positions (window-independent). Round-1 relief reconciliation is course-follows-terrain (valley preference); terrain-carves-for-course is the deferred converse, targeted at block/tile rungs.

Also: fix the stale scale.rs:107 grid-shape comment (one line, audit nit) in whichever PR touches that file first.


Ruling 7 — Jeroen items

7a. Riparian visibility at Atlas rungs (genuine design call). Under D-239 §8's governed 13 tile band, riparian vegetation is honestly invisible at every Atlas rung (Ruling 4e) — the drawn river itself is the only visible river response at District/Quarter. If you want the Nile effect — a visible green corridor at Atlas scale — that is a design widening of the band (e.g. floodplain-riparian corridors of hundreds of metres on alluvial-plain morphology, class- and morphology-scaled). The mechanism ships scale-free, so this is one governed constant/function away, zero architecture change. Your call, not blocking this batch.

7b. Endorheic basins (one paragraph, as asked). This design does not foreclose deliberate endorheic basins or inland deltas — it deliberately cheapens the future path. The TERMINAL downstream sentinel is reserved now (Ruling 2c), so an interior sink becomes an additive drainage-extraction change, not a wire migration; and the termination mechanism (walk stations to the first rung-consistent water verdict — which is the morphology verdict, covering lakes, not just ocean) generalizes unchanged to a terminal lake shoreline. When you want endorheic basins, the work is in D8 sink retention and lake morphology, and every piece this batch builds — pointers, courses, termination, riparian — consumes them without modification. Nothing to design today.


Discipline — benches, goldens, determinism tests

  1. Run the dormant bench first. zoom_ladder_bench.rs --release --ignored, before the course inventor merges; record the numbers in docs/architecture/atlas-zoom-ladder-t1143.md (which itself flags them UNMEASURED). Cheap, closes a documented gap this batch would otherwise inherit blind. Not load-bearing for the design (Ruling 3b avoided depending on it) but the gap closes now.
  2. Course-cost bench: window derive with courses on/off; assert the delta stays under ~5% of the window baseline (Dudley's envelope).
  3. Determinism tests (mandatory, new): (a) overlapping-windows agreement — two windows sharing an edge produce byte-identical course stations in the overlap (Ruling 1e); (b) cross-rung stability — Quarter course points within the truncated-octave amplitude bound of the District polyline (Ruling 3b); (c) salt isolation — course stream uncorrelated with coast warp at shared positions (the existing isolation-test pattern).
  4. Goldens, deliberate re-pin discipline (UPDATE_GOLDEN=1 + rationale comment, the T-1162 precedent): cascade_golden re-pins (mouths list loses pole drains; river_downstream added). window_derivation_golden: first verify empirically whether the 128×64 fixture produces river cells near the sweep positions (audit: unconfirmed); add a course-bearing sweep position either way so courses are actually pinned. believability: expected unchanged (the point test cannot fire at district spacing) — verify, don't assume; an unexpected re-pin there is a bug signal, not a regen chore.
  5. Full cargo test before push (the golden harnesses are separate test binaries; --lib misses them), and the live-screenshot pixel-diff eyeball check on the merged result, cold-launched — this batch is rendered-path.

Work split and sequencing

Dudley — server (owns the cost model he probed):

  • A1 (opening move, unblocks everything): river_downstream + sentinels + pole-drain mouth exclusion + Layer-1 golden re-pin.
  • A2: course inventor (Ruling 3a3d, 3h) in the window derive + Layer1Output retention in TerrainAnalysisCache + wire field.
  • A3: termination + mouth flags (3e3f) — with A2.
  • A5 (after A2): T-1168 riparian point test wired into the threaded signal, batch + window paths.

Stig — client + mechanical threading:

  • B1 (parallel with A2): T-1168 threading scaffolding (Ruling 4c), inert provider, zero behavior change.
  • B2 (needs A1 only): Region skeleton-chord drawing.
  • B3 (needs A2/A3): course polyline drawing, mouth markers, clip restructure (3g), visibility-table split (5c) with functional defaults for Araminta.

Hard edges: A1 → everything. A2 → {A3, A5, B3}. B1 ∥ A2, converging at A5. Nothing else serializes.

Difficulty tier, honestly: challenging but doable — and the architecture is on our side. Every primitive exists (value_noise, splitmix64, the cutoff idiom, the queue, the polyline draw path); this batch composes them, it doesn't invent machinery. The one place I'd have called "extremely difficult" — reconciling a whole-body course with a rung-indexed coastline — stopped being a problem the moment courses went windowed. That's the ruling doing the work.

— Tyre