Skip to content

fix(mcp): resolve projects by internal name + filter ghost dbs (#704)#717

Merged
DeusData merged 1 commit into
mainfrom
fix/704-store-resolution
Jun 30, 2026
Merged

fix(mcp): resolve projects by internal name + filter ghost dbs (#704)#717
DeusData merged 1 commit into
mainfrom
fix/704-store-resolution

Conversation

@DeusData

Copy link
Copy Markdown
Owner

Fixes #704.

Symptom: query_graph / search_graph / get_architecture return "project not found or not indexed" for a project that list_projects shows — inconsistently ("works now, fails 10 min later"). (_config.db being empty is a red herring — it's a KV config store, never a registry.)

Root cause: resolution is registry-less + filename-addressed (list_projects and the error's available_projects come from .db filenames) but a query requires the internal projects.name row to equal the passed name. When a db's filename ≠ its internal name — a copied/renamed db, or a legacy .-vs-- username twin from a past path sanitization — it lists but never resolves. The intermittency was a warm in-process store cache (60 s idle evict) masking a broken cold resolve; a fresh cli call is always cold. Node rows are keyed on the internal name, so opening the file isn't enough — queries must use the internal name too.

Fix — key list + resolve on the INTERNAL name; no file renames, no data mutation:

  • list_projects / collect_db_project_names / build_project_json_entry advertise each db's internal projects.name and skip ghost dbs (0-byte / no project row), so listed names are exactly the resolvable ones.
  • resolve_store keeps the fast path (open <passed>.db + get_project — the common case). On a miss it runs a bounded cache-dir scan, adopting the db whose sole internal project name equals the passed name (gated on exactly one project row, so it never serves another project's data). No match → not-found (typos stay not-found). The scan runs only on the fallback.

Reproduce-first: tool_resolve_store_by_internal_name_issue704 — isolated CBM_CACHE_DIR with a control db, a drifted db (filename ≠ internal name), and a 0-byte ghost; asserts list advertises the internal name (not the filename), the drifted project resolves via the scan, and the ghost is neither listed nor resolvable. RED (113/1 — list lacked the internal name) → GREEN (114/0). Full suite 5723/0.

Also hardened tool_bad_project_name_no_overflow_issue235: its fixture used 1-byte non-SQLite files that the new ghost filter would skip, making the buffer-overflow guard vacuous — rebuilt with valid dbs carrying long internal names so the guard still fires.

Design note: chose internal-name addressing over a file-rename migration — it makes list/resolve/queries consistent without renaming or mutating any user .db file. The separate latent READWRITE/WAL query-open bug (queries mutate dbs / fail on a read-only FS) is tracked as its own PR.

Part of the CLI-reliability series: #640#714, #680#716.

Project resolution was filename-addressed but internal-name-validated:
resolve_store opened <cache>/<passed>.db and required the internal
projects.name row to equal the passed name, while list_projects advertised
the .db FILENAME. When a db's filename differed from its internal name (a
copied/renamed db, or a legacy '.'-vs-'-' username twin from a past path
sanitization), it showed in list_projects but every query returned "project
not found or not indexed". Node rows are keyed on the internal name, so just
opening the file is not enough -- queries must use the internal name too. The
intermittency ("works now, fails 10 min later") was a warm in-process store
cache masking a broken cold resolve; a fresh cli call is always cold.

Key list + resolve on the INTERNAL project name, with no file renames or
mutation:
- list_projects / collect_db_project_names / build_project_json_entry now
  advertise each db's internal projects.name (via db_internal_project_name)
  and skip ghost/empty/corrupt dbs (0-byte, no project row) so the listed
  names are exactly the ones that resolve.
- resolve_store keeps the fast path (open <passed>.db, get_project -- the
  common case where filename == internal name). On a miss it runs a bounded
  cache-dir scan, adopting the db whose sole internal project name equals the
  passed name (gated on exactly one project row so it never serves another
  project's data). No match -> NULL (a typo stays not-found). The scan runs
  only on the fallback.

Reproduce-first: tool_resolve_store_by_internal_name_issue704 builds an
isolated CBM_CACHE_DIR with a control db, a drifted db (filename != internal
name), and a 0-byte ghost; asserts list advertises the internal name (not the
filename), the drifted project resolves via the scan, and the ghost is neither
listed nor resolvable. RED (113/1, list lacked the internal name) -> GREEN
(114/0). Full suite 5723/0.

Also hardened tool_bad_project_name_no_overflow_issue235: its fixture used
1-byte non-SQLite files that the new ghost filter would skip, making the
buffer-overflow guard vacuous; rebuilt it with valid dbs carrying long
internal names so the guard still fires.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
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.

query_graph / search_graph return "project not found" inconsistently; DBs valid on disk but CLI looks up wrong inode

1 participant