What's wrong
The #76 fix added _initialStateIsClean to SaveBoundaryManager, but the flag is not part of the saved state. UndoRedoService.RestoreFromState (Services/UndoRedoService.cs, ~lines 345-373) calls _saveBoundaryManager.Clear(), which resets the flag to true (Services/SaveBoundaryManager.cs, ~lines 516 and 604-608). Save boundaries are then recreated from the state, but when there are none the flag stays true.
So a loaded stack whose oldest commands were trimmed, and which has no save boundary, is dirty at position -1 but reports clean. That is exactly #76, and it comes back after every LoadStateAsync or RestoreFromState, including restoring a service from its own GetCurrentState().
Repro
MaxStackSize = 2. Execute 3 commands and Undo twice (position -1, which is after the first, trimmed command). Then either RestoreFromState(GetCurrentState()) into a new service, or round-trip through JsonUndoRedoSerializer.
before: pos=-1 v=1 HasUnsavedChanges=True
after restore: pos=-1 HasUnsavedChanges=False
after self-restore: pos=-1 HasUnsavedChanges=False
JSON round trip: loaded=True before=True after=False
Expected: HasUnsavedChanges stays True.
Reproduced against the current main build.
Why it matters
The app can close without prompting to save and lose the user's work. This is the scenario #76 fixed, and an app that persists its undo history across sessions hits it on every reload.
Suggested fix
Carry the "initial state is clean" flag through the saved state:
- Add a field to
UndoRedoStackState and to the serializer's SerializableStackState, defaulting to true so old data still loads.
- Add a way on
ISaveBoundaryManager to set the flag after Clear(), and call it from RestoreFromState.
If a format change is unwanted, a simpler fallback is to restore the flag as false whenever the saved state was dirty at -1.
Acceptance criteria
- The repro reports
HasUnsavedChanges == True after restore and after the JSON round trip.
- Data saved before the change still loads.
What's wrong
The #76 fix added
_initialStateIsCleantoSaveBoundaryManager, but the flag is not part of the saved state.UndoRedoService.RestoreFromState(Services/UndoRedoService.cs, ~lines 345-373) calls_saveBoundaryManager.Clear(), which resets the flag totrue(Services/SaveBoundaryManager.cs, ~lines 516 and 604-608). Save boundaries are then recreated from the state, but when there are none the flag staystrue.So a loaded stack whose oldest commands were trimmed, and which has no save boundary, is dirty at position -1 but reports clean. That is exactly #76, and it comes back after every
LoadStateAsyncorRestoreFromState, including restoring a service from its ownGetCurrentState().Repro
MaxStackSize = 2. Execute 3 commands and Undo twice (position -1, which is after the first, trimmed command). Then eitherRestoreFromState(GetCurrentState())into a new service, or round-trip throughJsonUndoRedoSerializer.Expected:
HasUnsavedChangesstaysTrue.Reproduced against the current
mainbuild.Why it matters
The app can close without prompting to save and lose the user's work. This is the scenario #76 fixed, and an app that persists its undo history across sessions hits it on every reload.
Suggested fix
Carry the "initial state is clean" flag through the saved state:
UndoRedoStackStateand to the serializer'sSerializableStackState, defaulting totrueso old data still loads.ISaveBoundaryManagerto set the flag afterClear(), and call it fromRestoreFromState.If a format change is unwanted, a simpler fallback is to restore the flag as
falsewhenever the saved state was dirty at -1.Acceptance criteria
HasUnsavedChanges == Trueafter restore and after the JSON round trip.