Skip to content

fix(api): separate entity and link resolution - #1192

Merged
phernandez merged 5 commits into
mainfrom
codex/split-entity-link-resolution
Aug 5, 2026
Merged

fix(api): separate entity and link resolution#1192
phernandez merged 5 commits into
mainfrom
codex/split-entity-link-resolution

Conversation

@phernandez

@phernandez phernandez commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

Separate two resolution contracts that were previously implemented behind one route:

  • keep POST /v2/projects/{project_id}/knowledge/resolve strict to the project named on
    the route
  • add POST /v2/projects/{source_project_id}/knowledge/links/resolve for source-aware
    wikilinks, including qualified cross-project targets
  • return the target project's UUID explicitly from link resolution

Addresses #1170.

Problem

The existing entity-resolution route delegated to the full cross-project LinkResolver.
That made a route whose path says “resolve inside this project” capable of escaping to
another project, and it mixed two different cache and authorization boundaries:

  • entity lookup depends on one target project's generation
  • link resolution depends on the source project, optional source path, and possibly a
    different target project

The conflation was especially sharp for hosted callers. A qualified wikilink could resolve
into another project through a route authorized and cached as the source project.

Contract

Target-scoped entity resolution

POST /v2/projects/{project_id}/knowledge/resolve now:

  • resolves only through the repository and search service bound to project_id
  • preserves same-project source_path behavior
  • returns the route project's project_external_id
  • after a local miss, rejects project::note and legacy project/note forms with HTTP 400
    when the prefix names another project
  • preserves local precedence for real same-project titles or paths, including titles containing
    :: and paths beginning with another project's name
  • points qualified callers to the link route
  • remains eligible for the project-scoped one-generation read cache

The explicit 400 is a migration guard. Returning a plain miss would let edit-style callers
interpret a cross-project lookup as “not found” and create the note in the active project.

Source-aware link resolution

POST /v2/projects/{source_project_id}/knowledge/links/resolve:

  • uses the source project as the default and as the owner of source_path
  • supports qualified cross-project targets through LinkResolver
  • returns target_project_external_id
  • is intentionally not project-read-cached because its result can depend on two project
    generations

Core does not own hosted authorization. The schema and domain documentation require hosted
callers to authorize the returned target project separately before exposing the resolved entity.

Hosted integration

The coordinated Basic Memory Cloud PR #1664
migrates web-v2 wikilinks to the new route, consumes target_project_external_id, preserves
source-project viewer access, and performs the existing second authorization against
cross-project targets. Public-share permalink resolution remains on the strict entity route.

The Cloud PR will remain draft until this Core contract lands and Cloud advances its pinned
Core revision.

Implementation

  • add typed LinkResolveRequest and LinkResolveResponse schemas
  • split LinkResolver.resolve_entity() from full resolve_link()
  • route entity and link resolution through their explicit contracts
  • guard both namespace and legacy path-qualified migration forms without shadowing local paths
  • document lookup, caching, and hosted-authorization ownership in docs/DOMAIN_MODEL.md
  • add regressions for strict qualified-reference rejection, local precedence, cross-project
    target identity, source-path misses, and same-project behavior

Verification

  • original focused entity/link API contract: 6 passed
  • LinkResolver and v2 knowledge-router suites after review fix: 117 passed
  • real Redis resolve-cache invalidation regression: 1 passed
  • MCP client plus read/edit/move/delete suites: 197 passed
  • targeted Postgres resolution regressions after review fix: 3 passed
  • just fast-check: passed
  • git diff --check: passed

Rollout

  1. Merge this Core contract.
  2. Advance Basic Memory Cloud's pinned Core revision.
  3. Re-run the Cloud hosted wikilink suite and merge the companion.
  4. Close Separate project-scoped entity resolution from cross-project wikilink resolution #1170 after both sides are deployed through their normal release paths.

Signed-off-by: phernandez <paul@basicmachines.co>

@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: 4e92fca690

ℹ️ 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/api/v2/routers/knowledge_router.py Outdated
Signed-off-by: phernandez <paul@basicmachines.co>

@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: 16aa361afe

ℹ️ 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/api/v2/routers/knowledge_router.py Outdated
Signed-off-by: phernandez <paul@basicmachines.co>

@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: 94079b0d97

ℹ️ 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/api/v2/routers/knowledge_router.py Outdated
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

Latest head: d91ed0f0

The qualified-reference guard now runs before non-strict fuzzy fallback, while exact local identifiers still take precedence. Fresh verification: 117 LinkResolver/API tests, 3 targeted Postgres contract tests, just fast-check, and git diff --check.

@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: d91ed0f003

ℹ️ 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/api/v2/routers/knowledge_router.py Outdated
Comment thread src/basic_memory/api/v2/routers/knowledge_router.py
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

Latest head: 878a1986

Qualified-reference detection now uses the same alias/bracket/whitespace normalization as LinkResolver before exact-local-first resolution. UUID wikilinks remain outside the supported product contract and are intentionally not expanded by this PR.

Fresh verification: 117 LinkResolver/API tests, 3 targeted Postgres contract tests, just fast-check, and git diff --check.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 878a1986ec

ℹ️ 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

Copy link
Copy Markdown
Member Author

Final gate update for 878a1986: the first semantic job failed only because Hugging Face returned repeated HTTP 429 responses while FastEmbed downloaded BAAI/bge-small-en-v1.5. The failed job was rerun unchanged and passed. Current result: 25 successful checks, 2 expected skips, CLEAN, MERGEABLE, authenticated Codex +1, and zero unresolved review threads.

@phernandez
phernandez merged commit de4589d into main Aug 5, 2026
46 of 47 checks passed
@phernandez
phernandez deleted the codex/split-entity-link-resolution branch August 5, 2026 02:59
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.

Separate project-scoped entity resolution from cross-project wikilink resolution

1 participant