Repository navigation
[BUG] Wikilinks naming a top-level note by filename never resolve: the .md fallback is gated on "/" in the identifier #1253
Description
Summary
A wikilink that names a top-level note by its filename stem cannot reach the file-path
fallback in LinkResolver._resolve, because the .md-appending step is gated on the identifier
containing a /. A note in a subdirectory resolves; the same note at the vault root does not.
This matters for vaults edited directly on disk, where the natural way to link is by filename,
and where a note's title: (and therefore its derived permalink) is a descriptive human string
rather than a slug of the filename.
Where
src/basic_memory/services/link_resolver.py on main (401ed6bf), resolution order:
- exact permalink
- exact title
- exact file path —
get_by_file_path(session, clean_text, …) - file path +
.md— only whennot clean_text.endswith(".md") and "/" in clean_text - fuzzy search (non-strict only)
Two gaps combine:
- (a) Step 4's
"/" in clean_textguard means a bare identifier such asalpha-noteis never
retried asalpha-note.md.notes/alpha-noteis. There is nothing about a root-level note that
makes the.mdretry less correct — the guard appears to be guarding against something else. - (b) No
_↔-normalization anywhere in steps 3–4, soalpha_note.mdon disk is
unreachable from[[alpha-note]]even if the.mdwere appended.
Reproduce
mkdir -p /tmp/bm-repro && cd /tmp/bm-repro
cat > alpha_note.md <<'EOF'
---
title: Alpha — a descriptive human title
type: note
---
Body.
EOF
cat > beta_note.md <<'EOF'
---
title: Beta
type: note
---
Links to [[alpha-note]] and [[alpha_note]].
EOF
basic-memory project add repro /tmp/bm-repro
basic-memory sync -p reproExpected: both links resolve to alpha_note.md.
Observed: the relations stay unresolved.
Step 1 misses because the permalink derives from the descriptive title, not the filename. Step 2
misses for the same reason. Step 3 misses because the stored file_path is alpha_note.md, not
alpha-note. Step 4 is skipped — no /.
Moving the same note to notes/alpha_note.md and linking [[notes/alpha-note]] still fails on
(b); linking [[notes/alpha_note]] succeeds via step 4, which is the asymmetry this report is
about.
Impact
In a ~350-relation vault where notes carry descriptive titles and links are written as filenames,
this left the great majority of relations unresolved. sync reports success and nothing is
logged, so the graph looks healthy while having almost no edges. The failure is only visible if
you go looking for it.
Suggested fix
Drop the "/" condition on step 4 — retry f"{clean_text}.md" for bare identifiers too — and
normalize _↔- (and case) when comparing against stored file_path. Both are additive: they
only turn current misses into hits, and step 4 already runs after the exact permalink/title/path
attempts, so nothing that resolves today changes.
If strict-by-design is preferred over either change, then emitting one warning per unresolved
link at sync time would at least make the failure visible.
Verification
Symptom reproduced on 0.22.1 (current PyPI release, macOS 15, Python 3.14, local file
backend). The resolution order and the "/" guard above were read from main at 401ed6bf;
I have not run main, so the exact step numbering there is from source, not from a live trace.
Filed separately from #1015 (forward-reference back-resolution), which the v0.23 change review
lists as fixed by #1161/#1159 — that is a different failure and I am not re-reporting it.
Activity
phernandez commentedon Aug 13, 2026
@codex investigate this issue
chatgpt-codex-connector commentedon Aug 13, 2026
Summary
.mdfallback even when the identifier contains no directory separator. Exact permalink, title, and file-path matches retain precedence.alpha_note,alpha-note, andALPHA-NOTEresolve to a root-levelalpha_note.mdwhose descriptive title and permalink do not match its filename.3e836c3(fix(core): resolve root wikilinks by filename) and recorded PR metadata with the semantic titlefix(core): resolve root wikilinks by filename.Testing
pytest tests/services/test_link_resolver.py tests/services/test_bulk_link_resolver.py -q --no-cov— 58 passed.ruff check src/basic_memory/repository/entity_repository.py src/basic_memory/services/link_resolver.py src/basic_memory/services/bulk_link_resolver.py tests/services/test_link_resolver.py tests/services/test_bulk_link_resolver.pyruff format --check src/basic_memory/repository/entity_repository.py src/basic_memory/services/link_resolver.py src/basic_memory/services/bulk_link_resolver.py tests/services/test_link_resolver.py tests/services/test_bulk_link_resolver.pygit diff --checkjust fast-check— unavailable because the environment does not have thejustexecutable installed.uv run ty check src tests test-int— change-related diagnostics are resolved; the command still reports four unresolved imports because the optionalpymilvusdependency is not installed.View task →