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, a59e52f); all 8 erosion fields byte-identical to
chat2/11_erosion (= ea291ea). Every gallery table row and every erosion
statistic reproduces its recorded value exactly. DrainageTool's a11 passes
bit-identical over 67,108,864 cells, and its analysis reproduces batch 12
exactly — so the whole chain rivers depends on (shape -> erosion -> drainage)
is unchanged. All 12 edited tools re-run clean; both new guards negative-tested.
The baseline moved in DEFAULTS, not in TERRAIN.
-> XX_Human/output/rivers/01_rebaseline_and_batch_namespace.report.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhkXBQh2tDmcWKpXYcj8vj
47 lines
2.6 KiB
Markdown
47 lines
2.6 KiB
Markdown
# 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>`. `BatchesRoot` is the plain root, and every
|
|
> `ISLA_*_SOURCE` anchor 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`
|