From 5db651a543a7617a7eec6d08fc7e571d4842e056 Mon Sep 17 00:00:00 2001 From: shafdev <96260000+shafdev@users.noreply.github.com> Date: Tue, 14 Apr 2026 01:36:04 +0530 Subject: [PATCH] fix: use microsecond timestamp and full content hash in diary entry ID (#819) --- mempalace/mcp_server.py | 5 ++++- tests/test_mcp_server.py | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/mempalace/mcp_server.py b/mempalace/mcp_server.py index 4e21426..33933ff 100644 --- a/mempalace/mcp_server.py +++ b/mempalace/mcp_server.py @@ -836,7 +836,10 @@ def tool_diary_write(agent_name: str, entry: str, topic: str = "general"): return _no_palace() now = datetime.now() - entry_id = f"diary_{wing}_{now.strftime('%Y%m%d_%H%M%S')}_{hashlib.sha256(entry[:50].encode()).hexdigest()[:12]}" + entry_id = ( + f"diary_{wing}_{now.strftime('%Y%m%d_%H%M%S%f')}_" + f"{hashlib.sha256(entry.encode()).hexdigest()[:12]}" + ) _wal_log( "diary_write", diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index 4cc8b4a..a8189ae 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -6,6 +6,7 @@ dispatch layer (integration-level). Uses isolated palace + KG fixtures via monkeypatch to avoid touching real data. """ +from datetime import datetime import json import sys @@ -643,6 +644,48 @@ class TestDiaryTools: r = tool_diary_read(agent_name="Nobody") assert r["entries"] == [] + def test_diary_write_same_second_shared_prefix_no_collision( + self, monkeypatch, config, palace_path, kg + ): + _patch_mcp_server(monkeypatch, config, kg) + _client, _col = _get_collection(palace_path, create=True) + del _client + + from mempalace import mcp_server + + class FrozenDateTime: + calls = [ + datetime(2026, 4, 13, 22, 15, 30, 123456), + datetime(2026, 4, 13, 22, 15, 30, 123457), + ] + fallback = datetime(2026, 4, 13, 22, 15, 30, 123457) + + @classmethod + def now(cls): + if cls.calls: + return cls.calls.pop(0) + return cls.fallback + + monkeypatch.setattr(mcp_server, "datetime", FrozenDateTime) + + from mempalace.mcp_server import tool_diary_read, tool_diary_write + + entry1 = "A" * 50 + " entry one" + entry2 = "A" * 50 + " entry two" + + result1 = tool_diary_write(agent_name="TestAgent", entry=entry1, topic="status") + result2 = tool_diary_write(agent_name="TestAgent", entry=entry2, topic="status") + + assert result1["success"] is True + assert result2["success"] is True + assert result1["entry_id"] != result2["entry_id"] + + read_result = tool_diary_read(agent_name="TestAgent") + contents = [entry["content"] for entry in read_result["entries"]] + assert read_result["total"] == 2 + assert entry1 in contents + assert entry2 in contents + # ── Cache Invalidation (inode/mtime) ──────────────────────────────────