Skip to content

fix(lint): preserve Obsidian heading, block, and embed link syntax#180

Open
Aldominguez12 wants to merge 1 commit into
VectifyAI:mainfrom
Aldominguez12:fix/lint-obsidian-link-syntax
Open

fix(lint): preserve Obsidian heading, block, and embed link syntax#180
Aldominguez12 wants to merge 1 commit into
VectifyAI:mainfrom
Aldominguez12:fix/lint-obsidian-link-syntax

Conversation

@Aldominguez12

Copy link
Copy Markdown
Contributor

Problem

The wikilink regex in openkb/lint.py captured the whole [[...]] body as a single page target. Valid Obsidian syntax therefore never matched a known target:

  • [[page#Heading]] / [[page#^block]] — heading and block links
  • ![[file.png]] / [[report.pdf]] — attachment embeds and links
  • [[#Heading]] — same-page fragment links
  • ![[concepts/x]] — note embeds

Consequences:

  • openkb lint --fix destroyed valid links: strip_ghost_wikilinks demoted them to plain text. A wiki-wide sweep silently mangled hand-written notes in explorations/ — a data-loss path.
  • openkb lint reported them all as broken links (false positives).
  • Note embeds created no graph edges in visualize and could produce false orphans.

Fix

  • Parse the embed marker, target, fragment, and alias as named regex groups.
  • Validate only the page target; fragments survive fuzzy canonical rewrites ([[concepts/Gist_Memory#Notes]][[concepts/gist-memory#Notes]]).
  • Pass through attachment embeds/links (extension whitelist) and same-page [[#Heading]] links untouched.
  • Treat note embeds ![[concepts/x]] as regular page links: validated, rewritten keeping the !, counted as incoming links for orphan detection, and picked up as graph edges by visualize (which reuses _extract_wikilinks).

Verification

  • New TestObsidianSyntax coverage: heading/block/fragment preservation through direct and fuzzy matches, ghost demotion without ! residue, attachment/note embed handling, and a regression test asserting a hand-written explorations/ note survives a wiki-wide lint --fix byte-for-byte.
  • pytest tests/test_lint.py tests/test_lint_cli.py tests/test_visualize.py tests/test_chat_session.py tests/test_remove.py green; ruff check/format and mypy openkb/lint.py clean.

🤖 Generated with Claude Code

@Aldominguez12 Aldominguez12 force-pushed the fix/lint-obsidian-link-syntax branch from 9d33a56 to 2775a3a Compare July 12, 2026 05:30
The single-capture wikilink regex treated [[page#Heading]],
[[page#^block]], ![[file.png]] and [[report.pdf]] as whole page
targets. None of them can ever match a known target, so lint --fix
(strip_ghost_wikilinks) demoted valid Obsidian links to plain text —
silently destroying hand-written notes in explorations/ on a full
sweep — and find_broken_links reported them all as broken.

- Parse the embed marker, target, fragment and alias as named groups.
- Validate only the page target; fragments survive fuzzy canonical
  rewrites ([[concepts/Gist_Memory#Notes]] -> [[concepts/gist-memory#Notes]]).
- Pass through attachment embeds/links (extension whitelist) and
  same-page [[#Heading]] links untouched.
- Treat note embeds ![[concepts/x]] as regular page links: validated,
  rewritten keeping the embed marker, counted as incoming links for
  orphan detection and as graph edges in visualize.

Adds regression coverage including a hand-written explorations/ note
that must survive a wiki-wide lint --fix byte-for-byte.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Aldominguez12 Aldominguez12 force-pushed the fix/lint-obsidian-link-syntax branch from 2775a3a to e8900ac Compare July 12, 2026 05:49
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.

1 participant