hashWavContent walks RIFF chunks and feeds only fmt body + data payload
through FNV-1a ('W' domain-separation prefix), skipping bext/iXML/LIST that
REAPER embeds with per-render origination timestamps. Fallback to whole-file
hashBytes for non-WAV. Wired into both capture commit paths.
Legacy entries keep their stored whole-file hash; a re-capture will not
collapse onto a pre-fix entry -- a one-time clean capture resolves it.
contentHash was left empty on every capture; hashReferencedElsewhere returns
false for empty hashes, so every remove looked like a last reference. Add
FNV-1a hashBytes to capture_paths, wire into both commit paths. NOTE: this
activates index dedup-by-hash in production — a bit-identical re-capture now
collapses onto the existing entry instead of adding a duplicate (spec-intended).
classifyProjectTransition now checks the stored GUID first, then the pointer,
fixing the w10 regression where a recycled ReaProject* address stopped the bank
reloading. poll()'s fork re-GUID gate is bound to !sameProjectObject. Full
transition matrix pinned in tests.
M4's GUID-only classifier read a tab-switch between two Save-As forks (shared
copied GUID, different paths) as a Save-As and clobbered a bank. Thread
sameProjectObject into classifyProjectTransition: a different object always
Loads, never relocates; a forked sibling gets re-GUID'd to diverge.
Serialize BankIndex to project ext state ('reasampler'), reload on project
load, resolve paths project-relative. Save-As copies the bank to the new .rpp;
identity keyed off a minted GUID (not the recycled ReaProject*) so project
switches don't clobber banks. Pure classifyProjectTransition tested.
ICaptureBackend/CaptureRequest seam + OfflineRenderBackend driving snapshotted
RENDER_* with dither/normalize forced off for bit-identical output, RenderFailed
via filesystem check, and a pure capture_paths lib with tests. Spike action registered.