Skip to content

fix(markdown): resolve [[wikilinks]] vault-wide, not sibling-only - #2415

Open
Phantomn wants to merge 1 commit into
Graphify-Labs:v8from
Phantomn:fix/wikilink-vault-wide-resolution
Open

fix(markdown): resolve [[wikilinks]] vault-wide, not sibling-only#2415
Phantomn wants to merge 1 commit into
Graphify-Labs:v8from
Phantomn:fix/wikilink-vault-wide-resolution

Conversation

@Phantomn

@Phantomn Phantomn commented Aug 3, 2026

Copy link
Copy Markdown

Fixes #2413.

Problem

[[wikilinks]] were resolved sibling-only. Obsidian resolves [[name]] against
the whole vault by filename, so in a vault organized by note type (Concepts/,
Entities/, Guides/, Maps/) every cross-folder link missed: no target_file
stamp was written, the #2169 remap pass had nothing to canonicalize, and
build_from_json dropped the edge.

Same failure shape as #2211 (fixed in v0.9.28 for the incremental case), but here
the target is not in the sibling directory at all, so the stamp is never produced
in the first place.

Approach

Sibling resolution still runs first and still wins. Only a wikilink whose sibling
guess missed is stamped with its bare name; extract()'s post-pass — the
first point that knows every in-root file — builds a stem -> [rel path] index
and repoints those edges.

  • Path-qualified links are untouched. [text](./other.md) is relative by
    spec, never gets the stamp, and never enters the new code path. Covered by a
    dedicated non-regression test.
  • Deterministic ambiguity resolution: the linking note's own folder first
    (mirrors Obsidian), then shallowest path, then lexicographic.
  • Transient stamp: wikilink_name is popped in the same pass, so graph.json
    shape is unchanged.
  • Broken links still dangle: a [[name]] pointing at a file that exists
    nowhere behaves exactly as before.
  • No public interface changes.

Measured impact

Real Obsidian vault, 499 notes, against v8 HEAD (0.9.32):

before after authored baseline
valid references 1,293 2,323 2,339
recall 55.3% 99.3% 100%
graph edges 7,623 8,470

The residual 16 are links to files that genuinely do not exist in the vault.

Tests

4 new cases in tests/test_languages.py:

  • test_markdown_wikilink_stamps_name_when_sibling_missing — stamp appears only
    for the non-sibling target; the sibling one resolves normally
  • test_markdown_path_qualified_link_is_not_stamped — relative-link
    non-regression
  • test_extract_resolves_wikilinks_across_folders — cross-folder link resolves,
    a genuinely missing target still dangles, no stamp leaks to the caller
  • test_extract_wikilink_prefers_same_folder_on_ambiguity — same-stem tie-break

Full suite is green against the same baseline as v8 HEAD: 3,891 passed with
the identical 16 pre-existing test_skillgen.py failures present before and after
this change (they come from a shallow clone lacking the skill sources, unrelated
to this patch).

…aphify-Labs#2413)

Obsidian resolves [[name]] against the whole vault by filename, but
_resolve_markdown_link only ever tried source_dir / name. In a vault that
splits notes into type folders (Concepts/, Entities/, Guides/, Maps/), every
cross-folder wikilink therefore missed: no target_file stamp was written, the
Graphify-Labs#2169 remap pass had nothing to canonicalize, and build_from_json dropped the
edge. Same failure shape as Graphify-Labs#2211, but the target is not in the sibling
directory at all, so the stamp is never produced in the first place.

Sibling resolution still runs first and still wins, so path-qualified links
([text](./other.md), which are relative by spec) keep exactly their current
semantics and never enter the new path. Only a wikilink whose sibling guess
missed is stamped with its bare name; extract()'s post-pass is the first point
that knows every in-root file, so it builds a stem index there and repoints
those edges. Ambiguity resolves deterministically: the linking note's own
folder first (mirrors Obsidian), then shallowest path, then lexicographic. The
stamp is transient and popped in the same pass, so graph.json shape is
unchanged, and a link to a file that exists nowhere still dangles as before.

Measured on a 499-note Obsidian vault: valid references 1,293 -> 2,323 against
an authored baseline of 2,339 (55.3% -> 99.3% recall); graph edges 7,623 ->
8,470. The residual 16 are links to files that genuinely do not exist.

Tests: 4 new cases covering the stamp, the path-qualified non-regression,
cross-folder resolution with a still-dangling broken link, and same-folder
preference on an ambiguous stem.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR adds vault-wide resolution for Obsidian-style [[name]] wikilinks in the markdown extractor. Previously such links were resolved only against the linking note's sibling directory; now when a sibling match isn't found, markdown.py stamps the edge with a wikilink_name, and a new post-pass in extract.py repoints those edges by building a filename index across all in-root document files (with same-folder/shallowest/lexicographic tiebreaking). Path-qualified markdown links keep their existing relative-resolution behavior, and the transient stamp is removed before results are returned. The surface area includes the new add_edge/add_link parameters in markdown.py, the new resolution block in extract.py's extract(), and several new tests in tests/test_languages.py covering stamping, path-qualified links, cross-folder resolution, and same-folder ambiguity preference.

Worth a look

  • name_index built only from remap_paths, missing unchanged in-root files on incremental runsgraphify/extract.py:4983 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • wikilink_name stamp left on edges for source files outside rootgraphify/extract.py:4987 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1498 functions depend on the 633 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: extract() — 361 callers, 29 callees

Verification — 1498 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1370 function(s) in the blast radius were not formally verified this run

· 1 more finding(s) on lines outside this diff (see the check run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown [[wikilinks]] resolve sibling-only, dropping every cross-folder link in note-type vaults

1 participant