pS remediation: legacy lift terminates on stale-id proof; displayName in v10 refs; refs-reader range fallbacks; load path keeps owned refs

This commit is contained in:
2026-07-28 12:17:36 -04:00
parent 261a6affa5
commit cb89dbf5c4
6 changed files with 213 additions and 36 deletions
+21 -3
View File
@@ -168,6 +168,9 @@ public:
// a non-target instance neither applies nor advances its marker.
// * LEGACY LIFT: a pre-v10 blob restored with intent but no refs retries the (cheap)
// bank read until the blob is parseable, then reloads ONCE to copy the refs in.
// TERMINATING: once the blob parses and NO referenced id resolves, the ids are
// provably stale — the lift concludes permanently (legacyLiftShouldRun) instead of
// churning a full bank read + reload every tick forever.
// The consumed marker advances in component state (marked dirty via the host handler) so a
// re-open does not re-apply. `isFocusedTarget` is the shell's thundering-herd policy input
// (the editor passes true only for the instance whose editor is open — see the handoff).
@@ -286,6 +289,12 @@ private:
// the new params bake into the next real reload. Off the audio thread only.
void rebuildVoiceEngine();
// The pre-v10 LEGACY LIFT gate (#A): true when a lift attempt this tick could make
// progress. Latches legacyLiftConcluded_ on a Stale proof (see the member below); the
// pure decision itself is sample_map's legacyLiftDecision. Off the audio thread only
// (bridge read + bank parse).
bool legacyLiftShouldRun();
// Publish `built` (null = install silence) into live_: prune the graveyard by the last
// process()-published generation, swap `built` into live_, displace the previous live into
// the drain slot, and park the drain-evicted instrument in the graveyard. REQUIRES
@@ -383,11 +392,20 @@ private:
// FIRST poll after an editor open BASELINES the seen value without a redundant reload
// (setState already loaded the instrument from the OWNED refs); a subsequent generation
// CHANGE then drives the reload. Since pS there is NO reopen-heal here: playback never
// depends on this poll — a v10 blob plays from its own refs at setState time. The only
// poll-driven reload besides a generation change is the pre-v10 LEGACY LIFT (see
// pollBankSync). NOT read on the audio thread.
// depends on this poll — a v10 blob plays from its own refs at setState time. Besides a
// generation change, pollBankSync reloads only for an APPLIED S8 assignment and for the
// pre-v10 LEGACY LIFT. NOT read on the audio thread.
std::int64_t lastSeenBankGeneration_ = -1;
// The pre-v10 LEGACY LIFT's terminating latch (#A): set once legacyLiftShouldRun proves
// the referenced ids STALE against a readable bank blob (LegacyLiftDecision::Stale) —
// there is nothing to lift, so the lift stops re-firing (the steady state is one relaxed
// load per tick, no bank read). Reset by setState (a new blob = new facts). NOT consulted
// by the genChanged/applied reload paths, so a later bank change that re-introduces an id
// (e.g. an extension-side undo) still refreshes the refs — the latch only gates the lift.
// Atomic: written on the UI-timer thread (pollBankSync) and the host load thread (setState).
std::atomic<bool> legacyLiftConcluded_{false};
// S-VIEW-4 preview-trigger velocity (MIDI 1..127). Persisted in component state (v6) so the
// user's chosen strike velocity survives a project save/reload. Since Wave 2 the Sample-view
// velocity knob writes it on the UI thread, so it is guarded by previewMutex_; setState and