Commit Graph

228 Commits

Author SHA1 Message Date
Dennis Hempel 4fd9cae293 fix(mcp): resolve write_note overwrite conflicts by file_path strictly
`write_note(overwrite=True)` caught the 409 from create_entity and then
called `knowledge_client.resolve_entity(entity.permalink)` with the
default `strict=False`. In workspace-prefixed palaces the client-built
permalink omits the workspace slug, so exact permalink lookup misses
and the fuzzy fallback could pick an orphan row that shares tokens with
the canonical permalink. The update then wrote to the orphan, leaving
the canonical row stale. On the next overwrite the permalink uniqueness
check in `_resolve_schema_permalink` found duplicate rows and minted
`-1`/`-2` suffixes on the canonical entity, accumulating orphans on
every re-synthesis run.

The 409 came from a `file_service.exists(file_path)` check in
`prepare_create_entity_content`, so the file_path is the authoritative
key for the canonical row — no fuzzy matching needed. Resolve by
file_path with `strict=True`, POSIX-normalized so Windows clients send
the form the server stores.

Adds a regression test that spies on `resolve_entity` and asserts the
identifier and `strict` flag, plus checks that no `-1`/`-2` suffix is
minted under the canonical permalink.

Reported in basic-memory-bug-report Issue 1.

Signed-off-by: Dennis Hempel <uhg.dennnis@gmail.com>
2026-05-28 09:26:38 +02:00
Drew Cain b109b7337f fix(mcp): attach local state to one workspace project row (#854)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-05-26 10:54:44 -05:00
Paul Hernandez 5a34a420c9 fix(mcp): preinitialize local ASGI database (#838)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-25 15:57:00 -05:00
Drew Cain 5ae8a733ea fix(mcp): route mixed local/cloud projects correctly (#837)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-05-23 11:31:13 -05:00
Paul Hernandez 9e3fe26a83 fix(core): purge SQLite search_index on project delete (#832)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-16 16:47:24 -05:00
Paul Hernandez 4cba7ba01c fix(core): parse prose wikilinks as inline links (#824)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-14 11:43:11 -05:00
Drew Cain 8eeec64e28 fix: basic-memory project list does not list projects from all workspaces (#822)
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-05-14 09:46:02 -05:00
Paul Hernandez c6fa185bf3 fix(mcp): route edit_note workspace-qualified permalinks (#813)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-11 12:56:26 -05:00
Paul Hernandez 415c2b3d6e feat(cli): add orphan entity command (#816)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-11 12:13:14 -05:00
Paul Hernandez df5e8d805f fix(mcp): centralize workspace permalink routing (#808)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-08 16:44:57 -05:00
Paul Hernandez 831dc1ecdc fix(mcp): make multi-project search opt-in (#807)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-08 12:54:20 -05:00
Paul Hernandez 7918e5c6bf fix(mcp): preserve workspace paths in build_context (#801)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-08 09:53:54 -05:00
Paul Hernandez f312341020 fix(mcp): add workspace routing to delete_project (#803)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-08 09:48:51 -05:00
Drew Cain 3415fd1014 fix(core): parse picoschema modifier descriptions (#796)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: phernandez <paul@basicmachines.co>
2026-05-06 12:21:35 -05:00
Paul Hernandez a661e924df feat(mcp): create projects by workspace slug (#789)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-03 19:31:55 -05:00
Paul Hernandez 05adda1502 fix(mcp): route workspace-qualified memory urls (#790)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-03 18:18:10 -05:00
Drew Cain 0a72d81bb3 fix(mcp): cap recent_activity rows with explicit truncation footer (#785)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:43:42 -05:00
Paul Hernandez b4bf14ebf7 fix(mcp): list factory projects across workspaces (#778)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-05-02 09:17:25 -05:00
Paul Hernandez 0b335476d6 fix(mcp): resolve projects by external_id, remove workspace from MCP tools (#777)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 05:15:44 -05:00
Paul Hernandez 799dd6c629 fix(mcp): remove no-op pagination params from read_note and view_note (#768)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-28 23:05:14 -05:00
Paul Hernandez f3e46d7984 feat(mcp): discover projects across workspaces (#757)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-18 14:04:57 -05:00
jope-bm 56d6f1b4a5 fix(mcp): report cloud projects as source=cloud in factory mode (#752)
Signed-off-by: Joe P <joe@basicmemory.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 11:10:20 -06:00
Paul Hernandez 1b39062ecd refactor(core): rip telemetry wrappers, use logfire directly (#754)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 13:58:19 -05:00
Paul Hernandez 4e53bb83fd refactor(core): simplify note write flow (#739)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-15 17:56:27 -05:00
phernandez 8f2b25f0e0 test(core): stabilize postgres fixtures
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-13 14:25:26 -05:00
Paul Hernandez 052545b661 chore(core): make ty the default typechecker (#736)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-13 10:34:01 -05:00
Drew Cain b3403e96b3 fix: add workspace routing to cloud upload and API client (#704)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: phernandez <paul@basicmachines.co>
2026-04-06 18:18:40 -05:00
Paul Hernandez fe04a0b2a2 fix(mcp): pass workspace parameter through client factory (#722)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:57:37 -05:00
Paul Hernandez e6b98a15c7 fix(cli): propagate cloud workspace routing and incremental sync (#712)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-04-03 14:25:02 -05:00
phernandez 7696fca826 fix: restore MCP telemetry compatibility and outcomes
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-28 15:31:13 -05:00
phernandez 98a2a3cbaf Unify MCP telemetry spans across routers and services
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-28 14:42:18 -05:00
phernandez a4e0422926 perf fixes
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-27 22:12:44 -05:00
Paul Hernandez 4791e19685 feat: add Logfire phased instrumentation (#692)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:39:42 -05:00
Paul Hernandez 36848410a1 feat(core): add default_search_type config setting (#676)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:19:53 -05:00
Paul Hernandez 1a6a65571e fix(mcp): add project detection from memory:// URLs in edit_note and delete_note (#668)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 19:14:22 -05:00
Paul Hernandez 013864ebf0 fix(cli): use resolved project path in doctor command (#667)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:43:23 -05:00
Drew Cain db60942267 fix(core): invalidate config cache when file is modified by another process (#662)
Signed-off-by: Drew Cain <groksrc@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 23:05:55 -05:00
Paul Hernandez 9809b469c6 fix: enforce strict entity resolution in destructive MCP tools (#650)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:56:07 -05:00
Paul Hernandez ad3f2650d9 feat: add insert_before_section and insert_after_section edit operations (#648)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:54:10 -05:00
phernandez e846ae85d8 fix: semantic embeddings not generated on fresh DB or upgrade
The previous backfill trigger relied on Alembic revision tracking, but
alembic_version only stores the head revision — intermediate revisions
(like the backfill trigger) are invisible after a multi-step upgrade or
fresh DB creation.

Three changes fix this:

1. Replace Alembic revision check with a simple "entities exist but
   embeddings are empty" check that works regardless of migration path
2. Generate embeddings during sync — after FTS indexing, batch-embed all
   synced entities at the end of the sync operation
3. Add background backfill at MCP startup for the upgrade path (entities
   already exist, no embeddings) without blocking server readiness

Also adds clear startup logging for semantic embedding status so issues
are easy to spot in the logs.

📋 Covers: fresh DB, upgrade from pre-embedding version, db reset,
   interrupted backfill

Signed-off-by: Pedro Hernandez <pedro@basicmachines.co>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-07 13:23:39 -06:00
phernandez 63e4bcdf1d fix: clarify search_notes parameter naming and fix note_types case sensitivity
- Add Annotated descriptions to note_types and entity_types parameters so
  LLMs can distinguish frontmatter type filtering from knowledge graph item
  type filtering (search.py, ui_sdk.py)
- Lowercase note_types values at filter time so "Chapter" matches stored
  "chapter"
- Fix misleading entity_types references in schema.py guidance strings
  (should be note_types)
- Add permalink pattern documentation note about full path matching
- Add test for note_types case-insensitive lowercasing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-05 15:08:52 -06:00
phernandez f23dd0474b fix(test): patch API fallback in project_context tests for Postgres
In Postgres test mode, stale dependency_overrides on the module-level
FastAPI app allow _resolve_default_project_from_api() to query a live
database and return 'test-project' even when the test sets
default_project=None. Monkeypatch the async fallback in the three
affected tests to isolate config-based resolution from API leakage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-04 15:34:35 -06:00
phernandez 1fdc9fdc69 fix: resolve_project_parameter falls back to projects API for default (#644)
In cloud mode, ConfigManager has no local config so default_project
is always None. Add API fallback in resolve_project_parameter that
queries /v2/projects/ for the default_project field. This fixes all
MCP tools that rely on project resolution (recent_activity, etc).

Removed discovery mode tests that simulated an invalid state by
clearing is_default — there must always be a default project.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-04 13:17:16 -06:00
phernandez 7f2d4d2a6f fix: three cloud-testing bugs (#640, #641, #642)
🔧 #640 — LinkResolver selects worst match instead of best
Replace `min(results, key=lambda x: x.score)` with `results[0]`.
Both SQLite and Postgres return results sorted best-first in SQL,
so using `results[0]` is backend-agnostic and correct.

🔧 #641 — search_notes output_format="text" returns raw Pydantic model
Add `_format_search_markdown()` that formats SearchResponse as readable
markdown with title, permalink, score, and matched snippet per result.
Update prompts to use `output_format="json"` since they need structured
data for result counting and branching logic.

🔧 #642 — metadata_filters with `note_type` key returns empty results
Add `_METADATA_KEY_ALIASES` mapping at the tool level that aliases
`note_type` → `type` before passing metadata_filters to the search query.
The frontmatter field is `type`, not `note_type`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-02 12:58:22 -06:00
phernandez 8b7f39ee9b docs: update AI assistant guides for v0.19.0, fix multi-tag search parsing
Update both the compact and extended AI assistant guides with v0.19.0 changes:
- 📝 write_note overwrite guard: callout, edit_note examples, overwrite=True
- 🔍 Expanded search section: all search types, tag: shorthand, filter-only
  searches, metadata_filters operators, min_similarity
- ⚠️ "Note already exists" error handling pattern
- 📋 Tool quick reference: updated params, added list_workspaces
- 🔗 memory:// URL: added cross-project format
- ✏️ Best practice: prefer edit_note for updates

Fix tag: shorthand parsing to handle multiple tags anywhere in the query.
Old parser only handled queries starting with "tag:" and broke on
"tag:coffee AND tag:brewing". New parser uses re.findall to extract all
tag:value tokens, strips boolean connectors, and preserves remaining text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-02 09:50:22 -06:00
phernandez 8e64caf784 fix: list_workspaces bypasses factory pattern on cloud MCP server (#636)
Add set_workspace_provider() injection point so the cloud MCP server can
list workspaces by querying its own database directly, instead of making
an HTTP round-trip to the control-plane API with credentials it doesn't have.

🔧 Mirrors the existing set_client_factory() pattern in async_client.py
🧪 Adds 3 tests for provider injection, fallback, and context caching
🩹 Updates build_context test assertions for v0.18 backward compat fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-01 12:08:25 -06:00
phernandez 1987581ee3 docs: update v0.19.0 release notes and apply formatting fixes
Add #634 (stale schema metadata) to bug fixes section.
Apply ruff formatting to schema.py, write_note.py, test files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-03-01 12:08:25 -06:00
phernandez 5d5efa02a5 fix: format schema_infer and schema_diff as markdown text (#28)
schema_infer and schema_diff returned raw Pydantic models in text mode,
causing LLMs to render field names as "undefined". Add text formatters
(_format_inference_report, _format_drift_report) matching the existing
_format_validation_report pattern. CLI paths are unaffected — they
always use output_format="json".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-02-28 12:24:38 -06:00
phernandez 4f12182e28 fix: parse tag: prefix at MCP tool level to avoid hybrid search failure (#30)
When semantic search is enabled (default), `search_notes(query="tag:security")`
failed because the HYBRID retrieval mode requires non-empty text, but the
service-layer tag: parser clears the text after the mode is already set.

Parse tag: prefix at the tool level before search mode selection, converting it
to a tags filter. This works with all search modes (text, hybrid, vector).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-02-28 11:53:05 -06:00
phernandez 2c5b63c4a2 fix: default search_notes to entity-level results (#31)
search_notes was returning individual observations and relations as
separate top-level results, wasting the result limit and creating
confusing UX. Default entity_types to ["entity"] when the caller
doesn't specify it — the entity row already indexes full file content,
so no matches are lost. Users can still override with explicit
entity_types=["observation"] etc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
2026-02-28 11:28:51 -06:00