calculate_sha256 now reports "no hash" for an empty read. The checkpoint and
other-model lazy paths used to store that (previously the empty-string digest)
as a completed hash and index it, so an empty or truncated file would keep a
poisonous identity.
Both paths now leave hash_status="pending" for a later retry and skip the hash
index entirely. The repair flow also tolerates a failing cache update instead
of aborting a metadata fetch whose SHA256 was already repaired.