No description
Find a file
beezm ea301f7a8c rivers/02: river promotion — unified ranking over both termini, and the count gate
Choose the river count against the terrain that actually exists. The M3 count of
3 was tuned on topography the southern stretch (D-065) and coastal fragmentation
(D-063) have since replaced, and TrunkCount/GiantCount/EndorheicMaxCount = 3 are
LEAN REPORTING CAPS, not a statement about this island.

UNIFIED RANKING. The reference promoted from two lists with two quotas — N sea
trunks, N endorheic giants — which encodes the assumption that reaching the sea
is what makes a drainage a river. This terrain does not satisfy it: 54-78% of
land drains inland across the gallery, 106-121 terminal basins per seed. So
selection is unified — every major drainage ranked by contributing-cell count in
ONE list, top N promoted, and the sea/endorheic split FALLS OUT. A quota would
have promoted small coastal drainages over far larger inland ones purely because
of where they end. Deliberate departure from the reference's structure (D-050
noted, approved in chat). Only SELECTION is unified: the per-terminus tag is
retained per river because rivers/03's routing branches on it, and Trunk/Giant
are untouched.

  The metric is the same unit on both sides, and that is now ASSERTED rather
  than argued: sea Acc and endorheic BasinInflow are both counts of contributing
  land cells on the same D8 field, and every land cell has exactly one
  destination, so Σ sea Acc + Σ BasinInflow + unrouted == LandCells exactly. The
  tool refuses to rank unless it holds, per seed. It held on all 8.

DrainageAnalysis.cs is NOT modified. The complete candidate set is re-derived
from state it already exposes (Dir/Acc/BasinId/BasinInflow/FullFilled); the
three internal per-basin values that are not exposed are exactly reconstructible
because terminal basins are reverted to the real surface while FullFilled keeps
the fill. Only REPORTING caps were raised, and only so the analysis's own
TraceStem produces a real upland course per promotable candidate.
EndorheicMinDepthM/MinAreaPx were left alone — they decide which depressions
BECOME terminal basins, i.e. they define the routing surface itself, and a count
chosen on a moved surface would be a count for terrain that does not exist.

TWO FINDINGS.

  1. THE TERRAIN HAS NO NATURAL COUNT. The knee (largest ratio between
     consecutive ranks) lands at rank 19/7/15/5/4/12/3/8 across the eight
     gallery seeds at ratios of 1.33-1.57x, with one uncorroborated 2.37x. A
     break that wanders across the whole plausible range at that strength is not
     a break — on a log axis the candidates fall on a near-straight line.
     N is a DESIGN choice, and the diagnostic says so instead of inventing one.

  2. THE ISLAND'S MAJOR RIVERS ARE INLAND RIVERS. Six of eight seeds have ZERO
     sea-reaching drainages in their top 8; the median at N=16 is one; one seed
     has none at 16. On the primary seed the largest sea drainage ranks 11th at
     736,088 px against 2,329,573 px for the largest endorheic — 3.2x. (That
     outlet is exactly batch 12's recorded top trunk, to the cell.)

A concern I raised and then CLOSED by measuring: MinOutletSeparationPx is a
plain Euclidean test with no notion of which landmass a coastline belongs to, so
on a fragmented archipelago it could suppress an island's only river. Measured:
0-6 cross-landmass suppressions per seed, and — decisively — the largest
suppressed above-floor outlet anywhere is 209,611 px against a smallest ladder
cutoff of 439,592 px, so ZERO suppressed outlets clear any rung on any seed. The
rule provably cannot have altered the ladder. Real but harmless; flagged for
rivers/03, not a blocker. The rule was NOT changed — it belongs to the analysis,
and moving it would move the candidate set the developer is being asked to judge.

Also found: the params are ABSOLUTE pixel counts, so this analysis is only valid
at the size they were tuned for. At 1024 nothing qualifies as endorheic (the
whole endorheic half cannot be exercised) and separation suppresses 15,043 of
15,048 outlets. The tool now refuses loudly to have a non-8192 run read as a
count decision. Flagged for rivers/03: these want to become scale-free fractions
as MinLandComponentFrac already is.

Giant.ProvisionalRoute is never rendered — the "comb" is rivers/03's to replace,
and drawing it would make a count judgment look like a finished network. The
plates draw the real erosion-carved upland stems. Endorheic rivers are marked at
where their stem POOLS, not at the basin's deepest cell: the analysis
distinguishes these deliberately and they sit up to 395 px apart, which drew
every stem detached from its own endpoint until it was fixed.

TASTE GATE: 8/12/16 are presented and nothing is decided. No count is chosen, no
default is set, and neither TerrainGenConfig nor DrainageAnalysis.Params changed.

-> XX_Human/output/rivers/02_promotion.report.md
-> batches/rivers/02_promotion/INDEX.md

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhkXBQh2tDmcWKpXYcj8vj
2026-08-23 06:13:03 -04:00
Client Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
Core rivers/01: re-baseline — bare defaults reproduce terrain-shape-v1 (calibration pinned family-off); retire stale oracles; namespace batch root by chat 2026-08-23 04:45:10 -04:00
Server Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
Tools rivers/02: river promotion — unified ranking over both termini, and the count gate 2026-08-23 06:13:03 -04:00
.gitattributes Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
.gitignore Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
islaApocalypse-v2.csproj Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
islaApocalypse-v2.sln Phase 0: clean project skeleton for the v2 rewrite 2026-08-19 20:21:12 -04:00
project.godot Move the user:// isolation warning out of project.godot 2026-08-19 21:31:12 -04:00
README.md Phase 2a: the faithful redistribution curve, re-measured against v2's own output 2026-08-20 01:38:10 -04:00

Isla Apocalypse — v2

The rewrite (D-049). A post-apocalyptic survival voxel game: one island, generated once offline, loaded by everyone.

Status: Phase 2 — shaping. Phase 0 laid the contracts and the tooling rails; Phase 1 ported the crown-jewel noise and pass 1 (the island's raw shape); chat2/01 added pass 2a — the height redistribution curve and shelf detail, re-calibrated against this repo's own output, with the two-height-field split and an automatic oracle.

Still no erosion, rivers, water bodies, crater carve, coast shelf, offshore islets, biomes, roads or mesher.


⚠ Two repos. Never one.

Repo Role
~/celerNexus/islaApocalypse-v2/ (this) The rewrite. Every line of new code is written here.
~/celerNexus/islaApocalypse/ READ-ONLY. The pre-rewrite prototype, archived at ab78883, tag pre-rewrite-reference. Ported FROM; never written to.
~/celerNexus/islaApocalypse-vault-v0.1/ The design vault — the source of truth for WHAT and WHY. The vault leads.

The old codebase is a spent quarry (D-050): mined, never tracked, never synced, never canon'd. Writing rewrite code into it would corrupt the very thing being ported from.

⚠ Runtime data isolation — do not weaken

This project pins its user:// directory away from the old prototype's, which holds the preserved reference seeds, blueprints and batches:

user:// → ~/.local/share/islaApocalypse-v2/          ← this project
          ~/.local/share/godot/app_userdata/islaApocalypse/   ← ⛔ the OLD prototype. Never touch.

Pinned two ways in project.godot — a distinct application/config/name and use_custom_user_dir + custom_user_dir_name. Both must stay.

⚠ Do not document this with a comment in project.godot — it will not survive.

Godot rewrites project.godot on import and STRIPS ALL COMMENTS, replacing the header with its own boilerplate. (Measured: a warning block written there was gone after the next --import. The settings survived; only the explanation was deleted.) So the warning lives here, and the guarantee is enforced by the probe below — which exits non-zero on a clash and therefore works in a pre-flight check, not just by eye.

After any change to the [application] block, re-run the probe:

Godot_v4.7.2-stable_mono_linux.x86_64 --headless \
  --path ~/celerNexus/islaApocalypse-v2 res://Tools/Scenes/UserDirProbe.tscn

Layout

Core/     math + data only, ENGINE-FREE   — depends on nothing above it
Server/   authoritative logic             — may use Core          (empty: not yet its phase)
Client/   rendering                       — may use Core          (empty: not yet its phase)
Tools/    the offline generator           — ⚠ may NOT use Client

Each layer has a README.md stating its role and its boundary. Read the one for the layer you are about to write in — they carry the reasons, not just the rules.

The contracts this phase establishes

  • ★★ The column model (D-053) — the world as a 2D grid of columns, each a stack of (material, thickness) runs. Air is the top run; there is no air-vs-solid height branch.Core/Scripts/Column.cs
  • The material schemas (D-054) — terrain and building, two schemas bridged by a transformation seam that is deliberately empty. Identity is a registry key, never an ordinal. Mesh style is per-origin, never a material property. → Core/Scripts/MaterialRegistry.cs
  • The scaling discipline — nothing uses a raw pixel number. → Core/Scripts/GenerationScale.cs
  • The vertical yardstick — ONE metres-per-raw-unit number, not three constants and twenty literals as the prototype had. → Core/Scripts/WorldScale.cs
  • The elevation profile — the 7-band redistribution curve, identity at and below sea, its knots measured rather than chosen. → Core/Scripts/HeightCurve.cs
  • The two-height-field split (D-046) — heightClassify is raw and is the oracle; height is curved and is what gets drawn. → Tools/Scripts/Pass2Result.cs
  • Tooling safety — every path env-overridable, deletion refused by code. → Core/Scripts/ToolingPaths.cs, Core/Scripts/FileSafety.cs

Build

dotnet build
Godot_v4.7.2-stable_mono_linux.x86_64 --headless --import --path .

Godot 4.7.2 (mono) · .NET 8 (net8.0) · Godot.NET.Sdk/4.7.2

The reference repo is Godot 4.7.1. This repo deliberately targets 4.7.2 — clean rewrite, current tooling. 4.7.1 is the port source, not a constraint on new code.

<RollForward>Major</RollForward> in the .csproj is load-bearing.

The target is net8.0 but this machine carries only a .NET 10 runtime. Without that property the generated runtimeconfig.json rolls forward by MINOR only and every run fails with "You must install .NET 8". This bites at run time, not build time — the build succeeds either way — so re-check the emitted runtimeconfig.json after any .csproj change.

What this phase is NOT

No mesher, no stratigraphy, no biomes, no water, no rivers, no erosion. The pipeline is 2D-maps-first (D-056): stages 15 are flat maps, gotten completely right before a single 3D vertex exists. Building the mesher before the data is right is the specific trap this rewrite exists to undo.

And the curve is a BASELINE, not a verdict. It is ported faithfully and calibrated honestly so that later reshaping has a control to be judged against. Whether this elevation profile — 60 % of land in the bottom 14 m, 4 % above the plateau — is the one the world wants is an open question the histograms exist to inform, not one this port has answered.