Commit graph

8 commits

Author SHA1 Message Date
bddd3636dd chat2/09: coastal fragmentation — a perimeter-wide, band-limited noise, amplitude laddered at a fixed stretch
A dedicated term, not the edge noise scaled: pass 1's edge noise is positive-only, squircle-
modulated, 2.5x the base frequency - the coastline's jitter; scaling it would roughen the whole
rim and bias the coast inward. CoastalFragment adds, to the PRE-power falloff, amp * window(f) *
noise(x + off, y + off) with its own deterministic field (seed offset 9109, coordinate offset
0.37 map widths - D-059), zero-mean, at 12 periods per map width (the lobe/neck scale, chosen by
probe against 20 and 32: 20 climbs into smalls, 32 is fuzz), weighted by a smooth window on the
falloff value itself, centred 0.66 (the coast's falloff for a median base noise, from the 08
diagnosis), half-width 0.18, EXACTLY ZERO beyond - so the interior, the massif and the deep sea
are bit-identical by construction. Thin necks of barely-land flip first; nothing is detected,
nothing is stamped. FragmentBitesOnly is exposed (probed: it erodes the coast inward - mainland
-3/-8/-14 % at 0.06/0.15/0.30 - rather than detaching pieces; off by default).

Batch: BatchRoot(9, "coastal_fragment") - 4 amplitudes (0.06, 0.15, 0.30, 0.50) x task 08's two
seeds at 4096, stretch fixed at 2 (08's stretch_3 rung, whose dump is the bit-identical baseline,
a8), sinker untouched, speck revert at < 4 cells, offshore/shelf off; lean render (regions overlay
+ relief + .f32), the hemisphere-split count/size table with largest-three and histograms.

Oracle, all passing: a1 (the curve untouched), a8 x2 (frag OFF at stretch 2 == the 08 field,
16.8 M cells each), interior locked (r: every cell clear of the window bit-identical, 8/8),
high-ground report (s, informational), centre is land, tag/coastline, classify == raw, ids and
heights deterministic.

The read: both hemispheres fragment; the north more readily (N 10 -> 28, 13 -> 40 islands across
the ladder; S 4 -> 31, 14 -> 28) and the south's big stretch pieces are the zero-mean noise's
other edge: on 999999937 the 83k-cell southern fragment is BRIDGED back onto the mainland at amp
0.50 (mainland +183k). The working range is amp 0.15-0.30; 0.50 is the bookend. Graduation held.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
2026-08-22 07:09:27 -04:00
537bd3e048 chat2/08: southern stretch, EXPLORATION — the fragmentation knob space, diagnosed then laddered
Diagnostic first (SouthernStretchTool, ISLA_DIAG_ONLY; scratch/southern_diagnosis.md). Along
the central south profile the mask blend climbs 0.018 per 0.01 N, edge noise adds 0.03-0.06,
and the southern sinker is zero until 0.75 N then adds 0.024 per 0.01 N before the 2.5 power;
the coast sits at 0.76-0.82 N, ~0.06 N north of where the blend alone would end. The stretch
compresses the southward distance the mask sees inside a fixed feathered latitude band,
y' = yB + (y - yB)/(1 + s*ramp), texture (base noise, edge noise, latitude) untouched, so the
extended mass keeps the elevation of the rows it came from. With the sinker reading the REAL y
it cancels the stretch outright: median coast saturates at 0.83-0.84 from s = 1 to 16 and the
south stays 1-10 tiny nubs on both seeds. The lever is therefore two things - extend reach AND
hold the sinker back - and this pass couples them 1:1 (StretchSinker = true; the real-y mode is
kept as the config's other setting). Ladder chosen from the sweep, non-linear: 0.5, 1, 2, 3, 5
(peninsula / onset / few big pieces / big pieces consolidating / over-stretched sheet); 8 and 16
are indistinguishable from 5.

Topography: cells above the band take the untouched code path (fy == y), so the classify field
north of the band is bit-identical by construction. SouthernStretch.cs holds the band constants
(start 0.70 N, feather 0.05 N - fixed for the batch) and the ramp. TerrainGenConfig: SouthStretch
(the swept axis, 0 = off), SouthBandStartFrac, SouthBandFeatherFrac, StretchSinker.

Oracle, all passing: a1/a3 stretch OFF bit-identical; a3b stretch 5 north of the band
bit-identical to the task-03 dump (2.93 M cells), a4b the same at 8192 against the
terrain-curve-v1 gallery dump (46.97 M cells north of the band, 20.1 M differ below - the
relaxation); per field north bit-locked classify and render vs stretch 0 (p, p2), northern
island set invariant (q), centre is land, tag/coastline, classify == raw; ids and heights
deterministic. Batch: BatchRoot(8, "southern_stretch_explore") - 5 levels x 2 seeds
(1063685222, 999999937 - the two 07 seeds with the most mainland south of the band), lean render
(regions overlay + relief + .f32), the hemisphere-split count/size table. Region labeling on,
offshore / shelf / speck revert off: the pure fragmentation signal.

The read at 4096: s 0.5 peninsula (coast +0.04 N); s 1 onset (999999937 6 -> 12 southern islands,
11 substantial); s 2 few big pieces (35k / 86k-cell fragments); s 3-5 a solid sheet to the trench
whose southern edge is the trench wall - a ruler-straight coast - and whose island count falls as
pieces re-merge. Gravel never dominates. Graduation held, per the task.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
2026-08-22 05:54:12 -04:00
c32a3b177c chat2/07: the region-labeling layer — label all land, tag by construction, tunable speck revert
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
2026-08-22 03:50:33 -04:00
e8571b2778 chat2/06: offshore islands — revert the forced floor, tune the organic layer for coverage
The seeded floor from chat2/05 — FloorNorth/FloorSouth, the PCG32 placement, StampWeight,
StampRadiusFrac/StampCoreFrac/StampEdgeJitter, the centre separation and land gap, the
floor self-check and oracle (h), the centre rings on the tag overlay — is reverted out
whole, as a forward commit. It looked stamped. It is one checkout away at 3b96e06. What
remains is ONE island mechanism: the organic noise-field layer, with chat2/05 stage 2's
shape untouched (freq 16, crest 24 m pre-curve, core 0.25, sharpness 2.5, corners on,
moat + falloff test + outer bound unmoved), now OffshoreSettings.Organic(). The count per
hemisphere is a statistical outcome of the tuning, read off a table, never guaranteed.

The south was measured before anything moved (OffshoreDiagnosis, over task 01's pool at
2048): the premise that the south generates fewer islands is not what the field says.
The NORTH has 1.75x less island-eligible ocean (56 % of its ocean is zone vs 69 %; 233
zone rows per column vs 407) and under half the island candidates (1,183 over-threshold
peaks in zone vs 2,422). Per cell the south is richer too (484 vs 414 candidates per
Mcell). The gate that loses candidates is the moat in the north and the falloff test in
the south — the bulging south coast pushes the 0.72 contour out — but neither binds
enough to suppress it. So the fix is density, and the binding constraint for "a couple
north, consistently" is the north.

The knobs: Density (the main one, the calibration quantile) and SouthWeight (south density
= Density x weight — applied to the quantile, so a south island looks exactly like a north
one; there are just more). Three levels batched on 12 seeds at 4096, SouthWeight 1.25:
0.016 N 2/4.4/10 S 8/13.8/21 (every seed clears N >= 2, S >= 3, but N's minimum IS 2),
0.022 N 3/6.3/12 S 15/19.9/29 (the preset — the first level whose north minimum sits
comfortably above the target), 0.030 N 3/7.8/15 S 19/25.2/33 (the too-many bookend). The
south weight is the developer's stated preference, not a fix; the SouthWeight 1.0 tables
(S 6/10.9/16 at 0.022) are in the batch's scratch/ for the comparison.

The guards, so more density does not buy slop, each a revert by component membership:
specks (< 3e-5 of the map's area, ~500 cells at 4096 — 70-100 per field at these
densities, the noise caps the reference also surfaced), clusters (the smaller of two
islands whose shores are within 0.8 % of the map width — 0-7 per field), blobs (> 6e-4 —
never bit). And the reference's submerged humps, now attributed by HUMP rather than by
bounding box: the first 4096 plate probe showed hollow ghost rings beside islands — a
guard-dropped island's cap went back to seabed but its rim survived inside its
neighbour's bbox keep-region. A hump (one connected raised region) now stays only if it
holds a kept island, and a dropped island's own cap goes even inside a kept hump.

Oracle, all passing: a1 (Phase-1 dump), a3 (task-03 dump), a4 NEW — offshore OFF at 8192
bit-identical to the terrain-curve-v1 tag's own 04 gallery dump over 67,108,864 cells —
j0 shelf inert on land, and per field moat / mainland unmoved / tag-coastline / HMaxSeed
(unchanged, 1.289169) / classify on all 36 table fields and 4 plates. Batch:
BatchRoot(6, "offshore_organic_tune") — exactly 4 plates (three densities on 1063685222,
density_mid on 424242, the table's sparsest south) + count_table.md/.csv + diagnosis.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EY3ZTF6NwzF8ukBHQXSK7
2026-08-21 06:22:54 -04:00
3b96e06c1a chat2/05: offshore islands — rejoin the stub faithfully, then reshape to a guaranteed hybrid
Stage 1 fills the IslandFalloff.cs stub from the reference, verbatim: the coast shelf
(below-sea only, depth-preserving, held strictly below sea by MathF.BitDecrement — the
clamp that makes "cannot move the waterline" exact rather than statistical) and the islet
layer (OffshoreBlob, CalibrateThreshold against the field's ACTUAL distribution, the
OffshoreZoneWeight moat + pre-Trench-falloff test). Both run as pass 1b — OffshorePass, a
second sweep over the finished pass-1 arrays with the same per-pixel arithmetic in the
same order — and HMaxSeed is retaken AFTER them, as the reference did. That closes
chat2/00 Drift §2. The value did not move on any of 15 runs; the order is now right by
construction and oracle (l) prints it every time.

Stage 2 is the reshape, OffshoreSettings.Hybrid(): a seeded floor of ≥2 N / ≥4 S islands
placed by a PCG32 off the world seed — min-separated, clear of ALL existing land by a gap
so each stamp is its own connected component by construction, fully inside the zone so
the moat and falloff protections gate the floor exactly as they gate the organic layer —
plus the noise layer on top, smaller (freq 16), lower (24 m pre-curve, which the
preserved toe squashes to ~5 m and keeps there across curve tweaks), flatter (core 0.25),
crisper (edge sharpness 2.5, stamp rim jittered so it is rigid without being a compass
disc), south-weighted (0.007 N / 0.012 S, blended across the midline), corners allowed
(trench mask 0.90→0.97). Every lifted cell is tagged with its hemisphere — NORTH is rows
[0, N/2), y runs south, read off the spine's southern fade and the southern sinker, not
invented — and carried through Pass1Result → Pass2Result for a consumer that does not
exist yet.

Two things the probes taught, both now in the code: the first organic densities produced
183 blobs of which 174 were noise debris (a six-config sweep fixed that), and the
reference's lerp-to-crest leaves shallow humps across the seabed wherever a blob fails to
surface (a guard reverts them outside any surviving island's skirt; specks by component
membership, bumps by location). The faithful control keeps both, because it is the
reference — its islets measure min 1 cell, median 28.

Oracle, all hard checks passing: floor met on every hybrid seed (N 2–4, S 9–16); zero
land bridges; every offshore-OFF land cell bit-identical with offshore ON; tag ↔ coastline
consistent in both fields; curve-off still bit-identical to Phase 1's dump and
continuous_restored to task 03's. Both gates default OFF, deliberately: flipping them is
the act that retires the Phase-1 regression dumps, and that belongs in a task that
re-baselines the oracles.

The faithful control on seed 8675309 produced one north island. That is the gap the
floor exists to close.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
2026-08-21 05:14:53 -04:00
639dc5f5a9 Phase 2a: restore the mountain — calibrate the climb to the staircase, not to a guess
chat2/02 dissolved the terraces and lost two thirds of the mountain with them, then
concluded the loss was structural and needed a Phase-1 noise change. That conclusion
was wrong, and this commit is the refutation.

A monotone curve is a free reparametrization: it may be gentle in one place and steep
in another, and can lift bottom-heavy input into a substantial massif without ever
going flat. The area above a height is set by where the percentile->height mapping
crosses it, and that mapping is entirely ours to choose. The 02 sweep that "proved"
the loss structural varied climbFeather, which shapes the JOIN, not the mass
distribution — the wrong knob, and too strong a conclusion drawn from it.

Core/ClimbCalibration — the climb's control points are now MEASURED off the staircase
instead of invented from shape knobs. For p in {10,30,50,70,85,95} of above-ceiling
land, take that percentile's raw height and its staircase output height; PCHIP through
the pairs. That reproduces the staircase's elevation envelope, so the mountain mass
returns, while MinNormalizedSecant floors every grade so the flat bench and plateau
interiors become slope. The floor bites on exactly one segment — the plateau — which
is precisely where the staircase was flat.

ContinuousCurve.BuildCalibrated joins it to the same pinned lowland handover, the same
C1 join and the same per-seed spikeMax. The 02 analytic path survives unchanged as the
"before" contrast, and deliberately keeps its strictly-increasing-secant rule: a
calibrated curve is WAVY by design, so convexity is the wrong invariant for it and the
secant floor is the right one.

peakSharpness replaces summitDrama and fixes its bad trade. Drama steepened the peak by
pulling the summit ONSET down, dragging the whole massif with it (p99 199 -> 121 m).
Sharpness reshapes only above the last measured percentile, leaving that height fixed,
so peak and massif are independent: raising it leaves p90, >100 m and >220 m untouched
and only moves land within the summit.

Measured, both seeds, 2048:

  variant                >100 m   >220 m    p90
  staircase (target)     16.05%    4.53%  127.4 m
  continuous_02default    4.80%    0.63%   58.4 m
  continuous_restored    14.25%    3.45%  123.2 m
  continuous_bigger      19.70%    5.80%  163.5 m

Oracle all hard checks pass, including (a2) staircase still bit-identical to task 01's
dump and (d) lowlands bit-identical across every calibrated variant. New (g) reports
land above 100/220 m per variant and is deliberately NOT gated — it is a taste target
the developer tunes, and gating it would make mountainLift unusable. What it must never
do is stay silent, which is how 02 lost the mountain unnoticed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
2026-08-20 04:14:47 -04:00
8e55326a84 Phase 2a: continuous grade — smooth the upper staircase, preserve the lowlands
rev 3 of the curve redesign. The developer's verdict on the 01 baseline was that the
LOWLANDS ARE GOOD; the fault is the terracing above them. So this adds a second curve
mode that preserves the low plain bit-for-bit and replaces everything above the flood
line with one smooth monotone climb.

Core/ContinuousCurve — piecewise, and the pieces have different loyalties:
- at/below sea: identity, as ever.
- above sea to K2: DELEGATES to HeightCurve's own toe+red branches. Not "equivalent" —
  the same code path, so the same floats. Oracle (d) holds it to that.
- above the ceiling: a Fritsch-Carlson (PCHIP) monotone spline to the 420 m cap,
  C1-joined to the red band's exit slope. Monotone by construction for any ordered
  control points, which retires the 24-corner sweep; a 10k strict-increase sample runs
  per seed anyway, because "cannot fail" is worth a millisecond.
- Build() REFUSES rather than degrades: a ceiling near the old bench, a drama that folds
  the summit under its own onset, control-point secants that are not strictly increasing
  (the no-magnet rule, enforced rather than hoped for).

Only BENCH_*/PLATEAU_* are dropped. SEA/ORANGE_CEIL/RED_CEIL survive because they are
the storm-ladder FLOOD TIERS and they live inside the preserved lowland; PEAK_CAP and
the per-seed spikeMax normalization survive as the summit.

Shelf detail is forced off in continuous mode: the flat benches it de-slabbed no longer
exist, and painting noise on the climb now would pre-judge what erosion should carve.

Oracle, all hard checks passing:
- (a1) curve off is bit-identical to Phase 1's dump.
- (a2) staircase mode is bit-identical to TASK 01's dump — the control is provably the
  control, not a re-derivation. (CurveBaselineTool is pinned to Staircase so the config
  default moving to Continuous cannot drift it.)
- (d) lowlands bit-identical to the staircase over 3.6M cells, every continuous variant,
  both seeds. The lifted_WRONG bookend fails it on 1.6M cells, as intended.
- (f) sea identity per CELL, not per count, including 67M cells at 8192.

The finding, measured and recorded in the batch scratch: the massif SHRANK. Land above
100 m goes 14.9% -> 4.8%, above 220 m 4.5% -> 0.6%. A feather sweep to the practical
floor recovers ~1.3 points, so this is structural, not a tuning miss: the staircase's
highland area was an artifact of the bench and plateau acting as magnets, and a curve
with no magnets preserves the raw distribution's bottom-heavy shape. "No terraces" and
"the same land up high" are not both available from curve work alone.

Exploration batch, not convergence. A tuning pass follows once a direction is picked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
2026-08-20 03:41:27 -04:00
35b4818e9e Phase 2a: the faithful redistribution curve, re-measured against v2's own output
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
2026-08-20 01:38:10 -04:00