fix(frontend): keep unsaved memory edits when a memory is added - #261
Merged
Conversation
Editing a memory row and then adding a new memory reverted the edit, with no warning and nothing to undo it. The draft list is re-seeded from the server whenever the server's answer changes. That effect exists for a reason -- #241 added it so a row the server normalized away (trimmed to nothing, a case-insensitive duplicate) stops sitting on screen looking saved -- but it re-seeded wholesale. Adding a memory also changes the server's answer, so every other row the user had edited and not yet saved was silently overwritten with the server's copy. Its own comment claimed the opposite ("leaves in-progress edits alone between saves"); that only held while nothing else changed the list. The effect now reconciles instead of replacing. Each row remembers the server value it was seeded from, and a server list that has changed carries every surviving row's in-progress value across by matching on that origin, building a fresh row only for genuinely new entries. Matching on the origin rather than the current value is the point: an edited row no longer equals its server value, which is exactly the case being preserved. Both behaviours are now pinned, including one test that exercises them together -- a normalized-away row is dropped in the same update that an edit to a surviving row is preserved -- so the fix cannot be satisfied by simply preferring the draft over the server or the reverse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
Edit a memory row, then add a new memory, and the edit reverts — silently, with nothing to undo it.
Root cause
The draft list is re-seeded from the server whenever the server's answer changes:
That effect exists for a good reason — #241 added it so a row the server normalized away (trimmed to nothing, a case-insensitive duplicate) stops sitting on screen looking saved. The mistake is that it replaces rather than reconciles.
Adding a memory also changes the server's answer. So the add succeeds,
memoschanges, the effect fires, and every other row the user had edited and not yet saved is overwritten with the server's copy.The comment above it claimed the opposite:
That only held while nothing else changed the list — which an add always does.
The fix
Each row now remembers the server value it was seeded from, and a changed server list carries every surviving row's in-progress value across by matching on that origin:
Matching on origin rather than current value is the whole point: an edited row no longer equals its server value, and that is precisely the row being preserved.
shift()so duplicate server values claim distinct rows rather than aliasing onto one.Both behaviours are pinned, including together
The risk with a fix like this is satisfying it by simply preferring the draft — which would put #241's bug straight back. So one of the new tests exercises both directions in the same update: a normalized-away row is dropped at the same moment an edit to a surviving row is kept.
drops rows the server normalized away(existing, #241)Both new tests fail against the unfixed component; the existing #241 test passes throughout.
npm test -- --runnpm run typechecknpm run lintpython -m pytest -qCompatibility and rollback
One component's local state. No API contract, stored data, or migration. A list that has not changed still skips the effect entirely, exactly as before. Reverting the commit restores the previous behaviour exactly.
Limits
A row the user deleted locally but has not saved is still restored when the server list changes, since the server still has it. That is pre-existing and arguably correct — the deletion is not committed until "Save changes" — but it is the same class of question as the edit case, and worth revisiting if it bites.
Reconciliation matches on value, so if the server reorders two memories the rows follow the server's order rather than the screen's. With a set-like list whose order comes from the server, that is the intended outcome.
🤖 Generated with Claude Code