Hoshe found a CRITICAL coherence defect (Tyre APPROVE):
- CRITICAL: cluster_scatter received the per-voxel sub_chunk_seed as its cluster
seed, so voxels in the same 6m cell got different scatter -> salt-and-pepper,
not the §3-mandated clustered patches. Re-key on (world, body, cluster-cell):
add SeedDomain::Cover=10 (pinned); cluster_scatter(world_seed, body_id, x, y)
derives for_body(world_seed,body_id).derive(Cover, cluster_pair_id). derive_cover
now takes (world_seed, body_id, ...). (Hoshe #1)
- The coherence test was tautological (constant seed bypassed the broken path).
Rewritten to the production derive_cover signature in a cold-lake scatter band;
it FAILED at 20% on the old code, passes 100% now. (Hoshe #2)
- Zigzag-encode cluster (cx,cy) before Cantor pairing (negatives were cast as u64
directly); fix the misleading 'no collision' comment. (Hoshe #3, Tyre)
- Sea-ice band was inverted (~85% ice at the -2 onset). Now monotonic: ice grows
~8% (onset) -> ~92% (cold edge); leads (open gaps) layered only in the cold half
via a 3x super-cell, passing the super-cell index directly. Dead depth==0 guard
removed. (Hoshe #4, Tyre #1/#3)
cargo test passes, clippy -D warnings clean, fmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hoshe REQUEST_CHANGES + Tyre APPROVE:
- Cache key omitted world_seed → a cache reused across worlds could return
another seed's column. CacheKey is now (world_seed, body_id_hash, voxel_pos).
(Hoshe #1)
- Sub-chunk voxel seed reused SeedDomain::ChunkContext (tier-collision risk vs
the region meander seed). Add SeedDomain::Voxel = 9 (append-only, pin test
updated) and use it. (Hoshe #2, Tyre #1)
- get_or_derive forced callers to pre-compute body_id_hash while fnv1a_64 is
pub(crate). Drop the param; compute the hash internally. (Tyre #2)
- Fix two inaccurate comments (micro-relief range [-4,+3] not ±2; scatter mask
is 4-bit [0,15] not bits [4:6]). (Hoshe #3/#4)
- Strengthen the LRU eviction test: add a test-only contains_voxel() helper and
assert WHICH entry was evicted (refreshed pos survives, LRU pos evicted). (Hoshe #5)
- LRU O(capacity)-scan perf flagged on T-1031 (Tyre #3, deferred to the budget harness).
cargo test passes, clippy -D warnings clean, fmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
D-239 §1/§7/§10 — the lower derivation chain end-to-end with the AlluvialPlain
fallback only (specialized families are T-1029).
- chunk_context.rs: ChunkContext (64m) — basin_direction + meander phase/wavelength
+ active-channel params. NO per-tile flow_direction[64x64] (D8 ~152m coarser than
a chunk, D-239 §10); features >64m seed from Region-or-higher. SeedDomain::ChunkContext=8.
- voxel.rs: VoxelColumn (1m) composing the D-228 tile axes (TerrainMaterial /
FloorMaterial=None / Vegetation / Water / elevation_m). Derive-on-demand + VoxelCache
(LRU BTreeMap, never persisted per D-227). Domain warp (T-1026) wired through:
f64 warp -> as-i32 truncation to the voxel address; all material/morphology gates
integer (D-010 — the meander wavelength f64 is truncated before the Water decision).
- AlluvialPlain generator: Soil/Wetland terrain, climate vegetation, integer-meander
channel (Shallow/Deep) from ChunkContext. The other 7 families are dispatch stubs
(canonical terrain material, fall back to AlluvialPlain geometry) — no panic.
29 new tests incl end-to-end determinism (same seed/pos -> identical column),
cache hit/miss/eviction/re-derivation, warp-applied. cargo test 1563 pass,
clippy -D warnings clean, fmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First foundation slice of the Atlas-to-tile derivation model (epic T-974),
building the carrier layer ahead of its T-1027+ consumers.
T-1026 — Anti-squaring domain warp (D-239 §4): stateless pure
fn(seed,body_id,pos)->(f64,f64), ±8m, f64 to the final voxel then as-i32
truncation for IEEE-754 cross-target determinism. New domain_warp.rs,
SeedDomain::DomainWarp; golden-vector + cross-thread tests. Position math
only — D-010 integer discipline preserved downstream. Marked dead_code
until the T-1028 VoxelColumn pipeline consumes it.
T-1023 — RegionProfile carrier (D-239 §1,§10): new RegionProfile +
TectonicClass/GlaciationGrade/PrecipitationClass enums + BodyParams; derived
per-region river_threshold replacing the global 200 for tile consumers.
regions: BTreeMap on BodyWorldState, populated via the cascade's new
RegionProfile layer (runs when body_params is Some, else falls back to
Settlement). D-010 integer discipline, BTree ordering.
T-1024 — District climate primitives (D-239 §2): nullable temperature_c +
moisture on RegionProfile, mean-annual scalar (no clock dep; dynamic branch
deferred to Q-105). Hybrid inputs — new bodies.axial_tilt_deg column imported
from planet-gen body-defs (populate_axial_tilt_deg, 2611 bodies), luminosity
and orbital distance derived at runtime; greenhouse + diurnal-swing tables in
source-canonical climate_constants.toml. D-239 implementation note added.
cargo test: 1498 passed, 0 failed. clippy clean (pre-existing
large_enum_variant only). make check-systems-db: stamp fresh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the Hoshe (QA) + Tyre (architecture) review of the SeedChain/cascade
work:
- Golden was pinning an empty river network (128x64 produced 0 river cells).
Bumped to 256x128, where GJ1c yields a real network (93 river cells, 19
mouths) — Layer 1's rivers are now actually guarded, not just attractors.
- SeedChain::for_body(world_seed, body_id) + fnv1a_64: the single canonical
body_id(String) -> u64 path (FNV-1a, the repo convention), so callers can't
derive divergent worlds from the same seed via different ad-hoc hashes. The
golden now uses it.
- Stability guards: seed_domain_discriminants_are_pinned test (CI fails if a
SeedDomain tag is renumbered); AttractorType gains #[repr(u8)] + explicit
discriminants (it's cast as a sort key in features.rs).
- Tests: SeedChain::root(0) non-degenerate; run_cascade error path (missing
file -> Err, not panic).
- Comments: clarified the id=0 derivations (sibling separation is caller-side
via the per-district/quarter chain; #957 threads the index), tightened the
all_district_types reachability comment (it pins the seed-0 sequence, not a
probabilistic claim), and noted the golden's WORLD_SEED is cosmetic at
Layers 0-1 + the x86_64 f32 capture caveat.
Deferred with reason: the run_cascade -> CascadeInputs struct refactor (Tyre)
is left for #954 — designing Layer-2's context shape now would be later-phase
detail, and there's a single caller to migrate then. EntityRng keeps its
domainless combine (migrating is stream-changing) — noted in D-224.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New top-level seed module that owns every deterministic-RNG primitive:
- splitmix64 — the one canonical mixer (was duplicated as a private fn in
simulation/rng.rs; EntityRng now imports the shared one, no behavior change)
- AtlasRng — moved here from atlas/rng.rs (it is a generation-RNG primitive,
not atlas-specific); atlas now depends on seed, not the reverse
- SeedDomain — append-only domain tags (Body/Layer1Topography/Layer3Settlement/
Layer4Quarter/Block/Npc) for collision-proof per-domain seed separation
- SeedChain — root(world_seed) → derive(domain, id) → atlas_rng()/seed(), per
the D-224 formula splitmix64(self ^ splitmix64(domain)) ^ splitmix64(id)
Structural move only — SeedChain is not yet threaded through the cascade
callers (skeleton_gen still uses ad-hoc wrapping_add pre-mixing); that is the
next step. Unit tests cover the splitmix64 known-vector, avalanche,
determinism, domain/id separation, and chain composition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>