Skip to content

Commit eee9c65

Browse files
phernandezclaude
andcommitted
Fix disable_permalinks feature to properly skip permalink generation
Fixed issue where entities created with disable_permalinks=True were still getting permalinks auto-generated from file paths. Solution: - Use empty string "" as sentinel value in schema._permalink to indicate permalinks are disabled - Updated permalink property to return None when it sees the empty string sentinel - Fixed type annotations to allow Optional[Permalink] return type - Fixed create_or_update_entity to handle None permalinks properly Tests: - All disable_permalinks unit tests now pass - Rewrote integration tests to use current DB API (engine_session_factory) - Added comprehensive integration test coverage for disable_permalinks feature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 6a9f0cc commit eee9c65

7 files changed

Lines changed: 156 additions & 320 deletions

File tree

src/basic_memory/cli/commands/cloud/api_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ async def make_api_request(
9090
# Handle both FastAPI HTTPException format (nested under "detail")
9191
# and direct format
9292
detail_obj = error_detail.get("detail", error_detail)
93-
if isinstance(detail_obj, dict) and detail_obj.get("error") == "subscription_required":
93+
if (
94+
isinstance(detail_obj, dict)
95+
and detail_obj.get("error") == "subscription_required"
96+
):
9497
message = detail_obj.get("message", "Active subscription required")
9598
subscribe_url = detail_obj.get(
9699
"subscribe_url", "https://basicmemory.com/subscribe"

src/basic_memory/schemas/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ class Entity(BaseModel):
197197
"""
198198

199199
# private field to override permalink
200+
# Use empty string "" as sentinel to indicate permalinks are explicitly disabled
200201
_permalink: Optional[str] = None
201202

202203
title: str
@@ -247,8 +248,11 @@ def file_path(self):
247248
return os.path.join(self.folder, safe_title) if self.folder else safe_title
248249

249250
@property
250-
def permalink(self) -> Permalink:
251+
def permalink(self) -> Optional[Permalink]:
251252
"""Get a url friendly path}."""
253+
# Empty string is a sentinel value indicating permalinks are disabled
254+
if self._permalink == "":
255+
return None
252256
return self._permalink or generate_permalink(self.file_path)
253257

254258
@model_validator(mode="after")

src/basic_memory/services/entity_service.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ async def create_or_update_entity(self, schema: EntitySchema) -> Tuple[EntityMod
147147
)
148148

149149
# Try to find existing entity using smart resolution
150-
existing = await self.link_resolver.resolve_link(
151-
schema.file_path
152-
) or await self.link_resolver.resolve_link(schema.permalink)
150+
existing = await self.link_resolver.resolve_link(schema.file_path)
151+
if not existing and schema.permalink:
152+
existing = await self.link_resolver.resolve_link(schema.permalink)
153153

154154
if existing:
155155
logger.debug(f"Found existing entity: {existing.file_path}")
@@ -197,12 +197,14 @@ async def create_entity(self, schema: EntitySchema) -> EntityModel:
197197
)
198198

199199
# Get unique permalink (prioritizing content frontmatter) unless disabled
200-
if self.app_config and not self.app_config.disable_permalinks:
200+
if self.app_config and self.app_config.disable_permalinks:
201+
# Use empty string as sentinel to indicate permalinks are disabled
202+
# The permalink property will return None when it sees empty string
203+
schema._permalink = ""
204+
else:
205+
# Generate and set permalink
201206
permalink = await self.resolve_permalink(file_path, content_markdown)
202207
schema._permalink = permalink
203-
else:
204-
# If permalinks are disabled, set to None
205-
schema._permalink = None
206208

207209
post = await schema_to_markdown(schema)
208210

@@ -754,9 +756,8 @@ async def move_entity(
754756
updates = {"file_path": destination_path}
755757

756758
# 7. Update permalink if configured or if entity has null permalink (unless disabled)
757-
if (
758-
not app_config.disable_permalinks
759-
and (app_config.update_permalinks_on_move or old_permalink is None)
759+
if not app_config.disable_permalinks and (
760+
app_config.update_permalinks_on_move or old_permalink is None
760761
):
761762
# Generate new permalink from destination path
762763
new_permalink = await self.resolve_permalink(destination_path)

src/basic_memory/sync/watch_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ async def _watch_projects_cycle(self, projects: Sequence[Project], stop_event: a
121121
ignore_patterns = self._get_ignore_patterns(project_path)
122122

123123
if should_ignore_path(file_path, project_path, ignore_patterns):
124-
logger.trace(f"Ignoring watched file change: {file_path.relative_to(project_path)}")
124+
logger.trace(
125+
f"Ignoring watched file change: {file_path.relative_to(project_path)}"
126+
)
125127
continue
126128

127129
project_changes[project].append((change, path))

0 commit comments

Comments
 (0)