fix(simulation): VoxelCache precondition doc + sec_phase decontamination + cache blend test (T-1042)

Addresses PR #167 review (Hoshe H1/H2/H3, Tyre T2).

H1 — the VoxelCache 'same (seed, body_id, tile_pos) -> same column' invariant
is no longer unconditional after the T-1042 blend (the key omits blend_weight/
secondary). Replaced with an explicit precondition on the struct + get_or_derive:
the caller must pass the ChunkContext of the chunk that contains the tile.

H2/T2 — sec_phase claimed to be 'position-independent' but mixed in
primary_meander_phase/2 (the primary's per-position seed), making the secondary
phase proxy asymmetric and primary-contaminated. Dropped that term; sec_phase is
now the secondary's own morphology scalars (slope_q + moisture_q), with a comment
honestly naming it a bounded cosmetic approximation and where to thread the real
secondary seed if needed.

H3 — added cache_boundary_tile_blend_is_stable: derives a boundary ChunkContext
(blend_weight=128, secondary=Some), calls get_or_derive twice, asserts a cache
hit + identical blended elevation + that the Cow::Owned blend path actually ran.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 17:30:46 +02:00
co-authored by Claude Opus 4.8
parent d974cf42b0
commit ff5e7ee944
2 changed files with 143 additions and 10 deletions
+16 -9
View File
@@ -292,15 +292,22 @@ pub fn derive_chunk_context(
let sec_wavelength = derive_meander_wavelength(sec);
let sec_channel_width = derive_channel_width(sec);
let sec_phase = {
// Secondary district's meander phase uses a secondary seed so it
// differs from the primary (different district-scale chunk id).
// We approximate: use the secondary's wavelength-derived phase
// as an integer representation of its channel geometry. For a
// well-defined phase, we re-derive from the secondary's slope and
// moisture as a deterministic position-independent proxy.
// Integer arithmetic (D-010).
(sec.slope_q.wrapping_add(sec.moisture_q) as u8)
.wrapping_add(primary_meander_phase / 2)
// The secondary district's true meander phase would require its
// district-scale seed, which in turn requires knowing the adjacent
// district's chunk coordinates — information not available at this
// call site. We use a bounded structural approximation: a hash of
// the secondary's morphology scalars (slope_q, moisture_q), which
// are the same inputs that drive `derive_meander_wavelength` and
// therefore capture the secondary district's channel character.
//
// This approximation is intentionally asymmetric in an acknowledged
// way: the blended phase is a cosmetic continuity aid at the 2 km
// seam (meander phase), not a structural gate decision. The seam
// is already invisible at the elevation level from elev_q blending;
// the phase contribution is second-order. If the real secondary seed
// is ever threaded through here, replace this with the proper
// district-scale derivation. Integer arithmetic (D-010).
sec.slope_q.wrapping_add(sec.moisture_q) as u8
};
let w = weight as i32;
let w_sec = 255 - w;