From d7343d1be21e030f5ba441675d127bfa9e12e5af Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Sat, 11 Apr 2026 19:37:39 -0400 Subject: [PATCH] wip: add updated_at, content_hash, touch() to ArchiveEntry --- .../__pycache__/entry.cpython-311.pyc | Bin 0 -> 4494 bytes nexus/mnemosyne/entry.py | 21 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 nexus/mnemosyne/__pycache__/entry.cpython-311.pyc diff --git a/nexus/mnemosyne/__pycache__/entry.cpython-311.pyc b/nexus/mnemosyne/__pycache__/entry.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e12425af2da70ae529b9742c7f105c48c37d46e GIT binary patch literal 4494 zcmc&%-ESLN6~8kcf5dj|=EF(qrtNH+(ha0eNW7(jCZydhEeKMUbfs#Nm6>|R@uXvq z_s+O&B9}E%B(hSqibPP0wrInu^g{))NUg*lu=@s*r?OOQ5K;u<0iL`H52#O^Ghjq*Rp%Q;P7&dZ5l z@VuDLHVhP<7x_F`@k*XIvNE65RBe%pg{+e1?K}Us@&?!x6ge&PT87WbVqVEF25%@v z78-^qiH11Jr?tER-yG$|yu_4MofA zvm>n4fxV8pWwG!{zfZcw*rk;hu!M1TM#5E z1n-5hg?-DRJw1n|POyl$7M*)6B2pAA4m2&Ku6@QAwZGc!+Vt#WbHKLgZQATJmM(iN zF|?4or9r8GmD@$29%*2e2))u_>EJ3S#N#Bjqyh|vfuJRnQ2V1Ii^=g4DbcEn?Obc0%4 zv8YIv7cu#mY-2AkN5_m@VJz2N4JU7C^x9ZnzFE|Flw zooK3ip-Ua1On&#~Gwx~d58i^zh-pKk%d`{p77Ifo zme)`WRkr+%`G^o=JPOw@a(ad`ru}H^4bmRx22+y zwmKADgJhHw4SE2Swu5LlN<9Ft@bMvdw0#7iOrBkQ`u)!bJ{vTXv(@D6i{zE($tz}3 zs3wJmx2<5B%9wFdqVa!s0zHZ|`|=YVMDt?^NE|CzEMNk}saRe+qgp;H8%3H=yI})6 z3>N&D?F|s_kP=yfyHh@b><4Yv`Xlly&!)SKnpSvk58^Gxz?e14k{k9Cvx^Xo&97S??%a5I?%2ik^SrIz5I&m*VI$mwe2^qS|d(Z1V1xtlhl zL)GZei|FX{=%^V@RHKPXAYq4b|5z2^3;*k-SRyGmJ`!Wcq5oVlSKxPeg(GsCd{t+L#H~O2K_e*9x zS&b)O#HXLfr_K0GH9oV(-Hxuowbx6SKx{_e2BDqNt!xI_jTi^ z9Gf;S!$BWt(Zd1HDWJEHeb|zidm;4T?BfuPL3@Xm^dL6l2$-noVT54>^cy{a@O6Y! z2$KMr&u(!LxGu%*s*0tD`cHA(w_Sin6_VaQH z#^W52J|GgOkP>^J`4E?cceZVPB5z-FRoC%LJkOWy?DR5t z21$_Ca)s%YueLEEL$#dE1vqkOX;Iau6Fauwf=={Nfd7<9rDx8mzr1|+?M>#>xBqyo z`pq9z-oUzkcKtm^0<`bj!0S8?Q_SBOdx~eC0HN704*6AyZk> zF?c_2CfW{n#4COK&jjDr4FDK5U?ftd)*RLQ}0!QrGkFfN0_`ZD(Ys$F!j&SzWD9VlnCV-IvE09X%#hjc)k+pV`u%f9{%ikP$Km(NwWX{VapbgiqWe7<}Mub*D3^~A~-DqPH|CKs!9_m;m?;d-5Fa=kTv zxct2eH|$iC8?Fr;zUzG?RR$)_fywH?B$`h;)#N5?vH1EskCK(xxEUL-#>UGRE8MtK zO>Vr_cW7O%j0#x$=FPtOYTrEeKJQeMoBt9P{e$bpN0%%86K4NJwSS^~zQRp7)#N6& zyq$H{;~lG$CVb%`zR2noM;m0lXPG)_!9b@Ma%wvqA-=9U>+>G1lO|vy$&aO)SiP&WYM*EEnL97sZT8BM5e|+X(>w zU>v?q(;tEf=3@OBK;6SIOr7Nzu1*lbggI6rL#=g-M9TJ6Bd=HXt2J_>vR|!{LzVVg iBcZZ=!Fp#ybvD6(oA$yNsQWhpxQ-329^7SVyY9a;oB?nE literal 0 HcmV?d00001 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)