Core/Scripts/RegionLabeling.cs is the shared-infra contract, built to the letter: it runs on
the CLASSIFY (raw) field; land is 8-connected, the deliberate complement of water's 4 (a
diagonal isthmus joins; the water either side stays separate); a component is a maximal
8-connected set of land cells; the MAINLAND is the component containing the map centre —
not merely the largest, which a later fragmentation step could flip — with a flagged
fallback to the largest if the centre were ever water (asserted, never needed: oracle m);
every other component is an island; per component id / sizeCells / centroid / hemisphere
(by centroid, one label per island) / isMainland. Ids come from a fixed scan order and are
proven stable across two generations (oracle o, 16.8M cells). It knows nothing about
offshore or stamped. Engine-free, in Core as C++-candidate math; the hemisphere convention
moved there with it, OffshoreAnalysis aliases it.
Tools/Scripts/RegionPass.cs is pass 1c: label, revert, relabel, tag. The island tag
(renamed IsIsland; IslandHemisphere from the component's centroid; Pass1Result.Regions
carries the whole table) is now a CONSEQUENCE of labeling — every non-mainland component.
That is the fix for the chat2/06 overlay, which tagged only what the offshore pass raised:
1063685222 has 11 natural islands including a 94,511-cell detached mass, 20260821 has 19,
all grey in 06's tags.png and all coloured now. The offshore pass itself is untouched; its
internal Tag stays for its own guards and is no longer exported.
The speck revert (TerrainGenConfig.SpeckRevert / MinLandComponentFrac) lowers every
non-mainland component below the threshold to the mean of its ring of adjacent sea cells,
held strictly below sea. Origin-blind: a natural nub goes the same way as an offshore dot
(6 natural components / 273 cells on the bare 1063685222 field at threshold_mid — reported
as a3r, informational). Lower-only and component-only are asserted cell by cell in the
pass and re-proven on the finished fields by oracle n (mainland bit-identical filter OFF
vs ON; every changed cell in a sub-threshold island, lowered below sea); the mainland is
never a candidate and its size is asserted unchanged across the revert. A reverted
offshore island leaves its submerged skirt as a shoal — not this component, by the rule.
Classify/render consistency is by construction (pass 1, curve identity at sea) and
asserted by oracle k. Deliberately OFF in the bare TerrainGenConfig for the reason the
shelf and islets are: the raw field has natural specks, so default-ON would move the
calibration pool and every regression dump; the batch turns it on.
Thresholds swept on 8 seeds at 4096 (1e-5 / 3e-5 / 1e-4 of the map = 168 / 503 / 1,678
cells): low removes 0–6 nubs per seed, mid (the config default, equal to the offshore
guard) 2–11, high 26–36 — most of the offshore islands, the "fewer, bigger" bookend. The
count/size table carries natural / pre / post counts per hemisphere, min/median/mean/max
and a log-spaced size histogram — the instrument for the southern-stretch step.
Oracle, all passing: a1, a3, a4 (8192, 67M cells) with labeling ON + revert OFF; a6 NEW —
labeling ON + revert OFF on the 06 preset bit-identical to the 06 batch's render field
(labeling is pure analysis); j0; m, n, o, i, j, k, l, b per field. Batch:
BatchRoot(7, "region_labeling") — exactly 4 plates (three thresholds on 1063685222,
threshold_mid on 20260821, the table's most-natural-islands seed), each with grayscale /
.f32 / relief / the labeled-regions overlay / the tag overlay, plus count_size_table.md/.csv.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
Ports the reference's v5 height curve and shelf-detail passes onto Phase 1's shape
and re-calibrates them against this repo's actual pass-1 distribution. This is the
BASELINE the reshape gets judged against, not the reshape.
Core (engine-free, D-060):
- WorldScale — THE vertical yardstick. One metres/raw number (251), replacing the
prototype's three duplicate M_PER_UNIT constants and ~20 bare literals. The
chunk-height coupling it had there is recorded as a DEFERRED vault decision, not
inherited. RawFromMetres divides, matching the reference bit-for-bit.
- HeightCurve — the 7 bands, the frozen corner-fix blends, the per-seed spike
normalization, the 24-corner monotonicity sweep that throws and refuses.
Identity at and below sea, which everything downstream rests on.
- CurveKnots / CurveAnchors — input knots (measured percentiles) and output anchors
(storm ladder) split apart and both made parameters, so the anchors are A/B-able
without editing source. The reference's shipped knots are kept beside the measured
ones as the fidelity yardstick.
- TerrainDetailPass — micro-relief skin plus the shelf-edge KNOT warp (which slides
K3/K4/K5, not height — that is what keeps monotonicity structural). The crater
exclusion is ported and inert until the carve lands.
Tools:
- Shaping — pass 2a, producing the two height fields. classify is bit-for-bit the
raw pass-1 field; render is curved and detailed. Aliased when the curve is off,
as the reference did. Pass1Result is left immutable so the oracle can compare.
- LandHistogram — the calibration engine AND the diagnostic. The reference shipped
six knot literals and threw the measuring instrument away; this rebuilds it.
- ShapingOracle + CurveBaselineTool — four automatic checks before anything is
looked at, and the batch that runs them.
Measured, not assumed:
- Knots re-measured over a 6-seed / 12.8M-sample pool. They differ from the
reference's by at most 5.6 m of world height, against a 44.7 m per-seed spread —
the pass-1 port is faithful.
- Oracle all pass, including pass 1 bit-identical to Phase 1's own .f32 dump.
- Band shares land on 60/13/10/5/8/3/1 to 0.00 pp.
- Knots hold across map size: the 8K delta (5.8 m) sits inside seed noise.
The finding the histograms deliver: 83% of land ends below 100 m and 96% below
220 m, with the median column at 13 m. That is the share targets doing exactly what
they say, not a bug — and it is the developer's call, which is why nothing here
reshapes it and the palette was deliberately left mis-fitted rather than recalibrated
to disguise it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
Stands up the CODE_REPO the rewrite is written into (D-049, D-058). Godot 4.7.2 /
.NET 8 / Godot.NET.Sdk 4.7.2. Nothing is generated, meshed, or ported — this is the
shell and the two data contracts.
Four layers, each with its boundary stated in a directory README:
Core/ math + data only, and engine-free — depends on nothing above it
Server/ authoritative logic (empty this phase)
Client/ rendering (empty this phase)
Tools/ the offline generator — may NOT reference Client (Phase 1 fills it)
Contracts (compiling stubs, no algorithms):
- Column model (D-053): a 2D grid of columns, each a stack of (material, thickness)
runs, any material at any depth. Air is the TOP RUN — there is no air-vs-solid
height branch, and no surface-height accessor exists to reintroduce one. Water is
not a band; it stays an overlay.
- Material schemas (D-054): terrain and building as two append-only registries,
bridged by a recipe seam that is deliberately EMPTY. Identity is a registry key,
never a serialized ordinal. Mesh style is per-ORIGIN and is not a material field.
Rails, so Phase 1 inherits them rather than rediscovering them:
- GenerationScale: MapSize is a parameter, everything derives from it, nothing uses
a raw pixel number. Tightening over the reference — decorrelation offsets are
declared in map widths, not pixels (chat1/00 §4.2).
- ToolingPaths: config/blueprint/output paths all env-overridable, resolved in one
place, so a batch cannot touch the developer's live files.
- FileSafety: the permanent no-deletion rules as throws rather than sentences.
user:// isolation: project name and use_custom_user_dir both pin the runtime dir to
~/.local/share/islaApocalypse-v2/, away from the old prototype's preserved seeds and
batches. Tools/Scenes/UserDirProbe.tscn confirms it rather than assuming it.