fix(pS-usage): fail-safe prune protection — in-wire owner nonce + sticky union poison, protect-all on zero identified, abort on unreadable record, rsusage_ prefix
This commit is contained in:
+31
-5
@@ -306,10 +306,17 @@ constexpr std::size_t kPruneListDisplayCap = 64;
|
||||
// * orphans — the FULL orphan set (owned ∩ present) − referenced, in enumeration
|
||||
// order, untruncated. The pure core decides; this only supplies inputs.
|
||||
// * sizeByRel — per-orphan-relative on-disk byte size (0 when it could not be stat'd).
|
||||
// * abortedUnreadableUsage — true iff a present rsusage_* instance-usage record could
|
||||
// not be read/decoded (pS-usage fail-safe): `orphans` is left EMPTY —
|
||||
// the prune must halt rather than proceed with degraded protection.
|
||||
// An empty orphan set is itself the delete-side guarantee (every
|
||||
// consumer of this scan deletes at most `orphans ∩ ...`), the flag is
|
||||
// what lets the action TELL the user instead of claiming "no orphans".
|
||||
struct PruneScan {
|
||||
std::string bankDirAbs;
|
||||
std::vector<std::string> orphans;
|
||||
std::unordered_map<std::string, std::uint64_t> sizeByRel;
|
||||
bool abortedUnreadableUsage = false;
|
||||
};
|
||||
|
||||
// Non-throwing: readActiveProject + resolveBankFile are pure/string; every filesystem
|
||||
@@ -362,14 +369,24 @@ PruneScan scanPruneOrphans(const BankBook& book, const OwnedFileManifest& owned)
|
||||
// referencedPaths() unions across the whole book (pool included); owned().paths() is
|
||||
// the manifest set. pS-usage: the referenced set additionally unions every LIVE
|
||||
// ReaSampler 9000 instance's held captures (usage_scan reads the per-instance
|
||||
// usage_* records + the live FX enumeration; sample_usage decides liveness) — a
|
||||
// capture any live instance holds can NEVER be an orphan, even when its bank entry
|
||||
// was deleted while the instance kept its ref. liveInstanceHeldPaths is READ-ONLY,
|
||||
// rsusage_* records + the live FX enumeration; sample_usage decides liveness,
|
||||
// including the protect-all net when zero instances were identified) — a capture
|
||||
// any live instance holds can NEVER be an orphan, even when its bank entry was
|
||||
// deleted while the instance kept its ref. liveInstanceHeldPaths is READ-ONLY,
|
||||
// preserving this scan's no-write contract. This shell only enumerates, resolves,
|
||||
// and stats.
|
||||
scan.bankDirAbs = bankDir;
|
||||
const UsageScanResult usage = liveInstanceHeldPaths(proj);
|
||||
if (usage.abortPrune) {
|
||||
// FAIL-SAFE ABORT: a present rsusage_* record could not be read/decoded, so the
|
||||
// protected set is unknowable. Compute NO orphans — every downstream consumer
|
||||
// (dry-run report, confirm set, fresh-recompute delete plan) then deletes
|
||||
// nothing. The flag surfaces the reason to the action's console message.
|
||||
scan.abortedUnreadableUsage = true;
|
||||
return scan;
|
||||
}
|
||||
scan.orphans = pruneOrphans(
|
||||
present, mergeReferenced(book.referencedPaths(), liveInstanceHeldPaths(proj)),
|
||||
present, mergeReferenced(book.referencedPaths(), usage.heldPaths),
|
||||
owned.paths());
|
||||
return scan;
|
||||
}
|
||||
@@ -380,7 +397,13 @@ PruneReport ReaSamplerSession::pruneDryRun() const {
|
||||
const PruneScan scan = scanPruneOrphans(book_, owned_);
|
||||
// buildPruneReport tallies count / byte-sum / display-truncation — no report logic
|
||||
// re-implemented here. An empty scan (no project / no folder) yields a zero report.
|
||||
return buildPruneReport(scan.orphans, scan.sizeByRel, kPruneListDisplayCap);
|
||||
PruneReport report =
|
||||
buildPruneReport(scan.orphans, scan.sizeByRel, kPruneListDisplayCap);
|
||||
// pS-usage fail-safe: surface the unreadable-record abort so the action halts with
|
||||
// an explicit message instead of reporting "no orphaned files" (the count IS zero —
|
||||
// the scan computed nothing — but the user must know the prune refused to run).
|
||||
report.abortedUnreadableUsage = scan.abortedUnreadableUsage;
|
||||
return report;
|
||||
}
|
||||
|
||||
std::vector<std::string> ReaSamplerSession::pruneOrphanSet() const {
|
||||
@@ -466,6 +489,9 @@ PruneDeletionResult ReaSamplerSession::pruneReclaim(
|
||||
// newly-appeared orphan not in `confirmed` is never swept without its own confirm.
|
||||
// Because freshOrphans is itself a pure-core output, the plan can contain NO referenced
|
||||
// and NO hand-dropped file — the R-C/R-D safety survives the recompute.
|
||||
// pS-usage: if THIS fresh scan hits an unreadable rsusage_* record it aborts with an
|
||||
// EMPTY orphan set, so the plan below intersects to empty and nothing is deleted —
|
||||
// the fail-safe holds even in the confirm→delete window, with no extra branch here.
|
||||
const PruneScan scan = scanPruneOrphans(book_, owned_);
|
||||
if (scan.bankDirAbs.empty()) return result; // no project / no folder -> nothing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user