fix: Prevent deleted projects from being recreated by background sync… · basicmachines-co/basic-memory@449b62d · GitHub
Skip to content

Commit 449b62d

Browse files
authored
fix: Prevent deleted projects from being recreated by background sync (#193) (#370)
Signed-off-by: Claude <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent b7497d7 commit 449b62d

4 files changed

Lines changed: 152 additions & 3 deletions

File tree

src/basic_memory/services/project_service.py

Lines changed: 7 additions & 3 deletions

src/basic_memory/sync/watch_service.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,17 @@ async def handle_changes(self, project: Project, changes: Set[FileChange]) -> No
236236
# avoid circular imports
237237
from basic_memory.sync.sync_service import get_sync_service
238238

239+
# Check if project still exists in configuration before processing
240+
# This prevents deleted projects from being recreated by background sync
241+
from basic_memory.config import ConfigManager
242+
config_manager = ConfigManager()
243+
if project.name not in config_manager.projects and project.permalink not in config_manager.projects:
244+
logger.info(
245+
f"Skipping sync for deleted project: {project.name}, "
246+
f"change_count={len(changes)}"
247+
)
248+
return
249+
239250
sync_service = await get_sync_service(project)
240251
file_service = sync_service.file_service
241252

tests/services/test_project_service.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,3 +1198,55 @@ async def test_add_project_nested_validation_with_project_root(
11981198
# Clean up
11991199
if parent_project_name in project_service.projects:
12001200
await project_service.remove_project(parent_project_name)
1201+
1202+
1203+
@pytest.mark.asyncio
1204+
async def test_synchronize_projects_removes_db_only_projects(project_service: ProjectService):
1205+
"""Test that synchronize_projects removes projects that exist in DB but not in config.
1206+
1207+
This is a regression test for issue #193 where deleted projects would be re-added
1208+
to config during synchronization, causing them to reappear after deletion.
1209+
Config is the source of truth - if a project is deleted from config, it should be
1210+
removed from the database during synchronization.
1211+
"""
1212+
test_project_name = f"test-db-only-{os.urandom(4).hex()}"
1213+
with tempfile.TemporaryDirectory() as temp_dir:
1214+
test_root = Path(temp_dir)
1215+
test_project_path = str(test_root / "test-db-only")
1216+
1217+
# Make sure the test directory exists
1218+
os.makedirs(test_project_path, exist_ok=True)
1219+
1220+
try:
1221+
# Add project to database only (not to config) - simulating orphaned DB entry
1222+
project_data = {
1223+
"name": test_project_name,
1224+
"path": test_project_path,
1225+
"permalink": test_project_name.lower().replace(" ", "-"),
1226+
"is_active": True,
1227+
}
1228+
created_project = await project_service.repository.create(project_data)
1229+
1230+
# Verify it exists in DB but not in config
1231+
db_project = await project_service.repository.get_by_name(test_project_name)
1232+
assert db_project is not None
1233+
assert test_project_name not in project_service.projects
1234+
1235+
# Call synchronize_projects - this should remove the orphaned DB entry
1236+
# because config is the source of truth
1237+
await project_service.synchronize_projects()
1238+
1239+
# Verify project was removed from database
1240+
db_project_after = await project_service.repository.get_by_name(test_project_name)
1241+
assert db_project_after is None, (
1242+
"Project should be removed from DB when not in config (config is source of truth)"
1243+
)
1244+
1245+
# Verify it's still not in config
1246+
assert test_project_name not in project_service.projects
1247+
1248+
finally:
1249+
# Clean up if needed
1250+
db_project = await project_service.repository.get_by_name(test_project_name)
1251+
if db_project:
1252+
await project_service.repository.delete(db_project.id)

tests/sync/test_watch_service.py

Lines changed: 82 additions & 0 deletions

0 commit comments

Comments
 (0)