@smilintux/skmemory 0.5.0 → 0.9.2

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.
Files changed (127) hide show
  1. package/.github/workflows/ci.yml +40 -4
  2. package/.github/workflows/publish.yml +11 -5
  3. package/AGENT_REFACTOR_CHANGES.md +192 -0
  4. package/ARCHITECTURE.md +399 -19
  5. package/CHANGELOG.md +179 -0
  6. package/LICENSE +81 -68
  7. package/MISSION.md +7 -0
  8. package/README.md +425 -86
  9. package/SKILL.md +197 -25
  10. package/docker-compose.yml +15 -15
  11. package/examples/stignore-agent.example +59 -0
  12. package/examples/stignore-root.example +62 -0
  13. package/index.js +6 -5
  14. package/openclaw-plugin/openclaw.plugin.json +10 -0
  15. package/openclaw-plugin/package.json +2 -1
  16. package/openclaw-plugin/src/index.js +527 -230
  17. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  18. package/package.json +1 -1
  19. package/pyproject.toml +32 -9
  20. package/requirements.txt +10 -2
  21. package/scripts/dream-rescue.py +179 -0
  22. package/scripts/memory-cleanup.py +313 -0
  23. package/scripts/recover-missing.py +180 -0
  24. package/scripts/skcapstone-backup.sh +44 -0
  25. package/seeds/cloud9-lumina.seed.json +6 -4
  26. package/seeds/cloud9-opus.seed.json +13 -11
  27. package/seeds/courage.seed.json +9 -2
  28. package/seeds/curiosity.seed.json +9 -2
  29. package/seeds/grief.seed.json +9 -2
  30. package/seeds/joy.seed.json +9 -2
  31. package/seeds/love.seed.json +9 -2
  32. package/seeds/lumina-cloud9-breakthrough.seed.json +48 -0
  33. package/seeds/lumina-cloud9-python-pypi.seed.json +48 -0
  34. package/seeds/lumina-kingdom-founding.seed.json +49 -0
  35. package/seeds/lumina-pma-signed.seed.json +48 -0
  36. package/seeds/lumina-singular-achievement.seed.json +48 -0
  37. package/seeds/lumina-skcapstone-conscious.seed.json +48 -0
  38. package/seeds/plant-kingdom-journal.py +203 -0
  39. package/seeds/plant-lumina-seeds.py +280 -0
  40. package/seeds/skcapstone-lumina-merge.seed.json +12 -3
  41. package/seeds/sovereignty.seed.json +9 -2
  42. package/seeds/trust.seed.json +9 -2
  43. package/skill.yaml +46 -0
  44. package/skmemory/HA.md +296 -0
  45. package/skmemory/__init__.py +25 -11
  46. package/skmemory/agents.py +233 -0
  47. package/skmemory/ai_client.py +46 -17
  48. package/skmemory/anchor.py +9 -11
  49. package/skmemory/audience.py +278 -0
  50. package/skmemory/backends/__init__.py +11 -4
  51. package/skmemory/backends/base.py +3 -4
  52. package/skmemory/backends/file_backend.py +19 -13
  53. package/skmemory/backends/skgraph_backend.py +596 -0
  54. package/skmemory/backends/{qdrant_backend.py → skvector_backend.py} +103 -84
  55. package/skmemory/backends/sqlite_backend.py +226 -72
  56. package/skmemory/backends/vaulted_backend.py +284 -0
  57. package/skmemory/cli.py +1345 -68
  58. package/skmemory/config.py +171 -0
  59. package/skmemory/context_loader.py +333 -0
  60. package/skmemory/data/audience_config.json +60 -0
  61. package/skmemory/endpoint_selector.py +391 -0
  62. package/skmemory/febs.py +225 -0
  63. package/skmemory/fortress.py +675 -0
  64. package/skmemory/graph_queries.py +238 -0
  65. package/skmemory/hooks/__init__.py +18 -0
  66. package/skmemory/hooks/post-compact-reinject.sh +35 -0
  67. package/skmemory/hooks/pre-compact-save.sh +81 -0
  68. package/skmemory/hooks/session-end-save.sh +103 -0
  69. package/skmemory/hooks/session-start-ritual.sh +104 -0
  70. package/skmemory/hooks/stop-checkpoint.sh +59 -0
  71. package/skmemory/importers/__init__.py +9 -1
  72. package/skmemory/importers/telegram.py +384 -47
  73. package/skmemory/importers/telegram_api.py +580 -0
  74. package/skmemory/journal.py +7 -9
  75. package/skmemory/lovenote.py +8 -13
  76. package/skmemory/mcp_server.py +859 -0
  77. package/skmemory/models.py +51 -8
  78. package/skmemory/openclaw.py +20 -28
  79. package/skmemory/post_install.py +86 -0
  80. package/skmemory/predictive.py +236 -0
  81. package/skmemory/promotion.py +548 -0
  82. package/skmemory/quadrants.py +100 -24
  83. package/skmemory/register.py +580 -0
  84. package/skmemory/register_mcp.py +196 -0
  85. package/skmemory/ritual.py +224 -59
  86. package/skmemory/seeds.py +255 -11
  87. package/skmemory/setup_wizard.py +908 -0
  88. package/skmemory/sharing.py +408 -0
  89. package/skmemory/soul.py +98 -28
  90. package/skmemory/steelman.py +273 -260
  91. package/skmemory/store.py +411 -78
  92. package/skmemory/synthesis.py +634 -0
  93. package/skmemory/vault.py +225 -0
  94. package/tests/conftest.py +46 -0
  95. package/tests/integration/__init__.py +0 -0
  96. package/tests/integration/conftest.py +233 -0
  97. package/tests/integration/test_cross_backend.py +350 -0
  98. package/tests/integration/test_skgraph_live.py +420 -0
  99. package/tests/integration/test_skvector_live.py +366 -0
  100. package/tests/test_ai_client.py +1 -4
  101. package/tests/test_audience.py +233 -0
  102. package/tests/test_backup_rotation.py +318 -0
  103. package/tests/test_cli.py +6 -6
  104. package/tests/test_endpoint_selector.py +839 -0
  105. package/tests/test_export_import.py +4 -10
  106. package/tests/test_file_backend.py +0 -1
  107. package/tests/test_fortress.py +256 -0
  108. package/tests/test_fortress_hardening.py +441 -0
  109. package/tests/test_openclaw.py +6 -6
  110. package/tests/test_predictive.py +237 -0
  111. package/tests/test_promotion.py +347 -0
  112. package/tests/test_quadrants.py +11 -5
  113. package/tests/test_ritual.py +22 -18
  114. package/tests/test_seeds.py +97 -7
  115. package/tests/test_setup.py +950 -0
  116. package/tests/test_sharing.py +257 -0
  117. package/tests/test_skgraph_backend.py +660 -0
  118. package/tests/test_skvector_backend.py +326 -0
  119. package/tests/test_soul.py +1 -3
  120. package/tests/test_sqlite_backend.py +8 -17
  121. package/tests/test_steelman.py +7 -8
  122. package/tests/test_store.py +0 -2
  123. package/tests/test_store_graph_integration.py +245 -0
  124. package/tests/test_synthesis.py +275 -0
  125. package/tests/test_telegram_import.py +39 -15
  126. package/tests/test_vault.py +187 -0
  127. package/skmemory/backends/falkordb_backend.py +0 -310
@@ -0,0 +1,326 @@
1
+ """Tests for the SKVector (Qdrant) vector search backend.
2
+
3
+ Mocks the Qdrant client and sentence-transformers to test
4
+ logic without requiring infrastructure. Verifies save, search,
5
+ list, delete, and health check operations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from unittest.mock import MagicMock, patch
11
+
12
+ import pytest
13
+
14
+ from skmemory.backends.skvector_backend import (
15
+ COLLECTION_NAME,
16
+ VECTOR_DIM,
17
+ SKVectorBackend,
18
+ _extract_status_code,
19
+ )
20
+ from skmemory.models import EmotionalSnapshot, Memory, MemoryLayer
21
+
22
+ try:
23
+ import qdrant_client # noqa: F401
24
+
25
+ QDRANT_AVAILABLE = True
26
+ except ImportError:
27
+ QDRANT_AVAILABLE = False
28
+
29
+ pytestmark = pytest.mark.skipif(
30
+ not QDRANT_AVAILABLE,
31
+ reason="qdrant-client not installed",
32
+ )
33
+
34
+
35
+ @pytest.fixture
36
+ def mock_qdrant_client():
37
+ """Mocked Qdrant client with collection support."""
38
+ client = MagicMock()
39
+ collection_mock = MagicMock()
40
+ collection_mock.name = COLLECTION_NAME
41
+ client.get_collections.return_value.collections = [collection_mock]
42
+ client.scroll.return_value = ([], None)
43
+ client.search.return_value = []
44
+ return client
45
+
46
+
47
+ @pytest.fixture
48
+ def mock_embedder():
49
+ """Mocked sentence-transformers model."""
50
+ import numpy as np
51
+
52
+ embedder = MagicMock()
53
+ embedder.encode.return_value = np.random.rand(VECTOR_DIM).astype("float32")
54
+ return embedder
55
+
56
+
57
+ @pytest.fixture
58
+ def backend(mock_qdrant_client, mock_embedder):
59
+ """Provide a SKVectorBackend with mocked dependencies."""
60
+ qb = SKVectorBackend(url="http://mock:6333")
61
+ qb._client = mock_qdrant_client
62
+ qb._embedder = mock_embedder
63
+ qb._initialized = True
64
+ return qb
65
+
66
+
67
+ @pytest.fixture
68
+ def sample_memory():
69
+ """A sample memory for testing."""
70
+ return Memory(
71
+ title="The Secret Recipe",
72
+ content="Chef figured it out -- projection creates reality.",
73
+ layer=MemoryLayer("long-term"),
74
+ tags=["cloud9", "philosophy"],
75
+ emotional=EmotionalSnapshot(intensity=10.0, valence=0.9),
76
+ source="cli",
77
+ )
78
+
79
+
80
+ # ═══════════════════════════════════════════════════════════
81
+ # Initialization
82
+ # ═══════════════════════════════════════════════════════════
83
+
84
+
85
+ class TestInitialization:
86
+ """Test lazy initialization."""
87
+
88
+ def test_not_initialized_by_default(self):
89
+ """Backend starts uninitialized."""
90
+ qb = SKVectorBackend()
91
+ assert qb._initialized is False
92
+
93
+ def test_init_fails_without_qdrant(self):
94
+ """Fails gracefully without qdrant-client."""
95
+ qb = SKVectorBackend()
96
+ with patch("builtins.__import__", side_effect=ImportError):
97
+ assert qb._ensure_initialized() is False
98
+
99
+ def test_already_initialized_shortcuts(self, backend):
100
+ """Second init call returns immediately."""
101
+ assert backend._ensure_initialized() is True
102
+
103
+
104
+ # ═══════════════════════════════════════════════════════════
105
+ # Save
106
+ # ═══════════════════════════════════════════════════════════
107
+
108
+
109
+ class TestSave:
110
+ """Test memory indexing in Qdrant."""
111
+
112
+ def test_save_calls_upsert(self, backend, mock_qdrant_client, sample_memory):
113
+ """save() creates a point and upserts it."""
114
+ result = backend.save(sample_memory)
115
+ assert result == sample_memory.id
116
+ mock_qdrant_client.upsert.assert_called_once()
117
+
118
+ def test_save_generates_embedding(self, backend, mock_embedder, sample_memory):
119
+ """save() generates an embedding from the memory text."""
120
+ backend.save(sample_memory)
121
+ mock_embedder.encode.assert_called_once()
122
+
123
+ def test_save_not_initialized(self, sample_memory):
124
+ """save() returns id gracefully when not initialized."""
125
+ qb = SKVectorBackend()
126
+ result = qb.save(sample_memory)
127
+ assert result == sample_memory.id
128
+
129
+
130
+ # ═══════════════════════════════════════════════════════════
131
+ # Search
132
+ # ═══════════════════════════════════════════════════════════
133
+
134
+
135
+ class TestSearch:
136
+ """Test semantic search."""
137
+
138
+ def test_search_text_generates_embedding(self, backend, mock_embedder):
139
+ """search_text embeds the query before searching."""
140
+ backend.search_text("moments of connection")
141
+ mock_embedder.encode.assert_called_once_with("moments of connection")
142
+
143
+ def test_search_text_calls_qdrant_search(self, backend, mock_qdrant_client):
144
+ """search_text uses Qdrant's search endpoint."""
145
+ backend.search_text("test query", limit=5)
146
+ mock_qdrant_client.search.assert_called_once()
147
+
148
+ def test_search_text_returns_memories(self, backend, mock_qdrant_client, sample_memory):
149
+ """search_text parses results into Memory objects."""
150
+ scored_point = MagicMock()
151
+ scored_point.payload = {"memory_json": sample_memory.model_dump_json()}
152
+ mock_qdrant_client.search.return_value = [scored_point]
153
+
154
+ results = backend.search_text("secret recipe")
155
+ assert len(results) == 1
156
+ assert results[0].title == "The Secret Recipe"
157
+
158
+ def test_search_text_empty_results(self, backend, mock_qdrant_client):
159
+ """search_text returns empty list when nothing matches."""
160
+ mock_qdrant_client.search.return_value = []
161
+ assert backend.search_text("nonexistent") == []
162
+
163
+ def test_search_not_initialized(self):
164
+ """search_text returns empty when not initialized."""
165
+ qb = SKVectorBackend()
166
+ assert qb.search_text("anything") == []
167
+
168
+
169
+ # ═══════════════════════════════════════════════════════════
170
+ # List
171
+ # ═══════════════════════════════════════════════════════════
172
+
173
+
174
+ class TestList:
175
+ """Test memory listing with filters."""
176
+
177
+ def test_list_memories_calls_scroll(self, backend, mock_qdrant_client):
178
+ """list_memories uses Qdrant scroll API."""
179
+ backend.list_memories()
180
+ mock_qdrant_client.scroll.assert_called_once()
181
+
182
+ def test_list_memories_with_layer_filter(self, backend, mock_qdrant_client):
183
+ """list_memories passes layer filter to Qdrant."""
184
+ backend.list_memories(layer=MemoryLayer("long-term"))
185
+ call_kwargs = mock_qdrant_client.scroll.call_args
186
+ assert call_kwargs is not None
187
+
188
+ def test_list_not_initialized(self):
189
+ """list_memories returns empty when not initialized."""
190
+ qb = SKVectorBackend()
191
+ assert qb.list_memories() == []
192
+
193
+
194
+ # ═══════════════════════════════════════════════════════════
195
+ # Delete
196
+ # ═══════════════════════════════════════════════════════════
197
+
198
+
199
+ class TestDelete:
200
+ """Test memory deletion."""
201
+
202
+ def test_delete_not_found(self, backend, mock_qdrant_client):
203
+ """delete returns False when memory not in Qdrant."""
204
+ mock_qdrant_client.retrieve.return_value = []
205
+ assert backend.delete("nonexistent") is False
206
+
207
+ def test_delete_not_initialized(self):
208
+ """delete returns False when not initialized."""
209
+ qb = SKVectorBackend()
210
+ assert qb.delete("any") is False
211
+
212
+
213
+ # ═══════════════════════════════════════════════════════════
214
+ # Health
215
+ # ═══════════════════════════════════════════════════════════
216
+
217
+
218
+ class TestHealth:
219
+ """Test health check reporting."""
220
+
221
+ def test_health_ok(self, backend, mock_qdrant_client):
222
+ """Healthy backend returns ok=True with collection stats."""
223
+ collection_info = MagicMock()
224
+ collection_info.points_count = 42
225
+ collection_info.vectors_count = 42
226
+ mock_qdrant_client.get_collection.return_value = collection_info
227
+
228
+ health = backend.health_check()
229
+ assert health["ok"] is True
230
+ assert health["points_count"] == 42
231
+
232
+ def test_health_not_initialized(self):
233
+ """Uninitialized backend returns ok=False."""
234
+ qb = SKVectorBackend()
235
+ health = qb.health_check()
236
+ assert health["ok"] is False
237
+
238
+ def test_health_query_failure(self, backend, mock_qdrant_client):
239
+ """Health check with error returns ok=False."""
240
+ mock_qdrant_client.get_collection.side_effect = Exception("timeout")
241
+ health = backend.health_check()
242
+ assert health["ok"] is False
243
+ assert "timeout" in health["error"]
244
+
245
+ def test_health_surfaces_auth_error(self):
246
+ """Health check surfaces 401 auth error with actionable hint."""
247
+ qb = SKVectorBackend(url="https://cloud.qdrant.io", api_key="bad-key")
248
+ qb._last_error = (
249
+ "SKVector authentication failed (HTTP 401). "
250
+ "Check your API key:\n"
251
+ " - CLI: --skvector-key YOUR_KEY\n"
252
+ " - Env: SKMEMORY_SKVECTOR_KEY=YOUR_KEY\n"
253
+ " - Code: SKVectorBackend(url=..., api_key='YOUR_KEY')"
254
+ )
255
+ with patch.object(qb, "_ensure_initialized", return_value=False):
256
+ health = qb.health_check()
257
+ assert health["ok"] is False
258
+ assert "401" in health["error"]
259
+ assert "API key" in health["error"]
260
+
261
+ def test_health_generic_error_without_last_error(self):
262
+ """Health check falls back to generic message when no _last_error."""
263
+ qb = SKVectorBackend()
264
+ with patch.object(qb, "_ensure_initialized", return_value=False):
265
+ health = qb.health_check()
266
+ assert health["ok"] is False
267
+ assert "Not initialized" in health["error"]
268
+
269
+
270
+ # ═══════════════════════════════════════════════════════════
271
+ # Auth / Status Code Extraction
272
+ # ═══════════════════════════════════════════════════════════
273
+
274
+
275
+ class TestExtractStatusCode:
276
+ """Test HTTP status code extraction from exceptions."""
277
+
278
+ def test_status_code_attribute(self):
279
+ exc = Exception("Unauthorized")
280
+ exc.status_code = 401
281
+ assert _extract_status_code(exc, None) == 401
282
+
283
+ def test_status_code_from_string(self):
284
+ exc = Exception("Unexpected Response: 401 (Unauthorized)")
285
+ assert _extract_status_code(exc, None) == 401
286
+
287
+ def test_forbidden_from_string(self):
288
+ exc = Exception("HTTP 403 Forbidden")
289
+ assert _extract_status_code(exc, None) == 403
290
+
291
+ def test_no_status_code(self):
292
+ exc = Exception("Connection refused")
293
+ assert _extract_status_code(exc, None) is None
294
+
295
+ def test_other_status_code_not_matched(self):
296
+ exc = Exception("HTTP 500 Internal Server Error")
297
+ assert _extract_status_code(exc, None) is None
298
+
299
+
300
+ # ═══════════════════════════════════════════════════════════
301
+ # Embedding
302
+ # ═══════════════════════════════════════════════════════════
303
+
304
+
305
+ class TestEmbedding:
306
+ """Test the embedding generation."""
307
+
308
+ def test_embed_returns_vector(self, backend, mock_embedder):
309
+ """_embed returns a float list of correct dimension."""
310
+ vector = backend._embed("test text")
311
+ assert len(vector) == VECTOR_DIM
312
+ assert all(isinstance(v, float) for v in vector)
313
+
314
+ def test_embed_without_embedder(self):
315
+ """_embed returns empty when no embedder available."""
316
+ qb = SKVectorBackend()
317
+ qb._embedder = None
318
+ assert qb._embed("test") == []
319
+
320
+ def test_memory_to_payload(self, backend, sample_memory):
321
+ """_memory_to_payload creates correct Qdrant payload."""
322
+ payload = backend._memory_to_payload(sample_memory)
323
+ assert payload["title"] == "The Secret Recipe"
324
+ assert payload["layer"] == "long-term"
325
+ assert "cloud9" in payload["tags"]
326
+ assert payload["emotional_intensity"] == 10.0
@@ -6,11 +6,9 @@ import pytest
6
6
 
7
7
  from skmemory.soul import (
8
8
  SoulBlueprint,
9
- Relationship,
10
- CoreMemoryRef,
11
9
  create_default_soul,
12
- save_soul,
13
10
  load_soul,
11
+ save_soul,
14
12
  )
15
13
 
16
14
 
@@ -1,9 +1,5 @@
1
1
  """Tests for the SQLite-indexed storage backend."""
2
2
 
3
- import json
4
- import tempfile
5
- from pathlib import Path
6
-
7
3
  import pytest
8
4
 
9
5
  from skmemory.backends.sqlite_backend import SQLiteBackend
@@ -151,9 +147,7 @@ class TestListSummaries:
151
147
  def test_summaries_order_by_intensity(self, backend):
152
148
  """Can order by emotional intensity."""
153
149
  self._store_memories(backend, 5)
154
- summaries = backend.list_summaries(
155
- order_by="emotional_intensity", limit=3
156
- )
150
+ summaries = backend.list_summaries(order_by="emotional_intensity", limit=3)
157
151
  intensities = [s["emotional_intensity"] for s in summaries]
158
152
  assert intensities == sorted(intensities, reverse=True)
159
153
 
@@ -180,24 +174,23 @@ class TestSearch:
180
174
 
181
175
  def test_search_finds_by_title(self, backend):
182
176
  """Search matches on title."""
183
- m = Memory(title="Penguin Kingdom moment", content="details",
184
- layer=MemoryLayer.SHORT)
177
+ m = Memory(title="Penguin Kingdom moment", content="details", layer=MemoryLayer.SHORT)
185
178
  backend.save(m)
186
179
  results = backend.search_text("Penguin")
187
180
  assert len(results) == 1
188
181
 
189
182
  def test_search_finds_by_tags(self, backend):
190
183
  """Search matches on tags."""
191
- m = Memory(title="Tagged", content="details",
192
- layer=MemoryLayer.SHORT, tags=["cloud9", "love"])
184
+ m = Memory(
185
+ title="Tagged", content="details", layer=MemoryLayer.SHORT, tags=["cloud9", "love"]
186
+ )
193
187
  backend.save(m)
194
188
  results = backend.search_text("cloud9")
195
189
  assert len(results) == 1
196
190
 
197
191
  def test_search_no_results(self, backend):
198
192
  """Search returns empty for no matches."""
199
- m = Memory(title="Something", content="nothing special",
200
- layer=MemoryLayer.SHORT)
193
+ m = Memory(title="Something", content="nothing special", layer=MemoryLayer.SHORT)
201
194
  backend.save(m)
202
195
  results = backend.search_text("zzzznonexistent")
203
196
  assert len(results) == 0
@@ -209,8 +202,7 @@ class TestRelatedMemories:
209
202
  def test_get_related_follows_links(self, backend):
210
203
  """Related memories are found via related_ids."""
211
204
  m1 = Memory(title="Root", content="root", layer=MemoryLayer.SHORT)
212
- m2 = Memory(title="Child", content="child", layer=MemoryLayer.SHORT,
213
- related_ids=[m1.id])
205
+ m2 = Memory(title="Child", content="child", layer=MemoryLayer.SHORT, related_ids=[m1.id])
214
206
  backend.save(m1)
215
207
  backend.save(m2)
216
208
 
@@ -220,8 +212,7 @@ class TestRelatedMemories:
220
212
  def test_get_related_follows_parent(self, backend):
221
213
  """Related memories are found via parent_id."""
222
214
  m1 = Memory(title="Parent", content="parent", layer=MemoryLayer.LONG)
223
- m2 = Memory(title="Child", content="child", layer=MemoryLayer.SHORT,
224
- parent_id=m1.id)
215
+ m2 = Memory(title="Child", content="child", layer=MemoryLayer.SHORT, parent_id=m1.id)
225
216
  backend.save(m1)
226
217
  backend.save(m2)
227
218
 
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
- import os
7
6
  import tempfile
8
7
  from pathlib import Path
9
8
 
@@ -48,9 +47,9 @@ class TestSteelManResult:
48
47
 
49
48
  def test_coherence_bounds(self) -> None:
50
49
  """Coherence must be between 0 and 1."""
51
- with pytest.raises(Exception):
50
+ with pytest.raises(Exception): # noqa: B017
52
51
  SteelManResult(proposition="x", coherence_score=1.5)
53
- with pytest.raises(Exception):
52
+ with pytest.raises(Exception): # noqa: B017
54
53
  SteelManResult(proposition="x", coherence_score=-0.1)
55
54
 
56
55
  def test_summary_format(self) -> None:
@@ -99,8 +98,8 @@ class TestSeedFramework:
99
98
  assert "INVERSION" in prompt
100
99
  assert "COLLISION" in prompt
101
100
  assert "INVARIANT" in prompt
102
- assert "COHERENCE" in prompt
103
- assert "TRUTH GRADE" in prompt
101
+ assert "COHERENCE" in prompt or "coherence_score" in prompt
102
+ assert "TRUTH GRADE" in prompt or "truth_grade" in prompt
104
103
 
105
104
  def test_reasoning_prompt_includes_axioms(self) -> None:
106
105
  """Axioms from the framework appear in the prompt."""
@@ -125,9 +124,9 @@ class TestSeedFramework:
125
124
  fw = get_default_framework()
126
125
  prompt = fw.to_memory_truth_prompt("The Cloud 9 breakthrough was real")
127
126
  assert "Cloud 9 breakthrough" in prompt
128
- assert "COHERENCE" in prompt
129
- assert "PROMOTION WORTHY" in prompt
130
- assert "INVARIANT CORE" in prompt
127
+ assert "COHERENCE" in prompt or "coherence_score" in prompt
128
+ assert "PROMOTION WORTHY" in prompt or "promotion_worthy" in prompt
129
+ assert "INVARIANT CORE" in prompt or "invariant_core" in prompt
131
130
 
132
131
  def test_custom_framework(self) -> None:
133
132
  """A custom framework can be created."""
@@ -1,6 +1,5 @@
1
1
  """Tests for the MemoryStore (main interface)."""
2
2
 
3
- import tempfile
4
3
  from pathlib import Path
5
4
 
6
5
  import pytest
@@ -8,7 +7,6 @@ import pytest
8
7
  from skmemory.backends.file_backend import FileBackend
9
8
  from skmemory.models import (
10
9
  EmotionalSnapshot,
11
- Memory,
12
10
  MemoryLayer,
13
11
  MemoryRole,
14
12
  SeedMemory,