fix(memory): re-read and merge on save_to_disk to prevent concurrent data loss - #96195
Open
michielhdoteth wants to merge 1 commit into
Open
fix(memory): re-read and merge on save_to_disk to prevent concurrent data loss#96195michielhdoteth wants to merge 1 commit into
michielhdoteth wants to merge 1 commit into
Conversation
…data loss save_to_disk() previously wrote self._entries_for(target) without re-reading the file, so a session's stale in-memory snapshot could overwrite entries from concurrent writers (other sessions, daemons, cron jobs, scripts) — issue NousResearch#85858. The fix re-reads the file under the already-held lock and merges on-disk entries with pending entries before writing. This preserves writes from external writers that don't use the memory tool's lock (the primary gap) and adds defense-in-depth for concurrent sessions (beyond PR NousResearch#1726's file locking). Merge strategy: union-where-session-wins — all pending entries preserved, on-disk entries not in pending appended, no duplicates. Conflicts (same entry modified by both) result in both versions present — safe, no data loss. Closes NousResearch#85858 Co-Authored-By: Hermes Agent <noreply@nousresearch.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.
What does this PR do?
Fixes silent
MEMORY.mddata loss when two Hermes instances (or a Hermes instance + any external writer — daemon, cron, script) share one profile and write concurrently.save_to_disk()now re-reads the file under the already-held lock from #1726 and merges on-disk entries with the session's pending entries before writing. Last-writer-wins on content is eliminated; no entry from any writer is silently dropped.Closes #85858.
Related Issue
Fixes #85858
Type of Change
Changes Made
tools/memory_tool.py:387—save_to_disk()now re-reads the file under the already-held_file_lockand merges on-disk entries with the session's pending entries before writing. New_merge_disk_with_pending()method implements the merge (union-where-session-wins, deduplicated). +58 lines.tests/tools/test_memory_concurrent_85858.py— new file. 6 tests reproducing the concurrent-snapshot-staleness bug and verifying the fix:test_concurrent_adds_both_survive— direct reproduction of MEMORY.md lost under concurrent Hermes instances sharing one profile (frozen-snapshot + full-file rewrite = last-writer-wins) #85858test_concurrent_add_replace_both_survive— replacement entry survives stale savetest_concurrent_adds_no_duplicate— dedup on mergetest_external_append_preserved— daemon append without lock preservedtest_external_single_entry_preserved— external full-file replace preservedtest_multiple_external_entries_preserved— multiple external appends preservedtests/tools/test_memory_tool.py— updatedtest_mutations_read_the_file_exactly_oncedocstring/assertions to distinguish the drift-detection single-read invariant (still fixed) from the new intentional merge-read insave_to_disk. -1/+19 lines.Total: 3 files, +290/-6 lines, 47 tests pass, 0 regressions.
How to Test
gh pr checkout 96195pytest tests/ -q— all tests passpytest tests/tools/test_memory_concurrent_85858.py -v— 6/6 passtest_concurrent_adds_both_surviveencodes the exact reproduction from MEMORY.md lost under concurrent Hermes instances sharing one profile (frozen-snapshot + full-file rewrite = last-writer-wins) #85858 — session A adds X, session B adds Y, session A saves from stale snapshot. Before the fix this test fails (Y is lost); after the fix it passes (both X and Y survive).Checklist
Code
fix(memory):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
tools/memory_tool.py:11-14already claims._file_lockfrom fix(memory): concurrent writes silently drop entries — add file locking #1726 and stdlibdict.fromkeys)Screenshots / Logs
N/A — behavior change, not UI. Test output demonstrates the fix: