Skip to content

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

Closed

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 + .md — only 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.

Activity

  1. phernandez commented on Aug 13, 2026

    @phernandez
    Member

    @codex investigate this issue

  2. chatgpt-codex-connector commented on Aug 13, 2026

    @chatgpt-codex-connector

    Summary

    • Fixed root-level filename-stem wikilinks by applying the .md fallback even when the identifier contains no directory separator. Exact permalink, title, and file-path matches retain precedence.
    • Added a case-insensitive file-path fallback that treats underscores and hyphens equivalently, while declining ambiguous normalized matches.
    • Kept bulk relation resolution consistent with the single-link resolver by indexing normalized paths and requiring a unique match.
    • Added regression coverage proving alpha_note, alpha-note, and ALPHA-NOTE resolve to a root-level alpha_note.md whose descriptive title and permalink do not match its filename.
    • Committed the changes with DCO sign-off as 3e836c3 (fix(core): resolve root wikilinks by filename) and recorded PR metadata with the semantic title fix(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.py
    • ✅ ruff 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.py
    • ✅ git diff --check
    • ⚠️ just fast-check — unavailable because the environment does not have the just executable installed.
    • ⚠️ uv run ty check src tests test-int — change-related diagnostics are resolved; the command still reports four unresolved imports because the optional pymilvus dependency is not installed.

    View task →

  3. added this to the v0.23 milestone on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions