The bare TerrainGenConfig defaults did NOT reproduce the terrain the developer
locked, so "run the default generator" was not "the terrain in the gallery" —
the single most expensive fact in the codebase, and the reason a fresh chat
would spend an afternoon chasing differences that were CONFIGURATION, not
regression. This is the deliberate task that ends that, before any river work.
A1 — the defaults ARE the locked shape now. Five fields actually move:
SpeckRevert false->true, MinLandComponentFrac 3e-5->2.5e-7 (120x smaller; the
config default would have eaten real islands, not specks), SouthStretch 0->2,
FragmentAmp 0->0.5, Erosion false->true. Seven more were already correct via
SouthernStretch.Default* / CoastalFragment.Default* and are now pinned as
literals, because TerrainShapeV1 used to do that pinning and this default set
inherits the job. CoastShelf stays OFF — the locked shape has no shelf, and
evaluating it (D-041) is its own later task once water renders. Offshore stays
Off permanently (D-063): islands are organic-only, made by the stretch +
fragmentation and identified by the region layer, never placed.
A2 — the preserve mechanism. The curve knots are percentiles of the FAMILY-OFF
land distribution; flipping the defaults would have moved the pool, the knots,
and with them the render field of every batch including terrain-shape-v1
itself. So the pool is pinned family-off (TerrainGenConfig.WithFamilyOff /
CalibrationPool) rather than the knots being baked: calibration stays live, its
INPUT distribution is held still. The pin was a no-op by construction — it sets
the values the defaults carried the instant before the flip — and re-measuring
after confirms it: pool, all six knots, per-seed spread, shaped max,
monotonicity spikeMax and all seven band shares identical.
Applied wider than "in CalibrateCurve": OffshoreIslandsTool,
RegionLabelingTool and SouthernStretchTool generate their own family-off
field for the Phase-1 anchor, so the pool pin alone would NOT have covered
them and their a1 would have failed for a configuration reason. TerrainGenTool
too — it AUTHORED 02_pass1_port and must stay able to regenerate its own
anchor.
Recorded as a judged-and-parked property: knots measured family-off, applied
family-on. Deliberate, not an oversight. Same disposition as the mid-slope
feather.
A4 — no oracle may pass against a superseded baseline. Six anchors retired
(01/03/04/06/08/09) with their checks and ISLA_T0x_SOURCE defaults; three kept
(chat1/02_pass1_port as the family-off pass-1 guard, chat2/10 and chat2/11 as
the shape and erosion acceptance anchors). Two invariants were RE-POINTED
rather than lost — the southern stretch's north-lock and the coastal-fragment
interior-lock now compare against SAME-RUN fields, which is scale-free and
cannot be invalidated by a moved dump. The retired dumps are kept, not deleted,
and marked superseded in their INDEX.md.
A missing anchor is now LOUD. The old pattern skipped silently, so a moved
anchor did not make its oracle fail — it made it not RUN, and a batch with a
skipped check prints an all-PASS table that reads like a clean one. That is
the INVERSE of the hazard the re-baseline guards against, and the migration
below is exactly the event that would have triggered it, on nine anchors at
once. ShapingOracle.LoadAnchor now separates the two cases: absent -> throw;
present at another size -> loud INCONCLUSIVE, which is a fail, never a pass.
TerrainShapeV1 inverted from PRESET to GUARD and moved to its own file.
Apply() is gone — stamping the values on top of the defaults would MASK a
drift instead of catching it. Its constants are now the assertion target, and
Assert() refuses a run whose defaults have drifted off the locked shape.
B/C — batches are namespaced by chat: batches/<chat>/NN_slug/. Task numbers
restart at 00 per chat, so a flat root collided the moment a second chat
existed — four colliding prefixes across 25 batches, separable only by slug.
ToolingPaths.ChatSlug is REQUIRED (throws if unset) and defaults per tool to
its authoring chat, so re-running reproduces a batch in place while ISLA_CHAT
redirects — which is also what stops an acceptance run from overwriting the
very anchor it checks against. Writes go through BatchRoot; historical READS
compose against BatchesRoot and so carry the prefix in their own source string
("chat1/02_pass1_port"). The 25 existing batches were migrated moves-only.
ACCEPTANCE — 16 of 16 byte-identical, 0 failed. All 8 gallery seeds at 8192
from the bare defaults are byte-identical to chat2/10_frag4_seed_gallery
(= terrain-shape-v1,
|
||
|---|---|---|
| .. | ||
| README.md | ||
batches/ — convention only; the data lives under user://
This folder documents the batch layout. It does not hold batches.
Real batch output is written under ISLA_OUTPUT_DIR (default user://output/batches), resolved by
Core/Scripts/ToolingPaths.cs. Generated worlds are large, regenerable, and do not belong in git.
Layout
batches/<chat>/NN_<name>/ <chat> = the chat namespace · NN = the task number that ran it
├── INDEX.md what varied, what to look at, what was concluded
├── scratch/ intermediates — PERSISTENT, never cleaned
└── <seed>_<variant>/ one directory per generated world
⭐ The <chat> segment (rivers/01)
NN is the AUTHORING TASK NUMBER, and task numbers restart at 00 in every build chat — so a flat
batches/ collides the moment a second chat exists. It did: chat 1's 02_pass1_port and chat 2's
02_curve_continuous are both "batch 02", and nothing in either name says which chat made it. On the
real pile there were four colliding prefixes (02, 03, 04, 06) across 25 batches, separable only by
SLUG.
The slug is set by ToolingPaths.ConfigureChat(...) and is required — with none set, BatchRoot
throws rather than writing to the un-namespaced root. Each tool defaults to its own authoring chat, so
re-running it reproduces its batch in place; ISLA_CHAT redirects a run to another namespace, which
is what keeps an acceptance run from overwriting the very anchor it is checking against.
⚠ Writes are namespaced; historical READS carry the prefix themselves.
BatchRoot(task, descriptor)inserts<chat>.BatchesRootis the plain root, and everyISLA_*_SOURCEanchor composes against it — so an anchor default is written out in full, e.g."chat1/02_pass1_port". Namespacing only the writes would silently orphan every historical read. That is why a missing anchor now THROWS (ShapingOracle.LoadAnchor): a moved anchor used to make its oracle not RUN, and a batch with a silently-skipped check prints an all-PASS table that reads exactly like a clean one.
INDEX.md is not optional. A/B comparisons are browsed by a human, and a flat directory of
same-named PNGs is not browsable.
⚠ No deletions here. Ever.
FileSafety.AssertDeletable refuses any path under the batches root, and it refuses without a
stated reason regardless. Intermediates persist. Any deletion that does happen elsewhere is
named explicitly in the run report.
→ Tools/README.md, Design - Tooling - Iteration and Batching.md