fix(capture): populate contentHash on captured samples to fix always-prompting remove
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).
This commit is contained in:
+27
-3
@@ -38,6 +38,7 @@
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -224,6 +225,21 @@ std::string makeUniqueTag() {
|
||||
return std::to_string(static_cast<long long>(now));
|
||||
}
|
||||
|
||||
// Reads the whole file into a byte buffer. Returns an empty vector on any I/O
|
||||
// failure (the caller then leaves contentHash empty — the safe, confirm-eliciting
|
||||
// direction for an unreadable file).
|
||||
std::vector<std::uint8_t> readFileBytes(const std::string& path) {
|
||||
std::ifstream f(path, std::ios::binary | std::ios::ate);
|
||||
if (!f) return {};
|
||||
const std::streamoff size = f.tellg();
|
||||
if (size <= 0) return {};
|
||||
std::vector<std::uint8_t> bytes(static_cast<std::size_t>(size));
|
||||
f.seekg(0);
|
||||
f.read(reinterpret_cast<char*>(bytes.data()), size);
|
||||
if (!f) return {};
|
||||
return bytes;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
@@ -483,9 +499,17 @@ CaptureResult OfflineRenderBackend::capture(const CaptureRequest& request) {
|
||||
s.lengthSeconds = request.endSeconds - request.startSeconds;
|
||||
s.captureTempo = Master_GetTempo(); // BPM at capture time (verified ~4651)
|
||||
s.tier = Tier::Scratch; // captures land in scratch by default
|
||||
// contentHash left empty for M3: hashing the rendered file is a peaks/M2-
|
||||
// adjacent concern wired in a later milestone. Empty hashes do NOT dedup, so
|
||||
// this is safe (bank_model treats "" as non-participating).
|
||||
// Content hash: FNV-1a over the rendered file bytes so hashReferencedElsewhere
|
||||
// can identify copies in other banks and suppress the last-reference confirm when
|
||||
// another bank still holds the same file. Best-effort: an unreadable file leaves
|
||||
// contentHash empty — the safe, confirm-eliciting direction (bank_model treats
|
||||
// "" as non-participating in dedup, which is the existing fallback semantics).
|
||||
{
|
||||
const std::vector<std::uint8_t> fileBytes = readFileBytes(expectedPath);
|
||||
if (!fileBytes.empty()) {
|
||||
s.contentHash = hashBytes(fileBytes.data(), fileBytes.size());
|
||||
}
|
||||
}
|
||||
s.createdTimestamp = static_cast<std::int64_t>(std::time(nullptr));
|
||||
|
||||
result.status = CaptureStatus::Ok;
|
||||
|
||||
Reference in New Issue
Block a user