deleteSlot method
Deletes slot id: removes its metadata AND every key namespaced to
it via keyFor (through StorageService.removeAllWithPrefix) — no
leftover keys from a deleted slot. If id was the activeSlotId,
that becomes null afterward (never left pointing at a slot that no
longer exists). Throws ArgumentError if id doesn't exist.
Implementation
Future<void> deleteSlot(String id) async {
_validateExists(id);
final storage = StorageService.to;
// BUG-87: data delete FIRST — if THIS throws, metadata/activeSlotId are
// untouched below, so a failed delete never orphans a slot's data (no
// metadata pointing at it, but the data itself still on disk). The old
// order removed metadata first, so a mid-delete crash/throw left data
// on disk with no metadata entry (and possibly activeSlotId still
// pointing at a slot that no longer "exists" per listSlots()).
final dataSnapshot = storage.exportWithPrefix('slot_${id}_');
await storage.removeAllWithPrefix('slot_${id}_');
// Snapshot in-memory metadata state BEFORE mutating it, so a failure to
// actually PERSIST the removal (verified below, not merely "didn't
// throw" — a swallowed error inside `_scheduleSave`'s fire-and-forget
// save chain would otherwise silently leave stale metadata on disk
// while this method returns as if fully committed) can roll everything
// — data, in-memory metadata, and the active-slot pointer — back to
// exactly the pre-delete state instead of leaving a partial delete
// behind.
final metaSnapshot = List<SaveSlotMeta>.of(_slotList);
final wasActive = activeSlotId == id;
_slotList.removeWhere((s) => s.id == id);
_scheduleSave();
await _waitForPendingSaves();
final persistedIds = (_store.load() ?? const <SaveSlotMeta>[])
.map((s) => s.id)
.toSet();
if (persistedIds.contains(id)) {
// Metadata removal never actually reached disk (the save chain's own
// `catch (_) {}` swallowed a real write failure) — restore data,
// in-memory metadata, and rollback the failed removal attempt with
// one more (best-effort) save, then surface the failure instead of
// returning as if the slot were gone.
if (dataSnapshot.isNotEmpty) {
await storage.importWithPrefix('slot_${id}_', dataSnapshot);
}
_slots = metaSnapshot;
_scheduleSave();
await _waitForPendingSaves();
throw StateError(
'deleteSlot: metadata removal for "$id" failed to persist; rolled '
'back',
);
}
if (wasActive) {
await storage.remove(_activeSlotStorageKey);
}
}