Skip to content

[BUG] Wikilinks naming a top-level note by filename never resolve: the .md fallback is gated on "/" in the identifier #1253

Description

@EricXu-0805

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:

  1. exact permalink
  2. exact title
  3. exact file path — get_by_file_path(session, clean_text, …)
  4. file path + .mdonly when not clean_text.endswith(".md") and "/" in clean_text
  5. fuzzy search (non-strict only)

Two gaps combine:

  • (a) Step 4's "/" in clean_text guard means a bare identifier such as alpha-note is never
    retried as alpha-note.md. notes/alpha-note is. There is nothing about a root-level note that
    makes the .md retry less correct — the guard appears to be guarding against something else.
  • (b) No _- normalization anywhere in steps 3–4, so alpha_note.md on disk is
    unreachable from [[alpha-note]] even if the .md were 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 repro

Expected: 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions