···393393 io.sync(fd).map_err(RepoError::storage)?;
394394 }
395395396396- if !hint_exists && !scanned_entries.is_empty() {
396396+ if !scanned_entries.is_empty() {
397397 tracing::info!(
398398 file_id = %file_id,
399399 scanned = scanned_entries.len(),
400400- "rebuilding index from data file (no hint file, treating as restored backup)"
400400+ hint_exists,
401401+ "reindexing blocks past hint coverage"
401402 );
402403 let cursor = super::types::WriteCursor {
403404 file_id,
+21-8
crates/tranquil-store/src/gauntlet/invariants.rs
···77use jacquard_repo::mst::Mst;
8899use super::oracle::{Oracle, hex_short, try_cid_to_fixed};
1010-use crate::blockstore::{CidBytes, CompactionError, TranquilBlockStore, hash_to_cid_bytes};
1010+use crate::blockstore::{
1111+ BLOCK_HEADER_SIZE, CidBytes, CompactionError, TranquilBlockStore, hash_to_cid_bytes,
1212+};
1113use crate::eventlog::{EventSequence, SegmentId};
1214use crate::io::{RealIO, StorageIO};
1315···388390 let result = tokio::task::spawn_blocking(move || {
389391 let disk = store_c.list_data_files().map_err(|e| e.to_string())?;
390392 let liveness = store_c.compaction_liveness(0).map_err(|e| e.to_string())?;
393393+ let header = BLOCK_HEADER_SIZE as u64;
391394 let orphans: Vec<String> = disk
392395 .iter()
393396 .filter(|fid| !liveness.contains_key(fid))
394394- .map(|fid| format!("{fid}"))
397397+ .filter_map(|fid| {
398398+ let path = store_c.data_file_path(*fid);
399399+ let size = std::fs::metadata(&path).map(|m| m.len()).unwrap_or(0);
400400+ match size > header {
401401+ true => Some(format!("{fid} ({size} B)")),
402402+ false => None,
403403+ }
404404+ })
395405 .collect();
396406 Ok::<_, String>(orphans)
397407 })
···479489 tokio::task::spawn_blocking(move || {
480490 let listed = store.list_data_files().map_err(|e| e.to_string())?;
481491 let liveness = store.compaction_liveness(0).map_err(|e| e.to_string())?;
492492+ let header = BLOCK_HEADER_SIZE as u64;
482493483494 let mut violations: Vec<String> = Vec::new();
484495 listed.iter().for_each(|fid| {
···487498 Err(e) => violations.push(format!("{fid}: metadata {e}")),
488499 Ok(meta) => {
489500 let on_disk = meta.len();
501501+ let content = on_disk.saturating_sub(header);
490502 match liveness.get(fid) {
491491- None => violations.push(format!(
503503+ None if on_disk > header => violations.push(format!(
492504 "{fid}: listed on disk at {on_disk} B but not in index liveness"
493505 )),
494494- Some(info) if on_disk < info.total_bytes => {
506506+ None => {}
507507+ Some(info) if content < info.total_bytes => {
495508 violations.push(format!(
496496- "{fid}: on-disk {on_disk} B < index total_bytes {}",
509509+ "{fid}: on-disk {on_disk} B (content {content}) < index total_bytes {}",
497510 info.total_bytes
498511 ));
499512 }
500500- Some(info) if on_disk > info.total_bytes => {
513513+ Some(info) if content > info.total_bytes => {
501514 violations.push(format!(
502502- "{fid}: on-disk {on_disk} B > index total_bytes {}, {} B unaccounted",
515515+ "{fid}: on-disk {on_disk} B (content {content}) > index total_bytes {}, {} B unaccounted",
503516 info.total_bytes,
504504- on_disk - info.total_bytes
517517+ content - info.total_bytes
505518 ));
506519 }
507520 Some(_) => {}