fix: classify future-version ledgers with changed record shape correctly, not as corrupt

Version-check now runs on the parseLedger failure path too, so a v3 blob whose record shape actually changed reports FutureVersion instead of Unreadable, avoiding the corrupt-blob "clear it" advice. Also closes the six minor findings.
This commit is contained in:
2026-07-30 20:25:46 -04:00
parent 45b87dc2ff
commit 6287534454
6 changed files with 67 additions and 22 deletions
+12 -2
View File
@@ -213,12 +213,16 @@ static void testUnreadableLedgerBlocksAndYieldsNoOrphans() {
// Both blockers at once must both be reported — the operator needs to fix both.
static void testBothBlockersReported() {
const OriginLedger empty;
// Deliberately NON-empty, like testUnreadableLedgerBlocksAndYieldsNoOrphans: an
// empty fixture asserts nothing about ownedPaths being withheld, only that it
// started empty.
OriginLedger populated;
populated.record(originOf("bank/would-be-orphan.wav", OriginKind::Capture, "S-1"));
const UsageFoldResult fold = foldLive(
{usage("rsusage_A", "{T1}", {UsageHold{"S-held", "bank/held.wav"}}),
unreadable("rsusage_BROKEN")},
{"{T1}"});
const TrackingState state{LedgerStatus::Unreadable, empty, fold};
const TrackingState state{LedgerStatus::Unreadable, populated, fold};
const ProtectionAnswer answer = pruneProtection(state);
CHECK(answer.blocked);
CHECK(answer.ledgerUnreadable);
@@ -227,6 +231,12 @@ static void testBothBlockersReported() {
// what prune protects, so withholding it would be the unsafe direction.
CHECK(contains(answer.heldPaths, "bank/held.wav"));
CHECK(answer.ownedPaths.empty());
// Belt-and-braces: the orphan set computed from this answer is empty even though
// the file is present, unreferenced, and recorded as owned.
const std::vector<std::string> orphans = reclaim::pruneOrphans(
{"bank/would-be-orphan.wav"}, {}, answer.ownedPaths);
CHECK(orphans.empty());
}
// A record that exists but whose track hosts no identified instance still protects