Take the read-back back out of the persist; a write verdict belongs only where evidence crosses the plugin boundary
Its false gated six undo points, so an unverified byte-equality assumption could have silently removed Ctrl-Z for a bank mutation that landed.
This commit is contained in:
+18
-62
@@ -9,8 +9,8 @@
|
||||
// degrading to Failed rather than to Ok; the action lookup name's leading underscore and
|
||||
// its channel fork; the two key classifiers each end reads the shared key through; the
|
||||
// write-back verdict, driven by a modelled key store that accepts or drops the write; and
|
||||
// the persist/upgrade state machine that is the ONLY route to a Banked landing, including
|
||||
// the reachability of its Unpersisted limb at both persist outcomes.
|
||||
// the persist/upgrade state machine that is the ONLY route to a Banked landing, at both
|
||||
// persist outcomes.
|
||||
|
||||
#include "../src/core/wire/bake_wire.h"
|
||||
|
||||
@@ -33,8 +33,8 @@ static int g_fail = 0;
|
||||
// here and used by both blocks below that need it, so the test suite does not become a
|
||||
// third place the sentence lives.
|
||||
static const std::string kUnpersistedAnswer =
|
||||
"the bake reached the bank in memory, but this pass's persist did not confirm its bank "
|
||||
"write, so this answer cannot promise a reload will find it";
|
||||
"the bake reached the bank in memory, but this pass's persist did not report success, "
|
||||
"so this answer cannot promise a reload will find it";
|
||||
|
||||
int main() {
|
||||
// --- The exact bytes on the wire -------------------------------------------------
|
||||
@@ -554,8 +554,10 @@ int main() {
|
||||
// rather than a keyed verdict and an unkeyed reason the reader has to pair up.
|
||||
CHECK(ok.find("(added as a distinct capture)") != std::string::npos);
|
||||
// The sentence names the OBSERVATION behind the flag (see BakeLanding) and stops
|
||||
// there — it may not claim the .rpp on disk already holds the entry.
|
||||
CHECK(ok.find("read back as exactly what this pass wrote") != std::string::npos);
|
||||
// there — the write was ISSUED into a saved project. It may not claim REAPER took
|
||||
// the value, nor that the .rpp on disk already holds the entry.
|
||||
CHECK(ok.find("issued its bank write into the saved project") != std::string::npos);
|
||||
CHECK(ok.find("read back") == std::string::npos);
|
||||
CHECK(ok.find(".rpp") == std::string::npos);
|
||||
CHECK(ok.find("carries it") == std::string::npos);
|
||||
|
||||
@@ -585,11 +587,11 @@ int main() {
|
||||
unpersisted.landing = BakeLanding::Unpersisted;
|
||||
const std::string memoryOnly = describeBakeKey(key, unpersisted);
|
||||
CHECK(memoryOnly.find("IN MEMORY ONLY") != std::string::npos);
|
||||
CHECK(memoryOnly.find("persist did not confirm its bank write") != std::string::npos);
|
||||
CHECK(memoryOnly.find("persist did not report success") != std::string::npos);
|
||||
CHECK(memoryOnly != ok);
|
||||
// It may NOT claim what the project's saved state holds: the persist can fail
|
||||
// before writing anything or throw part-way, and a dedup hit's target may have
|
||||
// been in the project since long before this pass.
|
||||
// It may NOT claim what the project's saved state holds: the persist may never
|
||||
// have run at all, and a dedup hit's target may have been in the project since
|
||||
// long before this pass.
|
||||
CHECK(memoryOnly.find("does not carry it") == std::string::npos);
|
||||
|
||||
BakeKeyOutcome partial = landed;
|
||||
@@ -686,6 +688,10 @@ int main() {
|
||||
// to be answered Banked directly, on the grounds that it changed nothing — false
|
||||
// exactly when the entry it deduped against was one the SAME pass had just added and
|
||||
// then failed to persist, which answers Ok for an entry the project does not carry.
|
||||
//
|
||||
// Both limbs are live in the shell, not just in this table: it assigns the landing
|
||||
// inside a guarded scan and sets its `persisted` local only in the block after it, so a
|
||||
// throw between the two reaches Unpersisted with a real landing behind it.
|
||||
{
|
||||
// A dedup hit and a fresh add are INDISTINGUISHABLE here, by construction: the shell
|
||||
// assigns Unpersisted to both, so both need the same observation to be promoted.
|
||||
@@ -741,8 +747,8 @@ int main() {
|
||||
// Judging this by SetProjExtState's return made `writeFailed` unproducible on the
|
||||
// landing path and its sentence dead code (extStateWriteLanded owns why). The verdict is
|
||||
// the read-back, which a store that drops the write does produce. The store is modelled
|
||||
// here; the three shells bind these same two calls to SetProjExtState and the
|
||||
// grow-loop read.
|
||||
// here; the two shells that bind the verdict make these same two calls, to
|
||||
// SetProjExtState and the grow-loop read.
|
||||
{
|
||||
struct FakeKeyStore {
|
||||
std::map<std::string, std::string> values;
|
||||
@@ -837,56 +843,6 @@ int main() {
|
||||
CHECK(!extStateWriteLanded("", std::optional<std::string>("leftover")));
|
||||
}
|
||||
|
||||
// --- The Unpersisted limb is REACHABLE, through the same predicate ------------------
|
||||
// `persisted` used to be structurally true wherever a landing could observe it: the
|
||||
// session reported it from SetProjExtState's unread return, and by the time a landing
|
||||
// exists the only two conditions that return could reflect (no active project, an
|
||||
// unsaved one) are already excluded by the Land verdict. So the Unpersisted limb, the
|
||||
// wire answer for it and its console clause were all dead. The session now proves the
|
||||
// `banks` key the same way the landing proves its own — modelled below at both
|
||||
// outcomes, so neither branch is a constant.
|
||||
{
|
||||
const std::string banksJson = R"({"banks":[{"id":"pool","samples":[]}]})";
|
||||
|
||||
// The write REJECTED: the project still holds whatever it held before, which is a
|
||||
// book without the entry this pass just landed in memory.
|
||||
const std::optional<std::string> stale{R"({"banks":[]})"};
|
||||
const bool persistedAfterDrop = extStateWriteLanded(banksJson, stale);
|
||||
CHECK(!persistedAfterDrop);
|
||||
|
||||
BakeKeyOutcome entry;
|
||||
entry.verdict = BakeScanVerdict::Land;
|
||||
entry.landing =
|
||||
bakeLandingAfterPersist(BakeLanding::Unpersisted, persistedAfterDrop);
|
||||
entry.proof = BakeWriteProof::Confirmed; // the ANSWER wrote fine; the persist did not
|
||||
CHECK(entry.landing == BakeLanding::Unpersisted);
|
||||
const std::string line = describeBakeKey("rsbake_0123abcd", entry);
|
||||
CHECK(line.find("IN MEMORY ONLY") != std::string::npos);
|
||||
CHECK(line.find("The answer was written back") != std::string::npos);
|
||||
|
||||
// ...and that landing is answered as a FAILURE on the wire, so no instance adopts
|
||||
// an entry a reload would not find.
|
||||
BakeOutcome answered;
|
||||
answered.status = BakeStatus::Failed;
|
||||
answered.message = kUnpersistedAnswer;
|
||||
answered.generation = 1893456000;
|
||||
const auto back = decodeBakeOutcome(encodeBakeOutcome(answered));
|
||||
CHECK(back.has_value() && back->status == BakeStatus::Failed);
|
||||
CHECK(back.has_value() && back->message == kUnpersistedAnswer);
|
||||
|
||||
// The write ACCEPTED, same predicate, same inputs but for the read-back: the limb
|
||||
// above is a real branch, not a constant.
|
||||
const bool persistedAfterTake =
|
||||
extStateWriteLanded(banksJson, std::optional<std::string>(banksJson));
|
||||
CHECK(persistedAfterTake);
|
||||
CHECK(bakeLandingAfterPersist(BakeLanding::Unpersisted, persistedAfterTake) ==
|
||||
BakeLanding::Banked);
|
||||
// An unreadable read-back must NOT be folded in as an absence-and-therefore-a-clear:
|
||||
// the session routes Overflow to false before it ever reaches this predicate, and a
|
||||
// non-empty write against an absent key is false here regardless.
|
||||
CHECK(!extStateWriteLanded(banksJson, std::nullopt));
|
||||
}
|
||||
|
||||
// --- Every outcome bake_land actually emits survives the key round trip -------------
|
||||
// The landing writes these and the instrument reads them back; a field the encoder and
|
||||
// the decoder disagreed about would strand exactly the bake that produced it.
|
||||
|
||||
Reference in New Issue
Block a user