fix(mqtt): undo a failed /mqtt.json publish instead of claiming a rollback

Publishing moves the old primary to .bak before the verified temp takes
that name, so a failed second rename left the new image exactly where
boot recovery promotes it — while the observer setter told the operator
the change had been rolled back. The refused value came back at the next
reset.

Restore the backup and discard the temp on that path, and distinguish
CommitIndeterminate from CommitFailed when the filesystem cannot be put
back, so the CLI reply says the flash state is unresolved rather than
claiming the change is gone. The indeterminate condition latches for the
boot: the artifact left behind also makes every later transaction fail to
begin, so it cannot clear itself.

Also state the version-first rule the future-version probe depends on.
The probe reads the root version with this firmware's grammar, so a newer
file that introduces unknown syntax ahead of that field reads as corrupt
rather than future and loses its preservation guarantee.

Tests: publish-failure rollback and the indeterminate outcome against a
SPIFFS-shaped store fake; the version-first writer invariant and the cost
of violating it; /prefs.json coverage for the strict shape checks
(deployed-shape file, unknown nested groups, torn files, mismatches).
This commit is contained in:
agessaman committed 2026-08-15 15:09:55 -07:00
1 parent 71847b3c92
commit dec4a995ea
9 files changed
+469 -49

No files matched your search

+7 -1
View File
@@ -208,7 +208,13 @@ bool CommonCLI::persistObserverPrefs(char* reply) {
if (_observer_prefs_rollback != nullptr) {
memcpy(&_mqtt_prefs, _observer_prefs_rollback, sizeof(_mqtt_prefs));
}
strcpy(reply, "Error: setting not persisted; change rolled back");
// The running node is back on the old value either way, but only claim the
// change is gone when the write really was undone on flash.
if (_observer_save_indeterminate) {
strcpy(reply, "Error: setting not persisted; flash state unresolved, recheck after reboot");
} else {
strcpy(reply, "Error: setting not persisted; change rolled back");
}
return false;
#else
(void)reply;