diff --git a/nexus/mnemosyne/__pycache__/entry.cpython-311.pyc b/nexus/mnemosyne/__pycache__/entry.cpython-311.pyc new file mode 100644 index 0000000..8e12425 Binary files /dev/null and b/nexus/mnemosyne/__pycache__/entry.cpython-311.pyc differ diff --git a/nexus/mnemosyne/entry.py b/nexus/mnemosyne/entry.py index a9e4be4..bcfc67d 100644 --- a/nexus/mnemosyne/entry.py +++ b/nexus/mnemosyne/entry.py @@ -6,6 +6,7 @@ with metadata, content, and links to related entries. from __future__ import annotations +import hashlib from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Optional @@ -24,8 +25,19 @@ class ArchiveEntry: topics: list[str] = field(default_factory=list) metadata: dict = field(default_factory=dict) created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) + updated_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) links: list[str] = field(default_factory=list) # IDs of related entries + @property + def content_hash(self) -> str: + """SHA-256 hash of title + content for dedup detection.""" + raw = f"{self.title}\x00{self.content}".encode() + return hashlib.sha256(raw).hexdigest() + + def touch(self): + """Bump updated_at to now.""" + self.updated_at = datetime.now(timezone.utc).isoformat() + def to_dict(self) -> dict: return { "id": self.id, @@ -36,9 +48,16 @@ class ArchiveEntry: "topics": self.topics, "metadata": self.metadata, "created_at": self.created_at, + "updated_at": self.updated_at, "links": self.links, + "content_hash": self.content_hash, } @classmethod def from_dict(cls, data: dict) -> ArchiveEntry: - return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__}) + # Strip non-field keys (like content_hash which is computed) + filtered = {k: v for k, v in data.items() if k in cls.__dataclass_fields__} + # Backfill updated_at for legacy entries that lack it + if "updated_at" not in filtered: + filtered["updated_at"] = filtered.get("created_at", datetime.now(timezone.utc).isoformat()) + return cls(**filtered)