AbstractMemory 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import threading
4
4
  import uuid
5
+ from dataclasses import replace
5
6
  from typing import Any, Iterable, List, Optional, Sequence
6
7
 
7
8
  from .embedding_pin import (
@@ -112,6 +113,28 @@ class InMemoryTripleStore:
112
113
  self._embedder = embedder
113
114
  return len(clean)
114
115
 
116
+ def close_assertion(self, assertion_id: str, valid_until: str) -> bool:
117
+ """Close one assertion's validity window in place (SQLite parity):
118
+ sets `valid_until` only while it is still open; everything else is
119
+ untouched. True when a row changed; False for an unknown id or an
120
+ already-closed window."""
121
+ rid = str(assertion_id or "").strip()
122
+ until = str(valid_until or "").strip()
123
+ if not rid:
124
+ raise ValueError("close_assertion requires an assertion_id")
125
+ if not until:
126
+ raise ValueError("close_assertion requires a valid_until timestamp")
127
+ with self._lock:
128
+ for row in self._rows:
129
+ if row.get("assertion_id") != rid:
130
+ continue
131
+ a = row.get("assertion")
132
+ if not isinstance(a, TripleAssertion) or a.valid_until is not None:
133
+ return False
134
+ row["assertion"] = replace(a, valid_until=until)
135
+ return True
136
+ return False
137
+
115
138
  def close(self) -> None:
116
139
  return None
117
140
 
abstractmemory/journal.py CHANGED
@@ -221,12 +221,22 @@ class ScopeBinding:
221
221
  return _jsonify(asdict(self))
222
222
 
223
223
 
224
+ #: The closure kinds a ClosureRecord accepts (``reinterpreted`` is additive, 0.3.2).
225
+ CLOSURE_KINDS = frozenset({"retract", "supersede", "reinterpreted"})
226
+
227
+
224
228
  @dataclass(frozen=True)
225
229
  class ClosureRecord:
226
- """Append-only belief-lifecycle record (backlog 0010): retract or supersede."""
230
+ """Append-only belief-lifecycle record (backlog 0010): retract, supersede or reinterpreted.
231
+
232
+ ``reinterpreted`` (additive, 0.3.2): the writer no longer reads its source this way (AbstractNote ADR 0008: a
233
+ note understood again against an earlier foundation withdraws its previous reading). It is neither a
234
+ correction of the fact (``retract``) nor a later fact replacing it (``supersede``); like both it leaves
235
+ ranked retrieval. ``replacement_ids`` is optional (the new reading's assertions, when there are any).
236
+ """
227
237
 
228
238
  assertion_id: str
229
- kind: str # retract | supersede
239
+ kind: str # retract | supersede | reinterpreted
230
240
  reason: str
231
241
  replacement_ids: Tuple[str, ...] = ()
232
242
  observed_at: str = field(default_factory=utc_now_iso)
@@ -240,8 +250,8 @@ class ClosureRecord:
240
250
  raise ValueError("ClosureRecord requires assertion_id")
241
251
  object.__setattr__(self, "assertion_id", self.assertion_id.strip())
242
252
  kind = str(self.kind or "").strip().lower()
243
- if kind not in {"retract", "supersede"}:
244
- raise ValueError("ClosureRecord.kind must be 'retract' or 'supersede'")
253
+ if kind not in CLOSURE_KINDS:
254
+ raise ValueError("ClosureRecord.kind must be 'retract', 'supersede' or 'reinterpreted'")
245
255
  object.__setattr__(self, "kind", kind)
246
256
  if not (isinstance(self.reason, str) and self.reason.strip()):
247
257
  raise ValueError("ClosureRecord requires a non-empty reason")
@@ -51,7 +51,8 @@ class SQLiteTripleStore:
51
51
 
52
52
  Notes:
53
53
  - Uses stdlib `sqlite3` (portable; no daemon).
54
- - Append-only: there is no update/delete API (see AbstractMemory FAQ).
54
+ - Append-only: there is no update/delete API (see AbstractMemory FAQ),
55
+ except `close_assertion`, which only closes an open validity window.
55
56
  - VECTORS (a2a 0003, the entity-home pairing): pass `embedder=` and each
56
57
  added assertion's canonical text is embedded and persisted in the SAME
57
58
  .sqlite3 file (JSON-encoded `embedding` column; in-place ALTER TABLE
@@ -526,6 +527,39 @@ class SQLiteTripleStore:
526
527
  rows = self._conn.cursor().execute(sql, params).fetchall()
527
528
  return [self._row_to_assertion(r) for r in rows]
528
529
 
530
+ def close_assertion(self, assertion_id: str, valid_until: str) -> bool:
531
+ """Close one assertion's validity window IN PLACE (additive, backlog:
532
+ abstractnote supersession). Sets `valid_until` only while it is still
533
+ open (NULL); every other column — terms, provenance, attributes, the
534
+ FTS row, the vector — is untouched, so the store stays append-only
535
+ for content. Returns True when a row changed, False when the id is
536
+ unknown or the window was already closed (idempotent replays).
537
+
538
+ Why: a later fact superseding an earlier one must make
539
+ `TripleQuery(active_at=...)` agree with the writer's belief window;
540
+ journal closures alone only reach ranked retrieval, never `query`."""
541
+ rid = str(assertion_id or "").strip()
542
+ until = str(valid_until or "").strip()
543
+ if not rid:
544
+ raise ValueError("close_assertion requires an assertion_id")
545
+ if not until:
546
+ raise ValueError("close_assertion requires a valid_until timestamp")
547
+ with self._lock:
548
+ cur = self._conn.cursor()
549
+ cur.execute("BEGIN IMMEDIATE")
550
+ try:
551
+ cur.execute(
552
+ f"UPDATE {self._table} SET valid_until = ? "
553
+ "WHERE assertion_id = ? AND valid_until IS NULL",
554
+ (until, rid),
555
+ )
556
+ changed = int(cur.rowcount or 0) > 0
557
+ cur.execute("COMMIT")
558
+ except BaseException:
559
+ cur.execute("ROLLBACK")
560
+ raise
561
+ return changed
562
+
529
563
  def replace_vectors(
530
564
  self, vectors: dict, pin: dict, *,
531
565
  expected_row_count: int, embedder: Optional[TextEmbedder] = None,
abstractmemory/system.py CHANGED
@@ -853,7 +853,8 @@ class MemorySystem(ValenceOps, AccessOps):
853
853
  replacement_ids: Sequence[str] = (),
854
854
  reason: str,
855
855
  ) -> List[str]:
856
- """Append one ClosureRecord per assertion (retract | supersede);
856
+ """Append one ClosureRecord per assertion (retract | supersede |
857
+ reinterpreted);
857
858
  returns closure ids. Supersede requires replacement_ids; reason is
858
859
  mandatory — validated by ClosureRecord BEFORE any append. A multi-
859
860
  assertion supersede shares one replacement set. Accepts either id
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: AbstractMemory
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: AbstractMemory: temporal, provenance-aware semantic memory primitives for AbstractFramework.
5
5
  Project-URL: AbstractFramework (monorepo), https://github.com/lpalbou/abstractframework
6
6
  Author: Laurent-Philippe Albou
@@ -164,7 +164,7 @@ system.commit_selection(result.trace_id, [h.record_id for h in result.handles[:2
164
164
 
165
165
  ## Design principles
166
166
 
167
- - **Append-only, no deletion**: updates are new assertions; belief revision is closure records (retract/supersede); forgetting is decay of retrieval strength plus closures and silencing — the substrate is lossless.
167
+ - **Append-only, no deletion**: updates are new assertions; belief revision is closure records (retract/supersede/reinterpreted); forgetting is decay of retrieval strength plus closures and silencing — the substrate is lossless.
168
168
  - **Reads are pure**: reconstruction, inspection, replay, and the entity card deposit nothing. `commit_selection` is the only strengthening path.
169
169
  - **One seq axis**: the journal assigns a monotonic `seq` to every record; any past state is reproducible by anchoring reads at `as_of`.
170
170
  - **Works-or-loud**: degraded paths are labeled `#FALLBACK` in result warnings; invalid inputs raise actionable errors instead of silently meaning something else.
@@ -25,8 +25,8 @@ abstractmemory/feelings_reads.py,sha256=eCnLLEnJwOw6NvWss2P4OXpitsDMieyPT5loDfiI
25
25
  abstractmemory/folds.py,sha256=DPy8po7k0Hp8HdEwbXwsS376mCYiECPk1-Ct2l_CNJg,17004
26
26
  abstractmemory/gradation.py,sha256=iee4w4hoEmGw72BN7XeoWru2-AF3viPsCrAY_8k3sVk,11850
27
27
  abstractmemory/identity_review.py,sha256=6PajkCICR4JXiG53T2EeNQuJitMnefxLni2x0zI6Ai8,7434
28
- abstractmemory/in_memory_store.py,sha256=5Yh2xcriQYjr0fDwRkfkdDZmD1NUph8a4XiESzb_OpM,12173
29
- abstractmemory/journal.py,sha256=C4FCE-rHLBi759cniTOFqD9fkPIsTSUd30H0TctKWe0,23525
28
+ abstractmemory/in_memory_store.py,sha256=hbJhfQonNYXI3ywLbuFU9XRNHuxG0fb3PXWrYeDVR58,13241
29
+ abstractmemory/journal.py,sha256=Xe6vxGFfkuN-8wkWOX9vF_spr9q41jUu5I9Wo4Bv-Rg,24157
30
30
  abstractmemory/journal_common.py,sha256=nB9jnUKFe-TTtc1YD5__kZTuqH56K5-A2oxaWNaIO34,9303
31
31
  abstractmemory/journal_memory.py,sha256=fHMSSw8CLfKPMMaI6yIoyxjpN6h_reqPI6ILuptngU0,23611
32
32
  abstractmemory/journal_sqlite.py,sha256=7Te1Lsh2d-Em9IMVUELcJa-flaqWRAQUBX994hAie20,28480
@@ -54,9 +54,9 @@ abstractmemory/sleep_cadence.py,sha256=sESud9WayZbPCzDO9Yfu8PWgVyyu3LtA0bTovTRzj
54
54
  abstractmemory/sleep_policy.py,sha256=xZftQLMJq6Rn9mEHc6N4BwLypmDtAKwWAXG7o-kPC_s,7471
55
55
  abstractmemory/spark.py,sha256=9Pmof_9TKHNkts-yMekOMV3Z-RrbQxEEwN56MK73iUE,8833
56
56
  abstractmemory/spreading.py,sha256=22-UZCuLrQ9w-2YbsOrTAR5pQOgopSJv9qbYeWGn3uE,16031
57
- abstractmemory/sqlite_store.py,sha256=KzZY7kLcXZlGT5DGXoJKH8mmr7-q2evUUAk6ouWIkAo,30861
57
+ abstractmemory/sqlite_store.py,sha256=Fz2ChaUfJ-OrCELpJrdgK9z2BS605Gao-stpKMv58_I,32527
58
58
  abstractmemory/store.py,sha256=4PXG-4JsNaBojr39CkrmR6fKRT9McRHK-bKXqB0eHaY,4195
59
- abstractmemory/system.py,sha256=px_Kob1cynd0NgijQX3_GO61SQJkwrhi5QwgILfsejM,47360
59
+ abstractmemory/system.py,sha256=jox7PPMmg9XV7TZXO9Wh1l_A78sYUmLRSLbV_AL1Bug,47384
60
60
  abstractmemory/system_access.py,sha256=cwj6fTyyApzWtaZiQbI72gbh03650rc4SxlePr-MwLY,6696
61
61
  abstractmemory/system_valence.py,sha256=JqX2W0GHkuzxkyl0QoJt7r24EEk8LIPAeN8eZNPUwqk,11627
62
62
  abstractmemory/tend.py,sha256=ubOh-Bb01Ro0B3WCNvdfquO-qExoF06_uo8RKPAq4HM,30080
@@ -64,7 +64,7 @@ abstractmemory/text_tokens.py,sha256=kYWAdNEEnGi50zuCam3bA6VY91An0C_6HlDlxqwkun8
64
64
  abstractmemory/vector_scoring.py,sha256=1NlC8DdoN0VFv5z6r2wyURvs86p6kbCJsKg4f__FEtw,2511
65
65
  abstractmemory/world_model.py,sha256=aHlRyyXz8hQOTbjtXzJpUpFy01wjkShyyUviAs4ST0w,46714
66
66
  abstractmemory/world_model_alias.py,sha256=A4_or9kmhgCu6AdxaZQEJQQsPi0RG9ZsePdBG9_JYnM,9436
67
- abstractmemory-0.3.0.dist-info/METADATA,sha256=2rvZ-Mf_PJuimoi7FFoF3y0G9Gqh2zu-2GfizRxfGx8,7492
68
- abstractmemory-0.3.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
69
- abstractmemory-0.3.0.dist-info/licenses/LICENSE,sha256=7t9ARuV3s_QVhJahnkFBnX_E5GPLW8BE9geplO1CGko,1079
70
- abstractmemory-0.3.0.dist-info/RECORD,,
67
+ abstractmemory-0.3.2.dist-info/METADATA,sha256=TY3BUpPbVdVcKkda9Uyc5eVvQfDzZChMUVCmdf2hpVY,7506
68
+ abstractmemory-0.3.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
69
+ abstractmemory-0.3.2.dist-info/licenses/LICENSE,sha256=7t9ARuV3s_QVhJahnkFBnX_E5GPLW8BE9geplO1CGko,1079
70
+ abstractmemory-0.3.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.31.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any