T-1030 — the static scattered freeze/snow model (D-239 §3). Adds a SeasonalCover { None, Snow, Ice } overlay axis to VoxelColumn (D-228 — cover, not TerrainMaterial), derived after the family generator in derive_voxel_column (one site; the 8 family generators are untouched).
The §3 model
Spatially-coherent cluster scatter (the load-bearing requirement — clustered patches, never per-tile dice): coarse 6 m cluster cells via div_euclid → Cantor pair → splitmix64 ^ sub_chunk_seed, so all voxels in a cell share the freeze decision → ragged patches. Integer/hash only (D-010).
Bands by surface (classify_surface from morphology_zone + water):
Freshwater (Lake / wet RiverBank·Meander·Delta·Estuarine·TidalFlat): +5 °C → −10 °C (the +5 = night-frost reaching the coldest scattered tiles).
Salt water (OpenOcean): own lower/wider band, −2 °C onset → −14 °C, with a 3×-coarser sheets + leads pack-ice pattern.
Land snow: +2 °C → −10 °C, moisture-gated (moisture_q < 30 → None, bare frozen ground).
Frozen fraction grows linearly across the band; permanent (no scatter) below it (poles/glaciers).
Airless (temperature_c == None) → cover None.
Deferred (Q-105 forward contract)
The transient/clock-bound layer (dawn frost that melts by noon → time-of-day passability shifts) requires Q-105's regional clock state and is not built — only the static mean-state cover field. Documented as a forward contract on the enum + derive_cover.
Tests
16 new cover tests: real spatial-coherence (≥90% intra-cluster agreement + inter-cluster variety), band edges per surface (freshwater/salt/snow above/within/below), moisture gating, salt-vs-fresh distinction, airless→None, determinism.
Verification
cargo test passes, clippy --all-targets -D warnings clean, fmt clean. D-010 integer throughout (temperature as i32, hash-based scatter — no f64/trig).
Ticket: T-1030 (epic T-974). With T-1031 (the verification harness) this completes the epic.
## Summary
**T-1030** — the static scattered freeze/snow model (D-239 §3). Adds a `SeasonalCover { None, Snow, Ice }` overlay axis to `VoxelColumn` (D-228 — cover, **not** TerrainMaterial), derived after the family generator in `derive_voxel_column` (one site; the 8 family generators are untouched).
### The §3 model
- **Spatially-coherent cluster scatter** (the load-bearing requirement — *clustered patches, never per-tile dice*): coarse 6 m cluster cells via `div_euclid` → Cantor pair → `splitmix64 ^ sub_chunk_seed`, so all voxels in a cell share the freeze decision → ragged patches. Integer/hash only (D-010).
- **Bands by surface** (`classify_surface` from `morphology_zone` + water):
- Freshwater (Lake / wet RiverBank·Meander·Delta·Estuarine·TidalFlat): +5 °C → −10 °C (the +5 = night-frost reaching the coldest scattered tiles).
- Salt water (OpenOcean): own lower/wider band, −2 °C onset → −14 °C, with a 3×-coarser **sheets + leads** pack-ice pattern.
- Land snow: +2 °C → −10 °C, **moisture-gated** (`moisture_q < 30` → None, bare frozen ground).
- Frozen fraction grows linearly across the band; permanent (no scatter) below it (poles/glaciers).
- **Airless** (`temperature_c == None`) → cover `None`.
### Deferred (Q-105 forward contract)
The transient/clock-bound layer (dawn frost that melts by noon → time-of-day passability shifts) requires Q-105's regional clock state and is **not built** — only the static mean-state `cover` field. Documented as a forward contract on the enum + `derive_cover`.
### Tests
16 new cover tests: **real spatial-coherence** (≥90% intra-cluster agreement + inter-cluster variety), band edges per surface (freshwater/salt/snow above/within/below), moisture gating, salt-vs-fresh distinction, airless→None, determinism.
### Verification
`cargo test` passes, `clippy --all-targets -D warnings` clean, `fmt` clean. D-010 integer throughout (temperature as i32, hash-based scatter — no f64/trig).
Ticket: T-1030 (epic T-974). With T-1031 (the verification harness) this completes the epic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
D-239 §3 — the static mean-state freeze/snow cover. New SeasonalCover
{None,Snow,Ice} overlay axis on VoxelColumn (D-228 cover, NOT TerrainMaterial),
derived after the family generator in derive_voxel_column.
- Spatially-COHERENT cluster scatter (never per-tile dice): coarse 6m cluster
cells (div_euclid) -> Cantor pair -> splitmix64 ^ sub_chunk_seed, so voxels in
a cell share the freeze decision -> ragged patches. All integer (D-010).
- Bands by surface (classify_surface from morphology_zone + water): freshwater
+5..-10C; salt water (OpenOcean) -2C onset / -14C, 3x-coarser sheets+leads;
land snow +2..-10C, moisture-gated (moisture_q<30 -> None). Linear frozen
fraction across the band; permanent below the band (poles/glaciers).
- Airless (temperature_c None) -> cover None.
- Transient/clock-bound layer (dawn frost melting by noon) doc'd as a Q-105
forward contract; only the static model is built.
16 new cover tests incl real spatial-coherence (>=90% intra-cluster agreement),
band edges per surface, moisture gating, determinism. cargo test passes, clippy
-D warnings clean, fmt clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Reviewers: Hoshe (code quality) + Tyre (architecture). Read from the branch worktree. No merge-path surface.
Tyre (Architecture): APPROVE
SeasonalCover correctly modeled as a D-228 cover overlay (orthogonal to TerrainMaterial), appended after the family generators; §3 fidelity solid across all three surfaces; Q-105 forward contract cleanly scoped (static mean-state only). Three clarity notes (dead onset branch, param naming, pack-ice quantization) — all folded into the fixes below.
Hoshe (Code Quality): REQUEST_CHANGES → resolved
Caught a critical defect Tyre and the lead both missed.
Findings & resolution (88b43235)
#
Sev
Finding
Resolution
1
critical
cluster_scatter was keyed on the per-voxelsub_chunk_seed, so voxels in the same 6 m cell got different scatter → salt-and-pepper, violating §3's "clustered patches, never per-tile dice"
Re-keyed on (world, body, cluster-cell): new SeedDomain::Cover = 10; cluster_scatter(world_seed, body_id, …) derives for_body(...).derive(Cover, cluster_pair_id).
2
critical
The coherence test passed a constant seed to derive_cover, bypassing the per-voxel variation — so it couldn't catch #1 (tautological)
Rewritten to the production derive_cover signature in a cold-lake scatter band. It failed at 20% on the old code and passes at 100% now — it genuinely gates the contract.
3
med
Cantor pairing cast negative cluster coords as u64 directly ("no collision" claim false)
Zigzag-encode (cx,cy) before pairing (the voxel_pos_to_id pattern); comment fixed.
4
med
Sea-ice band inverted (~85% ice at the −2 onset) + a dead depth==0 guard
Now monotonic: ice grows ~8% (onset) → ~92% (cold edge); leads layered only in the cold half via a 3× super-cell (super-cell index passed directly); dead guard removed.
Verdict: APPROVED (after resolution)
The critical fix restores the §3 spatial-coherence contract, and the rewritten test now actually fails against the broken implementation (verified: lead also confirmed the seed path is body/cluster-keyed, not per-voxel). cargo test passes, clippy -D warnings clean, fmt clean. Merging.
## Review: freeze-snow-model → main (type: code)
Reviewers: **Hoshe** (code quality) + **Tyre** (architecture). Read from the branch worktree. No merge-path surface.
### Tyre (Architecture): APPROVE
`SeasonalCover` correctly modeled as a D-228 cover overlay (orthogonal to TerrainMaterial), appended after the family generators; §3 fidelity solid across all three surfaces; Q-105 forward contract cleanly scoped (static mean-state only). Three clarity notes (dead onset branch, param naming, pack-ice quantization) — all folded into the fixes below.
### Hoshe (Code Quality): REQUEST_CHANGES → resolved
Caught a **critical** defect Tyre and the lead both missed.
### Findings & resolution (`88b43235`)
| # | Sev | Finding | Resolution |
|---|-----|---------|------------|
| 1 | **critical** | `cluster_scatter` was keyed on the **per-voxel** `sub_chunk_seed`, so voxels in the same 6 m cell got different scatter → salt-and-pepper, violating §3's "clustered patches, never per-tile dice" | Re-keyed on `(world, body, cluster-cell)`: new `SeedDomain::Cover = 10`; `cluster_scatter(world_seed, body_id, …)` derives `for_body(...).derive(Cover, cluster_pair_id)`. |
| 2 | **critical** | The coherence test passed a **constant seed** to `derive_cover`, bypassing the per-voxel variation — so it couldn't catch #1 (tautological) | Rewritten to the production `derive_cover` signature in a cold-lake scatter band. **It failed at 20% on the old code and passes at 100% now** — it genuinely gates the contract. |
| 3 | med | Cantor pairing cast negative cluster coords `as u64` directly ("no collision" claim false) | Zigzag-encode `(cx,cy)` before pairing (the `voxel_pos_to_id` pattern); comment fixed. |
| 4 | med | Sea-ice band inverted (~85% ice at the −2 onset) + a dead `depth==0` guard | Now monotonic: ice grows ~8% (onset) → ~92% (cold edge); leads layered only in the cold half via a 3× super-cell (super-cell index passed directly); dead guard removed. |
### Verdict: APPROVED (after resolution)
The critical fix restores the §3 spatial-coherence contract, and the rewritten test now actually fails against the broken implementation (verified: lead also confirmed the seed path is body/cluster-keyed, not per-voxel). `cargo test` passes, clippy `-D warnings` clean, fmt clean. Merging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
T-1030 — the static scattered freeze/snow model (D-239 §3). Adds a
SeasonalCover { None, Snow, Ice }overlay axis toVoxelColumn(D-228 — cover, not TerrainMaterial), derived after the family generator inderive_voxel_column(one site; the 8 family generators are untouched).The §3 model
div_euclid→ Cantor pair →splitmix64 ^ sub_chunk_seed, so all voxels in a cell share the freeze decision → ragged patches. Integer/hash only (D-010).classify_surfacefrommorphology_zone+ water):moisture_q < 30→ None, bare frozen ground).temperature_c == None) → coverNone.Deferred (Q-105 forward contract)
The transient/clock-bound layer (dawn frost that melts by noon → time-of-day passability shifts) requires Q-105's regional clock state and is not built — only the static mean-state
coverfield. Documented as a forward contract on the enum +derive_cover.Tests
16 new cover tests: real spatial-coherence (≥90% intra-cluster agreement + inter-cluster variety), band edges per surface (freshwater/salt/snow above/within/below), moisture gating, salt-vs-fresh distinction, airless→None, determinism.
Verification
cargo testpasses,clippy --all-targets -D warningsclean,fmtclean. D-010 integer throughout (temperature as i32, hash-based scatter — no f64/trig).Ticket: T-1030 (epic T-974). With T-1031 (the verification harness) this completes the epic.
🤖 Generated with Claude Code
D-239 §3 — the static mean-state freeze/snow cover. New SeasonalCover {None,Snow,Ice} overlay axis on VoxelColumn (D-228 cover, NOT TerrainMaterial), derived after the family generator in derive_voxel_column. - Spatially-COHERENT cluster scatter (never per-tile dice): coarse 6m cluster cells (div_euclid) -> Cantor pair -> splitmix64 ^ sub_chunk_seed, so voxels in a cell share the freeze decision -> ragged patches. All integer (D-010). - Bands by surface (classify_surface from morphology_zone + water): freshwater +5..-10C; salt water (OpenOcean) -2C onset / -14C, 3x-coarser sheets+leads; land snow +2..-10C, moisture-gated (moisture_q<30 -> None). Linear frozen fraction across the band; permanent below the band (poles/glaciers). - Airless (temperature_c None) -> cover None. - Transient/clock-bound layer (dawn frost melting by noon) doc'd as a Q-105 forward contract; only the static model is built. 16 new cover tests incl real spatial-coherence (>=90% intra-cluster agreement), band edges per surface, moisture gating, determinism. cargo test passes, clippy -D warnings clean, fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Review: freeze-snow-model → main (type: code)
Reviewers: Hoshe (code quality) + Tyre (architecture). Read from the branch worktree. No merge-path surface.
Tyre (Architecture): APPROVE
SeasonalCovercorrectly modeled as a D-228 cover overlay (orthogonal to TerrainMaterial), appended after the family generators; §3 fidelity solid across all three surfaces; Q-105 forward contract cleanly scoped (static mean-state only). Three clarity notes (dead onset branch, param naming, pack-ice quantization) — all folded into the fixes below.Hoshe (Code Quality): REQUEST_CHANGES → resolved
Caught a critical defect Tyre and the lead both missed.
Findings & resolution (
88b43235)cluster_scatterwas keyed on the per-voxelsub_chunk_seed, so voxels in the same 6 m cell got different scatter → salt-and-pepper, violating §3's "clustered patches, never per-tile dice"(world, body, cluster-cell): newSeedDomain::Cover = 10;cluster_scatter(world_seed, body_id, …)derivesfor_body(...).derive(Cover, cluster_pair_id).derive_cover, bypassing the per-voxel variation — so it couldn't catch #1 (tautological)derive_coversignature in a cold-lake scatter band. It failed at 20% on the old code and passes at 100% now — it genuinely gates the contract.as u64directly ("no collision" claim false)(cx,cy)before pairing (thevoxel_pos_to_idpattern); comment fixed.depth==0guardVerdict: APPROVED (after resolution)
The critical fix restores the §3 spatial-coherence contract, and the rewritten test now actually fails against the broken implementation (verified: lead also confirmed the seed path is body/cluster-keyed, not per-voxel).
cargo testpasses, clippy-D warningsclean, fmt clean. Merging.🤖 Generated with Claude Code
Pull request closed