tracking: one ledger, one authority — prune protection and replace-vs-add answered from the same records, fail-safe on unreadable state
This commit is contained in:
@@ -20,9 +20,9 @@ enumeration and the actual deletion live in `shell/persist` (`prune_fs`), not he
|
||||
- Referenced-set is the union across ALL banks, pool included: a file is an orphan
|
||||
iff no bank in the book references it. This is the safety-critical computation —
|
||||
the prune null test is *prune never deletes a file that any index references.*
|
||||
- Orphan attribution is an owned-file manifest (fork R-D): the book tracks the set
|
||||
of files it has created; prune reclaims `(owned ∩ on-disk) − referenced`. This
|
||||
rejects folder-sweep (which would delete hand-dropped files).
|
||||
- Orphan attribution comes from the tracking ledger (`core/tracking`): the book
|
||||
tracks the files it has created; prune reclaims `(owned ∩ on-disk) − referenced`.
|
||||
This rejects folder-sweep (which would delete hand-dropped files).
|
||||
- **Prune null test:** a prune of a folder whose every file is referenced by some
|
||||
bank deletes nothing; a prune deletes exactly the `present − referenced` orphan
|
||||
set and nothing else.
|
||||
@@ -32,7 +32,7 @@ enumeration and the actual deletion live in `shell/persist` (`prune_fs`), not he
|
||||
|
||||
## Modules
|
||||
|
||||
- `prune_reconcile` — pure prune core: `pruneOrphans(present, referenced, owned)` computes `(owned ∩ present) − referenced`; the safety-critical "which files are orphans" decision, filesystem-free and hard-tested before any I/O exists. Gains `mergeReferenced(bankRefs, liveInstanceHeldPaths)` (pS-usage) — unions live instance holds into the prune referenced-set so the pure orphan computation includes them.
|
||||
- `prune_reconcile` — pure prune core: `pruneOrphans(present, referenced, owned)` computes `(owned ∩ present) − referenced`; the safety-critical "which files are orphans" decision, filesystem-free and hard-tested before any I/O exists. `mergeReferenced(bankRefs, heldPaths)` unions live instance holds into the referenced-set. Two of the three inputs (`owned`, and the held half of `referenced`) come from `core/tracking`'s authority, never assembled ad hoc by a shell. `PruneReport` carries the authority's verdict: `blockedByTracking` + `ledgerUnreadable` / `unreadableUsageKeys`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ std::vector<std::string> pruneOrphans(const std::vector<std::string>& present,
|
||||
const std::vector<std::string>& referenced,
|
||||
const std::vector<std::string>& owned) {
|
||||
// Exact-string membership — the model's canonical relative-path comparison
|
||||
// (Sample.relativePath / OwnedFileManifest::contains). std::string hashes/compares
|
||||
// (Sample.relativePath / OriginLedger::contains). std::string hashes/compares
|
||||
// byte-for-byte, so no normalization creeps in.
|
||||
const std::unordered_set<std::string> referencedSet(referenced.begin(),
|
||||
referenced.end());
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
// pool included (union across the whole book — see BankBook::
|
||||
// referencedPaths). A file referenced by any bank — including via a
|
||||
// COPY into a second bank — is NEVER an orphan (the prune null test).
|
||||
// * owned — the owned-file manifest: files the bank system itself created. A
|
||||
// present-but-unowned (hand-dropped) file is NEVER reclaimed.
|
||||
// * owned — the tracking ledger's paths: files the bank system itself created.
|
||||
// A present-but-unowned (hand-dropped) file is NEVER reclaimed.
|
||||
//
|
||||
// The three guardrails fall straight out of the set algebra:
|
||||
// * ∩ present — never proposes deleting a file that is not on disk (an owned-
|
||||
// but-absent manifest entry yields no orphan, no error).
|
||||
// but-absent ledger record yields no orphan, no error).
|
||||
// * ∩ owned — never a hand-dropped file (ownership attribution).
|
||||
// * − referenced — never a file any bank references (union safety, prune null test).
|
||||
//
|
||||
// Path representation: EXACT-STRING match everywhere — Sample.relativePath,
|
||||
// OwnedFileManifest::contains, BankModel all use raw std::string equality: no
|
||||
// OriginLedger::contains, BankModel all use raw std::string equality: no
|
||||
// separator normalization, no case-folding, no trailing-slash trimming. Feeding a
|
||||
// consistent spelling across the three inputs is the shell's contract (it enumerates
|
||||
// the folder, unions the book, and reads the manifest against the SAME resolved
|
||||
// the folder, unions the book, and reads the ledger against the SAME resolved
|
||||
// current folder). Diverging from exact match here (e.g. case-insensitive compare)
|
||||
// would be the unsafe direction — it could let one spelling of a referenced file be
|
||||
// treated as an orphan under another.
|
||||
@@ -48,23 +48,22 @@ namespace reasampler::reclaim {
|
||||
// order (deterministic). MAY be truncated for a large set (the
|
||||
// shell's display cap); `count` stays exact regardless.
|
||||
// * truncated — true iff `orphans` holds fewer than `count` entries.
|
||||
// * abortedUnreadableUsage — true iff the scan found a present-but-unreadable
|
||||
// rsusage_* instance-usage record: the orphan computation was NOT
|
||||
// performed (count 0, empty list) and the prune must HALT —
|
||||
// deleting with degraded protection is the data-loss direction.
|
||||
// Set by the scan shell, never by buildPruneReport (which stays a
|
||||
// pure tally).
|
||||
// * offendingUsageKeys — the exact "rsusage_<guid>" key names that triggered the
|
||||
// abort (non-empty iff abortedUnreadableUsage), so the operator
|
||||
// can clear each key via ReaScript:
|
||||
// reaper.SetProjExtState(0, "reasampler", "<key>", "")
|
||||
// * blockedByTracking — true iff the tracking authority could not answer the
|
||||
// protection question: the orphan computation was NOT performed
|
||||
// (count 0, empty list) and the prune must HALT — deleting with
|
||||
// degraded protection is the data-loss direction. Set by the scan
|
||||
// shell, never by buildPruneReport (which stays a pure tally).
|
||||
// * ledgerUnreadable / unreadableUsageKeys — which side blocked, so the action can
|
||||
// tell the operator what to recover. The key names are the exact
|
||||
// "rsusage_<guid>" spellings.
|
||||
struct PruneReport {
|
||||
std::size_t count = 0;
|
||||
std::uint64_t totalBytes = 0;
|
||||
std::vector<std::string> orphans;
|
||||
bool truncated = false;
|
||||
bool abortedUnreadableUsage = false;
|
||||
std::vector<std::string> offendingUsageKeys; // non-empty iff abortedUnreadableUsage
|
||||
bool blockedByTracking = false;
|
||||
bool ledgerUnreadable = false;
|
||||
std::vector<std::string> unreadableUsageKeys;
|
||||
};
|
||||
|
||||
// The outcome of an actual prune DELETION. The shell fills this as it deletes the
|
||||
@@ -91,7 +90,7 @@ struct PruneDeletionResult {
|
||||
//
|
||||
// Returns the subset of `present` that is BOTH owned AND unreferenced, in the order
|
||||
// they appear in `present` (deterministic — mirrors the insertion-order determinism
|
||||
// the index/manifest keep). Duplicate spellings within `present` are de-duplicated
|
||||
// the index/ledger keep). Duplicate spellings within `present` are de-duplicated
|
||||
// in the result (a folder enumeration yields distinct names, but the core does not
|
||||
// rely on that).
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user