test(simulation): the conservation gate's monoculture check was a tautology (T-1213)
D-258 invariant 2 says descending the ladder must reveal COMPOSITION — a cell
reading forest must be able to contain the clearings and rock the vote
suppressed. One assertion stood behind that, and it read:
assert!(tally.len() > 1 || share == 1.0, ...)
A single-class tally has a 100% share by definition, so both branches are always
satisfiable: the check could never fail, including in the exact case its own
message names, "or nothing was composed". The invariant had a test and no gate.
Split into the two bounds the invariant actually has, because it is two-sided:
conservation caps how much may be invented (majority > 50%, already asserted) and
composition sets a floor on how little (minority >= 0.1%). Verified by raising
the floor to 2% and watching it fail on the measured 1.07%, then restoring it —
the floor is a tripwire for "did anything happen", deliberately far below the
measurement rather than tuned to it.
Measured at the descent ladder's own anchor on Ferrath:
conservation: majority class 3 at 98.9% across 2 classes {1: 175, 3: 16209}
So composition IS working in the data and conservation holds. The map is flat
anyway, and tooling/atlas-flatness (added here) says why the eye was not enough:
rung distinct lum p1-p99
Global 1581 145.69
Region 2923 33.59
District 53 13.72
Quarter 46 11.01
Region carries almost TWICE Global's distinct-colour count while holding a
quarter of its structure — the dither pass adds colour noise, not information, so
a colour-count metric would have called the flattest rung the richest. Structure
falls ~92% from Global to Quarter.
The cause is a channel mismatch rather than a missing generator: composition
perturbs moisture_q/slope_q, and the base map draws morphology hue x elev_q
lightness. The ladder scenarios pass no overlays deliberately, so the composed
fields are never rendered in the very shots that judge this work. Recorded on
T-1213 with the three ways forward; the choice touches D-258 and is Jeroen's.
The gate is still #[ignore]d — noted on the ticket as worth moving into a harness
that runs, since believability and window-derivation already load real bodies in
the normal cargo test path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,21 @@ pub const MOISTURE_COMPOSITION_CEILING_Q: i32 = 12;
|
||||
/// slope.
|
||||
pub const SLOPE_COMPOSITION_CEILING_Q: i32 = 6;
|
||||
|
||||
/// The floor on how much of a District patch the minority classes must hold for
|
||||
/// composition to count as having happened (D-258 invariant 2's lower bound).
|
||||
///
|
||||
/// Deliberately far below the measured value rather than tuned to it: at the
|
||||
/// descent ladder's own anchor on Ferrath the minority holds 1.07% (175 of
|
||||
/// 16,384 samples), and this floor is 0.1%. It is a "did anything happen at all"
|
||||
/// tripwire, not a quality bar — the bar for whether the map READS as composed
|
||||
/// is visual, and lives in the ladder captures rather than in an assertion.
|
||||
///
|
||||
/// One-sided on purpose. The upper bound (conservation — the majority must
|
||||
/// survive) is the invariant's other half and is asserted separately; a single
|
||||
/// combined check is what let the previous version collapse into a tautology.
|
||||
#[cfg(test)]
|
||||
const MIN_COMPOSED_MINORITY_SHARE: f64 = 0.001;
|
||||
|
||||
/// The fine-band composition field at a world position, in `[-1, 1]`.
|
||||
///
|
||||
/// Only [`VOXEL_OCTAVE_WAVELENGTHS_M`] (1,024–128 m) — the band that actually
|
||||
@@ -398,9 +413,34 @@ mod tests {
|
||||
tally
|
||||
);
|
||||
// ...and it must not be a monoculture either, or nothing was composed.
|
||||
//
|
||||
// This assertion used to read `tally.len() > 1 || share == 1.0`, which is
|
||||
// a TAUTOLOGY: a single-class tally has a 100% share by definition, so
|
||||
// both branches were always satisfiable and the check could never fail —
|
||||
// including in the exact case its own message describes, "nothing was
|
||||
// composed". It was the only thing standing behind D-258's invariant 2
|
||||
// (descending reveals COMPOSITION), and it was standing behind nothing.
|
||||
//
|
||||
// Both bounds are now asserted separately, because the invariant is
|
||||
// two-sided: conservation caps how much may be invented (above), and
|
||||
// composition sets a floor on how little (here). Measured at this anchor:
|
||||
// 2 classes, minority 175/16384 = 1.07%.
|
||||
assert!(
|
||||
tally.len() > 1 || share == 1.0,
|
||||
"tally {tally:?} — expected either composition or an honestly uniform patch"
|
||||
tally.len() > 1,
|
||||
"tally {tally:?} — a District patch resolved to ONE vegetation class. \
|
||||
D-258 invariant 2 says a summarised cell must be able to CONTAIN the \
|
||||
minority it suppressed; a monoculture here means the fine tier did \
|
||||
nothing at this scale, which is the flat map this work exists to fix."
|
||||
);
|
||||
let minority = total - count;
|
||||
let minority_share = minority as f64 / total as f64;
|
||||
assert!(
|
||||
minority_share >= MIN_COMPOSED_MINORITY_SHARE,
|
||||
"minority classes hold {:.2}% of the patch, below the {:.2}% floor — \
|
||||
inclusions this sparse are indistinguishable from none at map scale. \
|
||||
tally {tally:?}",
|
||||
100.0 * minority_share,
|
||||
100.0 * MIN_COMPOSED_MINORITY_SHARE,
|
||||
);
|
||||
eprintln!(
|
||||
"conservation: majority class {majority} at {:.1}% across {} classes {:?}",
|
||||
|
||||
Reference in New Issue
Block a user