fix(markdown): resolve wikilinks vault-wide when sibling resolution misses - #2875
fix(markdown): resolve wikilinks vault-wide when sibling resolution misses#2875BaeHyunJae wants to merge 1 commit into
Conversation
- 어휘적으로 해소된 경로가 부재한 위키링크만 스캔 루트 전역 파일명 색인으로 재해소하고, 인라인·참조 링크와 실존 형제 대상은 종전 상대 의미론 유지. - 색인은 detect._SKIP_DIRS 와 닷 디렉터리를 배제하고 NFC 정규화로 대조하며, 동명 충돌은 얕은 깊이 우선 뒤 사전식 타이브레이크로 해소. - 적중은 실파일이므로 target_file 스탬프와 기존 remap 경로가 전체·증분 실행 모두에서 정규화하고, 미적중은 종전대로 매달린 간선 유지.
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds Obsidian-style vault-wide wikilink resolution to the markdown extractor: _resolve_markdown_link now takes a wikilink flag that, when a lexically-resolved sibling path doesn't exist, falls back to a per-scan-root basename index (_vault_lookup/_build_link_index) with shallowest-then-lexicographic tie-breaking and NFC normalization. Only wikilinks opt in — inline and reference-style links keep pure relative semantics. Wires _MD_LINK_INDEX_CACHE.clear() into extract() alongside the other per-run cache resets, and adds coverage for cross-folder, path-qualified, and root-wins-collision cases.
Worth a look
- Markdown wikilink resolution reads a process-global active root —
graphify/extractors/markdown.py:112· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Missing wikilinks now retarget to unrelated same-basename files under scan root —
graphify/extractors/markdown.py:221· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Wikilink display-text alias not stripped before resolution —
graphify/extractors/markdown.py:374· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1849 functions depend on the 735 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 473 callers, 42 callees - new:
_rebuild_code()— 98 callers, 51 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_js()— 80 callers, 3 callees - new:
dispatch_command()— 2 callers, 117 callees - new:
_get_extractor()— 26 callers, 6 callees - new:
run_pipeline()— 8 callers, 13 callees - new:
extract_markdown()— 13 callers, 8 callees - …and 23 more — each is listed as a finding
Verification — 1849 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: 1706 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify extract.
The verifier did not have enough to check extract, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `cache_root` is annotated `Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify extract\_markdown.
The verifier did not have enough to check extract\_markdown, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_resolve\_markdown\_link.
The verifier did not have enough to check \_resolve\_markdown\_link, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `source_dir` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 grounded finding(s) anchored inline below; 30 more finding(s) on lines outside this diff (see the check run).
| return Path(os.path.normpath(str(hit))) | ||
| return resolved | ||
|
|
||
| def extract_markdown(path: Path) -> dict: |
There was a problem hiding this comment.
extract_markdown()
fans out to 8 callees (efferent coupling); 13 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
…isses (#2875) An Obsidian-style [[wikilink]] resolves to a note anywhere in the vault by basename, but resolution only tried sibling files, so cross-folder links were silently lost. Add a vault-wide fallback that fires only when relative/sibling resolution misses, with a deterministic tiebreak on ambiguous basenames (shallowest path, then lexicographic root-relative path) and a once-per-scan index so it stays O(N).
Ships this cycle: the LLM-resilience batch — hollow-response same-chunk retry (#2880), reasoning-first JSON recovery (#2882), deliberately-declined data JSON not counted as failed (#2879); extractor fixes — C++ nested types + C++/CLI (#2876), markdown vault-wide wikilinks (#2875); export fixes — control-char no longer aborts export (#2897), graph.html restored for large graphs (#2853); and the --no-dedup opt-out (#2881). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Shipped in v0.9.48 via authorship-preserving cherry-pick. Thanks @BaeHyunJae! Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.48 |
Problem
A
[[wikilink]]is resolved lexically against the linking file's directory (_resolve_markdown_link), but Obsidian-style vaults resolve wikilinks by vault-global filename lookup: a note in a subfolder linking[[hub]]means the root-levelhub.md. Today the lexically derived target id matches no node, the #2169 remap pass rightly refuses to canonicalize a nonexistent path, and the builder drops the edge — so every cross-folder vault link is a silently lost reference. That is the same lost-edge failure #1376 fixed for hub docs, reintroduced for the vault layout graphify's own Obsidian support targets.Fix
When a wikilink's lexically resolved path does not exist, retry it against a per-scan-root index of linkable documents:
detect._SKIP_DIRSand dot-directories — the same corpus boundary the scanner draws (and Obsidian itself does not index dot-folders); cleared at the start of eachextract()run beside the other run-scoped caches[[folder/name]]) by full segment suffix; ties break shallowest-first then lexicographically, mirroring Obsidian's root-wins collision rule, so resolution is deterministic regardless of walk orderA hit is a real file, so the existing
target_filestamp and the #2169 remap canonicalize the edge on full and incremental runs alike — no new remap machinery. A miss stays dangling exactly as before. The fallback is scoped deliberately:[text](path.md)and reference-style links keep pure relative semantics, where a missing target is an authoring error rather than an alternate resolution orderextract()run — a directextract_markdown()call has no scan root and behaves exactly as beforeTests
Six new tests in
tests/test_languages.py(tmp_path style, end-to-end throughextract()): subfolder→root resolution, path-qualified suffix match, root-wins collision, sibling-shadows-fallback, inline-links-unchanged, and NFD-link-to-NFC-file normalization. Full suite passes (the one pre-existing failure,test_hermes_skill_destination_posix_uses_home, fails identically on a clean checkout of v8 on Windows and is unrelated).