Skip to content

fix(core): v0.24.0 live-run defects (project lookup, build_context, wiki, man, search scope, examples) - #1637

Merged
phernandez merged 18 commits into
mainfrom
fix/v024-live-run-defects
Sep 30, 2026
Merged

phernandez merged 18 commits into
mainfrom
fix/v024-live-run-defects

Conversation

@phernandez

Copy link
Copy Markdown
Member

Summary

This fixes the release-relevant defects from the v0.24.0 live regression run. There is one commit per issue.

Fixes #1624
Fixes #1632
Fixes #1628
Fixes #1626
Fixes #1625
Fixes #1629
Fixes #1630
Fixes #1633

Commits

fix(core): match config entries by permalink; unknown names route local (#1624, #1632)

At startup, synchronize_projects rewrites config keys to permalinks, but the database keeps the display name. Code then looked config up by that display name with an exact dict.get:

  • is_locally_syncable missed, so the startup index and the file watcher skipped a project named Gamma-Mixed.
  • get_project_mode missed and called the project cloud.

The fix:

  • BasicMemoryConfig.project_entry(name) matches a project by its name or its permalink.
  • get_project_mode, is_locally_syncable, the workspace lookup and the MCP routing lookup all go through it.
  • An unknown name still defaults to cloud, so cloud-only projects stay reachable. But when there are no credentials, no client factory, and no explicit --cloud, the local API answers instead, so the error is "project not found".

fix(mcp): keep man on the bundled manual without a manual project (#1628)

man fell back to a project named manual, which only exists in cloud workspaces. Now:

  • If no manual project is reachable, an unbundled page returns "No manual entry for X".
  • Query mode says it needs the manual project.
  • A named project, a client factory, a configured manual project, or cloud credentials keep the old path.

fix(core): resolve memory:// titles exactly and keep misses empty (#1626)

I reproduced this on main: memory://zzq-nonexistent returned an unrelated note. There were two causes:

  • build_context's fallback called the link resolver with use_search=True and took the top fuzzy hit.
  • Routing makes the path project-qualified (main/Cache Layer Design). The title and file-path lookups never stripped that prefix, so a title URL could only resolve through the fuzzy search.

The fix:

  • resolve_link runs its exact lookups on the own-project remainder before any fuzzy match.
  • The fuzzy step moves into _search_best_match and runs last.
  • build_context resolves with use_search=False.

fix(core): stamp local accepted journal rows once their file work settles (#1625)

The wiki projector defers until every accepted change it replays is materialized. Cloud marks journal rows as materialized from its materialization job (wiki_project_changes.py), but core never called mark_accepted_note_change_materialized, so a local project with any API write stayed partial forever. The local provider now marks the row with the same rule cloud uses:

  • after a write reaches any terminal status except conflict, since a conflict means a disk edit the user still has to resolve;
  • after an inline delete.

fix(core): let an exact non-Markdown file path win over stem permalinks (#1629)

notes/foo.txt turned into the permalink candidate notes/foo and matched foo.md. An identifier with a non-Markdown extension now tries its exact file_path first.

fix(mcp): match search project scopes by permalink like routing does (#1630)

The case-sensitive check in _matches_constrained_project and in projects= selection now compares permalinks, the way project routing already does (#1388).

fix(mcp): make model-facing tool-call examples callable as written (#1633)

About 100 examples in tool descriptions, docstrings and error guidance were broken:

  • legacy project-first positional calls, such as read_note("{project}", "{identifier}");
  • the retired list_projects();
  • search_notes(kind=...);
  • the --search-type flag in the search-notes(3) CLI synopsis.

The fix:

  • Every example now names a registered tool and passes everything after its first argument by keyword.
  • tests/mcp/test_model_facing_call_examples.py scans the string literals in the MCP tools, prompts and resources. It fails on a positional example, a keyword the tool doesn't accept, or a retired tool name.

docs(core)

Not in this PR

Tests

Each fix has a regression test that fails on main:

  • test_config.py: permalink-keyed entry
  • test_async_client_modes.py and test_unknown_project_routing.py: unknown project
  • test_tool_posix.py: man
  • test_link_resolver.py and test_tool_build_context.py: titles and misses
  • test_wiki_after_api_writes.py: wiki after create, edit and delete
  • test_link_resolver.py: .txt vs .md
  • test_search_notes_multi_project.py: case-insensitive scope
  • test_model_facing_call_examples.py: the examples guard

just fast-check is clean and just man-regen reports no drift.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig

phernandez and others added 9 commits September 29, 2026 18:15
Startup reconciliation rewrites config keys to permalinks while the
database keeps each project's display name, and config was then looked up
by that name with an exact dict get. A project named `Gamma-Mixed` (key
`gamma-mixed`) failed is_locally_syncable, so the startup index and the
watcher skipped it, and get_project_mode treated it as cloud.

BasicMemoryConfig.project_entry matches by name or permalink, and
get_project_mode, is_locally_syncable and the workspace and routing
lookups use it.

An unknown name still defaults to cloud mode so cloud-only projects stay
reachable, but with no cloud route available the local API now answers,
so a typo or a deleted project reports "not found" instead of asking for
cloud credentials.

Fixes #1624
Fixes #1632

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
man fell back to a project named "manual" for unbundled pages and query
mode. A local install has no such project, and routing an unknown name to
cloud turned every such call into a cloud-credentials error. When no
manual project is reachable (none named, none in config, no factory and no
cloud credentials), a page miss is "No manual entry" and query mode says
what it needs.

Fixes #1628

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
build_context answered a memory:// URL that did not name a permalink with
the top fuzzy search hit. Routing makes the path project-qualified
(main/Cache Layer Design), and the title and file-path lookups saw that
prefixed text, so a title URL could only resolve through fuzzy search and
often landed on a neighbour that links to it. A miss returned whatever the
search ranked first.

The link resolver now runs its exact lookups on the own-project remainder
before any fuzzy match, with the fuzzy step extracted so it runs last.
build_context resolves with use_search=False: a memory:// URL is an
address, and a miss returns no primary result.

Fixes #1626

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
…tles

The wiki projector defers until every accepted change it replays is
materialized. Cloud stamps materialized_at from its materialization job,
but the local runtime never called mark_accepted_note_change_materialized,
so one API or MCP write left bm wiki partial forever.

The local provider now settles the journal row after a write reaches a
terminal status other than conflict (a disk edit the user must resolve),
and after an inline file delete, matching the cloud rule.

Fixes #1625

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
Permalink candidates drop the extension, so notes/foo.txt became notes/foo
and matched the same-stem Markdown note before any file-path lookup ran.
read_content, cat and read_note returned foo.md's bytes for foo.txt. An
identifier with a non-Markdown extension now tries its exact file path
first; a miss falls through to the usual order.

Fixes #1629

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
Project routing matches names by permalink (#1388), but search scoping
compared exact strings. Under BASIC_MEMORY_MCP_PROJECT=ALPHA, a project
named alpha was filtered out of the scoped project list, so
search_all_projects and projects= saw no projects; projects=["BETA"] was
refused as unknown. Both now compare permalinks.

Fixes #1630

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
Error guidance, docstrings and tool descriptions still showed legacy
project-first positional calls (read_note("qa", "notes/x") reads a note
named qa from project notes/x), a retired list_projects() tool, and a
search_notes kind= parameter that does not exist. The #1603 audit was a
manual pass with no test behind it.

Every example now names a registered tool and passes everything after its
first argument by keyword. A new test scans the string literals in the
MCP tools, prompts and resources and fails on a positional example, an
unknown keyword, or a retired tool name. The search-notes(3) CLI synopsis
listed --search-type, which bm tool search-notes does not accept.

Fixes #1633

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
The module docstring claimed created_by/updated_by cannot be set through
note content. That holds only where the runtime supplies an identity and
stamps over them; on local runtimes the keys pass through as written,
which is the intended behavior (#1634).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 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-30T02:00:47.552230Z 9535677 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.

… fallback

The underscore relation test used a relation-shaped URL without the
target's project prefix, which resolved only through the fuzzy search
removed for #1626. Assert the real relation address resolves and the
prefix-less form is a miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
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: 6d0fe2f631

ℹ️ 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/services/link_resolver.py Outdated
…lookup

file_path is project-relative, so a project-qualified
<project>/components/foo.txt missed the exact lookup and fell through to
the extension-less permalink candidates that match foo.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@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: 8e84981cad

ℹ️ 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/services/link_resolver.py Outdated
Comment thread src/basic_memory/index/note_content_materialization.py
phernandez and others added 2 commits September 29, 2026 19:19
…retry

Workspace-scoped routes qualify identifiers as <workspace>/<project>/Title.
The own-project retry only split the first segment and compared the
workspace slug with the project permalink, so it was skipped, and with
build_context no longer fuzzy matching, the title returned nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
Startup recovery rebuilds its materialization request from note_content,
so it carries no journal position and never settled the accepted row; a
note recovered on restart and not written again kept the wiki partial.
Recovery now settles the note's journal rows up to the recovered
db_version when the write ends in any status but conflict.

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

ℹ️ 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/services/link_resolver.py Outdated
Resource entities (text, images, PDFs) are indexed with permalink NULL, so
a permalink candidate built by dropping .txt could only ever match the
same-stem Markdown note. Rather than special-casing an exact file-path
probe ahead of the candidates in LinkResolver, the shared candidate
builder now keeps a real non-Markdown extension, and the ordinary lookup
order reaches the file: exact file path, then the own-project remainder
retry, whose prefix match normalizes by permalink (so TEST-PROJECT/... and
workspace prefixes work too). The builder is shared, so the bulk link
resolver and OKF rendering no longer turn [[notes/foo.txt]] into foo.md.

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

ℹ️ 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/services/link_resolver.py Outdated
resolve_entity, the path API entity reads use, ran one exact pass on the
raw identifier and never retried the own-project remainder, so a routed
<project>/components/foo.txt (resource, permalink NULL) or
<project>/<Title> missed under strict resolution. Both entrypoints now
share _resolve_in_current_project: exact lookups, then the remainder,
then fuzzy search only for non-strict callers that ask for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@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: 62664938e4

ℹ️ 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/utils.py
The bulk resolver declined its parsed-prefix fallback when the prefix
named the current project, so [[<project>/assets/a.txt]] (a resource with
no permalink) and [[<project>/Title]] stayed unresolved on every reindex.
It now runs the same strict lookups on the project-relative remainder,
matching LinkResolver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@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: 08e60d6c7e

ℹ️ 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/mcp/async_client.py Outdated
…al fallback

A caller that names a workspace asked for cloud explicitly. The unknown
project fallback to local ASGI now applies only when no workspace is
given, so a cloud request without credentials still fails instead of
reading a same-named local project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@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: 780ba311ba

ℹ️ 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/services/bulk_link_resolver.py Outdated
LinkResolver and the bulk resolver each grew their own own-project prefix
retry, and the bulk one handled only <project>/..., so a workspace route's
[[<workspace>/<project>/assets/image.png]] stayed unresolved. utils
own_project_remainder now strips <workspace>/<project>/ or <project>/ by
permalink, and both resolvers call it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BF4TkgAJpVrgwzrhemkdig
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 95356777cc

ℹ️ 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 8018721 into main Sep 30, 2026
36 checks passed
@phernandez
phernandez deleted the fix/v024-live-run-defects branch September 30, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment