From 7472ce41526e56d54d4b4528d8f749782d520c4d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 08:13:17 +0000 Subject: [PATCH] fix(bgz-tensor): resolve 5 CI-invisible test failures (3 stale tests + 1 slice-bounds bug) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bgz-tensor is excluded from CI (no workflow tests its manifest), so these reds accumulated unseen on main — surfaced by a local sweep of the CI-uncovered standalone crates. Diagnosed each as test-stale vs code-bug before fixing; did not blanket-bump assertions. Test-stale (code was correct, expected values drifted): - gamma_calibration: byte_size() is 48 (RoleGamma 36 + CosineGamma 12), not 40 — the struct has 8 roles (32B)+phi_scale(4B)=36B; the stale '28+12' comment assumed 7 roles. - hhtl_cache: serialize() writes a 16-byte gamma_meta trailer that the test's size formula omitted; added + 16. - hhtl_d: 0x3C00 is the IEEE-half (F16) bit pattern for 1.0; real BF16 1.0 is 0x3F80. bf16_to_f32 was correct; the test literal was wrong. Code-bug (genuine robustness fix, both encode + decode): - matryoshka encode_row/decode_row panicked when the SVD basis rank is lower than the band profile's nominal max (fewer sample rows than requested components → a band extends past the available coeffs). encode: clamp slice start (was only clamping end); decode: cap the per-band component count to the coeff buffer. Symmetric, so the byte stream stays in sync; roundtrip + quality assertions pass. Verified: bgz-tensor 200 passed / 0 failed (was 195/5); fmt clean; clippy -p bgz-tensor --all-targets -D warnings clean. --- crates/bgz-tensor/src/gamma_calibration.rs | 2 +- crates/bgz-tensor/src/hhtl_cache.rs | 4 ++-- crates/bgz-tensor/src/hhtl_d.rs | 2 +- crates/bgz-tensor/src/matryoshka.rs | 7 +++++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/bgz-tensor/src/gamma_calibration.rs b/crates/bgz-tensor/src/gamma_calibration.rs index 0561046e..aa4fc44f 100644 --- a/crates/bgz-tensor/src/gamma_calibration.rs +++ b/crates/bgz-tensor/src/gamma_calibration.rs @@ -432,7 +432,7 @@ mod tests { #[test] fn calibration_profile_size() { - assert_eq!(CalibrationProfile::byte_size(), 40); // 28 + 12 + assert_eq!(CalibrationProfile::byte_size(), 48); // RoleGamma 36 + CosineGamma 12 } #[test] diff --git a/crates/bgz-tensor/src/hhtl_cache.rs b/crates/bgz-tensor/src/hhtl_cache.rs index 3c4767ae..44e2705a 100644 --- a/crates/bgz-tensor/src/hhtl_cache.rs +++ b/crates/bgz-tensor/src/hhtl_cache.rs @@ -630,8 +630,8 @@ mod tests { cache.serialize(path).expect("serialize"); let size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0); - // 4 magic + 2 k + 256×34 entries + 256×256×2 distances + 256×256×1 routes + 256×4 radii - let expected = 4 + 2 + 256 * 34 + 256 * 256 * 2 + 256 * 256 + 256 * 4; + // 4 magic + 2 k + 256×34 entries + 256×256×2 distances + 256×256×1 routes + 256×4 radii + 16 gamma_meta + let expected = 4 + 2 + 256 * 34 + 256 * 256 * 2 + 256 * 256 + 256 * 4 + 16; assert_eq!( size, expected as u64, "expected {expected} bytes, got {size}" diff --git a/crates/bgz-tensor/src/hhtl_d.rs b/crates/bgz-tensor/src/hhtl_d.rs index 6a213513..5480d523 100644 --- a/crates/bgz-tensor/src/hhtl_d.rs +++ b/crates/bgz-tensor/src/hhtl_d.rs @@ -553,7 +553,7 @@ mod tests { #[test] fn hhtl_d_entry_roundtrip() { - let entry = HhtlDEntry::new(HeelBasin::Gate, 7, 42, true, 0x3C00); // BF16 1.0 + let entry = HhtlDEntry::new(HeelBasin::Gate, 7, 42, true, 0x3F80); // BF16 1.0 (0x3F80, not the F16 0x3C00) let bytes = entry.to_le_bytes(); let decoded = HhtlDEntry::from_le_bytes(&bytes); diff --git a/crates/bgz-tensor/src/matryoshka.rs b/crates/bgz-tensor/src/matryoshka.rs index 6c19bf89..1eea28a0 100644 --- a/crates/bgz-tensor/src/matryoshka.rs +++ b/crates/bgz-tensor/src/matryoshka.rs @@ -466,7 +466,7 @@ pub fn encode_row(row: &[f32], basis: &SvdBasis, profile: &BandProfile) -> Matry for band in &profile.bands { let max_val = band.precision.max_val(); - let band_coeffs = &coeffs[band.start..band.end.min(coeffs.len())]; + let band_coeffs = &coeffs[band.start.min(coeffs.len())..band.end.min(coeffs.len())]; // Find scale for this band let band_max = band_coeffs @@ -552,7 +552,10 @@ pub fn decode_row(encoded: &MatryoshkaRow, basis: &SvdBasis, profile: &BandProfi offset += 2; let inv_scale = band_max / max_val as f32; - let n = band.n_components(); + // Cap to the coeff buffer: the basis rank can be lower than the + // profile's nominal max (e.g. fewer sample rows than requested + // components), so a band may extend past the available coeffs. + let n = band.n_components().min(coeffs.len().saturating_sub(band.start)); match band.precision { BandPrecision::I16 => {