Summary
batch_indexer._upsert_regular_file computes a permalink for a regular (non-markdown) file and then discards it, creating the entity without one. Every non-.md file — Dockerfile, scripts/*.py, images — is therefore indexed with permalink = NULL.
src/basic_memory/indexing/batch_indexer.py (~line 441):
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,
checksum=checksum,
title=Path(file.path).name,
... # no permalink=
)
The resolve_permalink() call reads as unintentional rather than deliberate: the value is computed and thrown away. sync_service should be checked for the same pattern.
Why it matters
A permalink is an entity's stable address, used by memory:// references, wikilinks, and permalink-keyed lookups. Because these rows have none, every consumer must treat permalink as optional — and a consumer that didn't previously produced a hard failure when deleting a non-markdown file (tracked privately; that specific delete path has since been fixed by making the delete/live-update contract permalink-optional).
Honest scoping: this is a latent-consistency issue, not a live user-facing bug. Nothing is known to be broken today; the value of fixing it is removing a class of "assumed permalink exists" defects at the source instead of guarding each consumer.
Suggested fix
- Assign the resolved permalink when indexing a regular file (stop discarding it). Audit
sync_service for the same pattern.
- Decide the backfill question for existing
NULL rows — new rows having permalinks while old ones don't is its own inconsistency. (Observed in one deployment: ~114 note_type=file rows, all NULL, alongside a stray note_type=canvas row, so the assumption is not strictly limited to note_type=file.)
- Regression test: index a non-
.md file → assert a permalink is stored → delete it → assert deleted and not resurrected.
Not purely additive. Giving regular files real permalinks changes data shape (permalink-keyed routing, search, wikilink resolution) and interacts with permalink-collision handling, so it warrants a deliberate decision rather than a drive-by change.
Open questions
- Are non-markdown files excluded from full-text search because they lack a permalink, or by design independently of it? Observed but not determined.
- Should regular files get a permalink at all, or is
NULL the intended design and the resolve_permalink() call simply dead code to remove? Either resolution is coherent; the current state — computing a value and discarding it — is not.
Moved from the private Cloud tracker (basicmachines-co/basic-memory-cloud#1536), which holds the production evidence, the original failure report, and the verification that the delete path is fixed. Details omitted here because this repository is public.
Summary
batch_indexer._upsert_regular_filecomputes a permalink for a regular (non-markdown) file and then discards it, creating the entity without one. Every non-.mdfile —Dockerfile,scripts/*.py, images — is therefore indexed withpermalink = NULL.src/basic_memory/indexing/batch_indexer.py(~line 441):The
resolve_permalink()call reads as unintentional rather than deliberate: the value is computed and thrown away.sync_serviceshould be checked for the same pattern.Why it matters
A permalink is an entity's stable address, used by
memory://references, wikilinks, and permalink-keyed lookups. Because these rows have none, every consumer must treat permalink as optional — and a consumer that didn't previously produced a hard failure when deleting a non-markdown file (tracked privately; that specific delete path has since been fixed by making the delete/live-update contract permalink-optional).Honest scoping: this is a latent-consistency issue, not a live user-facing bug. Nothing is known to be broken today; the value of fixing it is removing a class of "assumed permalink exists" defects at the source instead of guarding each consumer.
Suggested fix
sync_servicefor the same pattern.NULLrows — new rows having permalinks while old ones don't is its own inconsistency. (Observed in one deployment: ~114note_type=filerows, allNULL, alongside a straynote_type=canvasrow, so the assumption is not strictly limited tonote_type=file.).mdfile → assert a permalink is stored → delete it → assert deleted and not resurrected.Not purely additive. Giving regular files real permalinks changes data shape (permalink-keyed routing, search, wikilink resolution) and interacts with permalink-collision handling, so it warrants a deliberate decision rather than a drive-by change.
Open questions
NULLthe intended design and theresolve_permalink()call simply dead code to remove? Either resolution is coherent; the current state — computing a value and discarding it — is not.Moved from the private Cloud tracker (
basicmachines-co/basic-memory-cloud#1536), which holds the production evidence, the original failure report, and the verification that the delete path is fixed. Details omitted here because this repository is public.