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:
@@ -26,11 +26,12 @@ is owned by other directories and only skinned here.
|
||||
one Ctrl-Z.
|
||||
- **The prune action is the ONLY file-deletion action in the system**; it opens no
|
||||
undo point (file deletion is not REAPER-undoable). It halts on
|
||||
`abortedUnreadableUsage` and prints the offending `rsusage_*` key names.
|
||||
`blockedByTracking` and prints whichever blockers fired — the malformed ledger,
|
||||
the offending `rsusage_*` key names, or both.
|
||||
|
||||
## Modules
|
||||
|
||||
- `shell/actions` (`action_registry` / `design_view_actions` / `bank_actions` / `prune_action`) — the bindable action families, all routed via the `command_id`/`gaccel`/`hookcommand` contract. `action_registry` owns the shared registration plumbing (interned channel-qualified id strings; register and mirror-unregister present the identical pointer) **and the Q-W6 registration TABLE**: `main.cpp`'s own family (capture scopes, panel toggle, insert, batch, realtime, recapture, version) is one `ActionTableRow` array — suffix, phrase, flat function-pointer handler — that registration, hookcommand dispatch, and the unload mirror-unregister all iterate, so adding an action touches the table only (OCP). Bank mutations flow through the promptless `shell/bank_ops` verbs (`bankOp*` + `persistBankOp`, taking `ReaSamplerSession&`), which the panel menus and `bank_actions` consume as thin UX skins. **Every bank index verb wraps its mutation in a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`) so one bank operation is one Ctrl-Z.** The prune action (`prune_action`, `BANK_PRUNE_FOLDER`) is **the ONLY file-deletion action in the system**; it opens no undo point (file deletion is not REAPER-undoable). **pS-usage:** `BANK_PRUNE_FOLDER` halts on `abortedUnreadableUsage` and prints the offending `rsusage_*` key names with clear instructions.
|
||||
- `shell/actions` (`action_registry` / `design_view_actions` / `bank_actions` / `prune_action`) — the bindable action families, all routed via the `command_id`/`gaccel`/`hookcommand` contract. `action_registry` owns the shared registration plumbing (interned channel-qualified id strings; register and mirror-unregister present the identical pointer) **and the Q-W6 registration TABLE**: `main.cpp`'s own family (capture scopes, panel toggle, insert, batch, realtime, recapture, version) is one `ActionTableRow` array — suffix, phrase, flat function-pointer handler — that registration, hookcommand dispatch, and the unload mirror-unregister all iterate, so adding an action touches the table only (OCP). Bank mutations flow through the promptless `shell/bank_ops` verbs (`bankOp*` + `persistBankOp`, taking `ReaSamplerSession&`), which the panel menus and `bank_actions` consume as thin UX skins. **Every bank index verb wraps its mutation in a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`) so one bank operation is one Ctrl-Z.** The prune action (`prune_action`, `BANK_PRUNE_FOLDER`) is **the ONLY file-deletion action in the system**; it opens no undo point (file deletion is not REAPER-undoable). `BANK_PRUNE_FOLDER` halts on `blockedByTracking` and prints each blocker that fired, with recovery instructions.
|
||||
- `drag_out_win` — OS drag-out shell: Windows OLE `DoDragDrop`/`CF_HDROP`, copy-only (`DROPEFFECT_MOVE` not offered); macOS/Linux via `SWELL_InitiateDragDropOfFileList`.
|
||||
- `instrument_drop_win` — FX-button drop shell: resolves a screen point to a track + FX-surface hotspot, then adds a ReaSampler 9000 instance and applies the dragged capture's state via a transient `.vstpreset` + `TrackFX_SetPreset` (the former `TrackFX_SetNamedConfigParm` "vst_chunk" write was silently unappliable for VST3). Exposes `loadInstrumentOntoTrack` (inner half, no own undo block) and `performInstrumentDrop` (wraps in its own undo block). **Never captures, never writes the bank, never inserts a timeline item.**
|
||||
- `ingest` — ingest-through-the-bank shell on the EXTENSION side: three surfaces — (1) arrange capture→bank→assign (bindable action), (2) Media-Explorer import→bank→instrument on the selected track, (3) file drop onto the bank panel→bank only. Only surface (1) writes the `assignment_request` ext-state wire. **ingest NEVER inserts a timeline item.**
|
||||
|
||||
@@ -273,9 +273,9 @@ ImportResult importFileIntoActiveBank(const std::string& absoluteSourcePath) {
|
||||
s.createdTimestamp = nowSec;
|
||||
|
||||
const AddResult r = book.activeIndex().add(s);
|
||||
// Record as owned regardless of outcome — the tool WROTE the file, so prune must
|
||||
// Record the birth regardless of outcome — the tool WROTE the file, so prune must
|
||||
// attribute it even in the narrow Collapsed race below.
|
||||
g_session->owned().add(paths.relativePath);
|
||||
g_session->recordCreated(s, tracking::OriginKind::Ingest);
|
||||
|
||||
switch (r) {
|
||||
case AddResult::Added:
|
||||
|
||||
@@ -23,20 +23,30 @@ namespace reasampler {
|
||||
void doBankPruneFolder(ReaSamplerSession& session) {
|
||||
const reclaim::PruneReport report = session.pruneDryRun();
|
||||
|
||||
// FAIL-SAFE: an unreadable instance-usage record makes the protected set
|
||||
// unknowable, so the prune HALTS outright rather than proceed with degraded
|
||||
// protection.
|
||||
if (report.abortedUnreadableUsage) {
|
||||
// FAIL-SAFE: tracking state the authority could not read makes the protected
|
||||
// set unknowable, so the prune HALTS outright rather than proceed with degraded
|
||||
// protection. Both blockers can fire at once; report each one that did.
|
||||
if (report.blockedByTracking) {
|
||||
std::string msg =
|
||||
"ReaSampler prune: ABORTED -- one or more instance usage records could not "
|
||||
"be read or decoded. Nothing was deleted.\n"
|
||||
"If the owning instance is still loaded it will republish its record on the "
|
||||
"next poll tick, clearing the abort. If the instance no longer exists (the "
|
||||
"key is an orphaned corrupt record), clear it manually via ReaScript:\n"
|
||||
" reaper.SetProjExtState(0, \"reasampler\", \"<key>\", \"\")\n"
|
||||
"Offending key(s):\n";
|
||||
for (const std::string& key : report.offendingUsageKeys) {
|
||||
msg += " " + key + "\n";
|
||||
"ReaSampler prune: ABORTED -- the file-tracking state could not be read. "
|
||||
"Nothing was deleted.\n";
|
||||
if (report.ledgerUnreadable) {
|
||||
msg += "The stored file-tracking ledger is malformed. It has been left "
|
||||
"intact rather than overwritten, so it can be repaired or cleared:\n"
|
||||
" reaper.SetProjExtState(0, \"reasampler\", \"owned_files\", \"\")\n"
|
||||
"Clearing it makes every existing bank file un-reclaimable (they stop "
|
||||
"being attributable to ReaSampler); no file is lost.\n";
|
||||
}
|
||||
if (!report.unreadableUsageKeys.empty()) {
|
||||
msg += "One or more instance usage records could not be read or decoded. "
|
||||
"If the owning instance is still loaded it will republish its record "
|
||||
"on the next poll tick, clearing the abort. If the instance no longer "
|
||||
"exists (the key is an orphaned corrupt record), clear it manually:\n"
|
||||
" reaper.SetProjExtState(0, \"reasampler\", \"<key>\", \"\")\n"
|
||||
"Offending key(s):\n";
|
||||
for (const std::string& key : report.unreadableUsageKeys) {
|
||||
msg += " " + key + "\n";
|
||||
}
|
||||
}
|
||||
ShowConsoleMsg(msg.c_str());
|
||||
return;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
// module. Registration/dispatch for its FOREVER-STABLE id (BANK_PRUNE_FOLDER) stay
|
||||
// with bank_actions; one guarded body here.
|
||||
//
|
||||
// Contract (preserve exactly): dry-run first; abort outright on unreadable usage
|
||||
// records (fail-safe); confirm-with-manifest before any deletion; opens NO undo
|
||||
// point and writes NO ext state (file deletion is not REAPER-undoable).
|
||||
// Contract (preserve exactly): dry-run first; abort outright when the tracking
|
||||
// authority reports a block (fail-safe); confirm-with-manifest before any deletion;
|
||||
// opens NO undo point and writes NO ext state (file deletion is not REAPER-undoable).
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user