Skip to content

Remove the dead resolve_permalink() call for regular files, and document the no-permalink invariant #1182

Description

@jope-bm

Rescoped 2026-08-04 after review. permalink IS NULL for non-markdown files is the intended design, not a defect — see discussion. What remains is dead code plus an undocumented invariant. The original, wider framing is preserved in this issue's history.

What remains

1. A dead resolve_permalink() call. batch_indexer._upsert_regular_file (src/basic_memory/indexing/batch_indexer.py, ~line 441) computes a permalink and discards it:

if existing is None:
    await self.entity_service.resolve_permalink(file.path, skip_conflict_check=True)  # result discarded
    entity = Entity(
        note_type="file",
        file_path=file.path,
        ...                                    # no permalink=  (correct — see below)
    )

resolve_permalink is read-only (conflict detection + lookups, returns a string, no persistence side effect), and with skip_conflict_check=True it doesn't even emit the conflict warning. So this is a wasted DB round-trip per regular file during indexing, and it misleads readers into thinking the value was meant to be stored. Remove it, or comment why it's there. Check sync_service for the same pattern.

2. The invariant is undocumented. "Non-markdown entity ⇒ no permalink" holds in practice but is nowhere stated, so consumers keep rediscovering it the hard way — one that assumed a non-null permalink previously produced a hard failure when deleting a non-markdown file (fixed Cloud-side by making the delete/live-update contract permalink-optional). Document it, and keep consumer contracts explicitly permalink-optional.

Why NULL is correct

The permalink's durable home is the frontmatter; the DB row is a projection. A non-markdown file has nowhere to record one, so a DB-only permalink would be derived state that cannot round-trip:

  • Unstable. Collisions resolve by suffixing (-1, -1-1). With no file-side record, a rebuild-from-scratch regenerates permalinks in whatever order indexing happens — the same file could return with a different address, silently rotting any memory:// link or wikilink to it.
  • Breaks rebuild-from-files. Drop the DB, reindex, get the same graph back; a permalink with no file-side record is the one field that wouldn't survive.
  • Not needed. File entities already have two stable, file-derived addresses: file_path and external_id. Permalink serves note semantics (memory:// addressing, wikilinks, search keying), which a .png doesn't have.

Supporting observation

In one sampled deployment the NULL-permalink set was exactly the files without frontmatter — 114 note_type=file plus 1 note_type=canvas (.canvas files are JSON; file_service assigns application/json) — while every markdown note_type had a permalink. The invariant is already consistent; only the documentation and the dead call are missing.

Explicitly not wanted

  • Storing permalinks for regular files (see "Why NULL is correct").
  • Backfilling existing NULL rows.
  • Any change to permalink-keyed routing, search, or wikilink resolution.

Open question

Are non-markdown files excluded from full-text search because they lack a permalink, or by design independently of it? Observed but not determined — worth confirming so the search behavior is intentional rather than incidental.


Moved from the private Cloud tracker (basicmachines-co/basic-memory-cloud#1536), which holds the production evidence and the original failure report. Details omitted here because this repository is public.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentation

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions