Skip to content

feat(core): index Markdown links to project files - #1514

Merged
phernandez merged 2 commits into
mainfrom
feat/1246-markdown-relations
Sep 8, 2026
Merged

phernandez merged 2 commits into
mainfrom
feat/1246-markdown-relations

Conversation

@phernandez

Copy link
Copy Markdown
Member

Why

Ordinary Markdown links currently navigate in editors but contribute no Basic Memory graph edges. This implements the latest direction in #1246: recognize links to paths inside the project, similarly to inline wikilinks, without a syntax mode or an export conversion.

Refs #1246.

What Changed

  • Markdown links and reference-style links produce links_to relations to project files.
  • Relative paths are resolved against the containing note; / starts at the project root. Percent-encoded filenames are decoded and fragment/query suffixes do not alter the target file.
  • External URLs, images, code, fragment-only links and paths escaping the project do not mint relations.
  • Authored Markdown and bm_parse_semantics: false are preserved.

Implementation Details

The entity parser consumes MarkdownIt's parsed link tokens and stores a normalized project-root path as the relation target. Both ordinary and bulk relation resolution treat rooted targets as exact file identities, without title, permalink, filename alias or cross-project fallback. Missing paths remain unresolved for later index passes.

No schema migration or second graph representation is needed. docs/MARKDOWN_RELATIONS.md documents the behavior and its limits.

Testing

  • uv run pytest tests/markdown tests/services/test_link_resolver.py tests/services/test_bulk_link_resolver.py tests/services/test_markdown_path_resolution.py test-int/mcp/test_markdown_path_relations_integration.py -q --no-cov: 390 passed.
  • BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest test-int/mcp/test_markdown_path_relations_integration.py tests/services/test_markdown_path_resolution.py tests/services/test_bulk_link_resolver.py -q --no-cov: 10 passed.
  • The real MCP/API integration creates both notes, checks the stored resolved edge and exact bulk lookup, and verifies the original Markdown link remains in the source file. The resolved-edge assertion was added afterward and rerun successfully on SQLite.
  • just fast-check, just doctor, and git diff --check: passed.

Risks / Follow-ups

Existing notes gain these edges on their next edit or reindex. Rooted relation targets now mean exact project file paths, so a rooted spelling does not fall back to a similarly named title or permalink. Ordinary Markdown links are untyped links_to edges; typed relations still use wikilinks. This change does not claim full OKF conformance or implement bundle import/export.

Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez phernandez added this to the v0.24.0 milestone Sep 8, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T04:51:14.679686Z 68f97ab Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a086cfbf87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/markdown/entity_parser.py
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

68f97ab fixes rooted Markdown self-links in eager note preparation and adds a real MCP write regression.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 68f97abf76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@phernandez
phernandez merged commit 4ed4501 into main Sep 8, 2026
36 checks passed
@phernandez
phernandez deleted the feat/1246-markdown-relations branch September 8, 2026 13:36
phernandez added a commit that referenced this pull request Sep 16, 2026
… time

A Markdown link was the only relation whose meaning depended on where the
parser read the bytes: the parser derived the note's project path from the
filesystem path with relative_to and turned the href into a rooted path at
parse time. Content parsed from anywhere outside the project root raised
(cloud reads a note from object storage into a temp file), and any other
temporary location gave every link the wrong base.

Wikilinks are recorded as authored and resolved later against database
state. Path links now behave the same way:

- The parser stores the path as the author wrote it: ../guides/Guide.md,
  ./same.md (a bare same.md gets the ./ mark so the stored target says it
  is a path and not a title), or a rooted /root.md. parse() takes no
  source path.
- Both resolvers detect a path target (/, ./, ../) after wikilink
  normalization and resolve it against the note's own project path with
  resolve_project_path. Exact file only; no title, permalink, alias or
  cross-project fallback; a path that climbs past the root names nothing.
  Path-shaped wikilinks such as [[../x.md]] follow the same rule.
- Background resolution keys targets by RelationTargetRequest(link_text,
  source_path). Identity targets carry no source and resolve once for
  every note; path targets are keyed by their source note, so ./Guide.md
  from two folders resolves to two files in one pass. Source paths are
  loaded with one find_by_ids call only when a batch contains a path
  target.
- The write-time self-link check resolves the authored path against the
  note's own path.

Existing relation rows for Markdown links are rewritten to the authored
form on the note's next edit or reindex, as the docs already state.

Refs #1514

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YW9ysxugGGBCNEGzsxtFV
Signed-off-by: phernandez <paul@basicmachines.co>
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