feat(cli): refuse db reset while basic-memory mcp processes run#776
feat(cli): refuse db reset while basic-memory mcp processes run#776phernandez merged 3 commits intomainfrom
Conversation
Closes #765. On POSIX, Path.unlink() removes the directory entry but leaves the inode alive as long as any process holds the file open. A `bm reset` that races against a live `basic-memory mcp` therefore "succeeds" — but the still-running MCP keeps reading the now-invisible memory.db inode and returns phantom search rows back to the user. Windows naturally raises PermissionError on unlink so the bug is POSIX-only. The fix is a pre-flight scan via psutil for any non-self `basic-memory mcp` invocations. If any are found, abort with the PIDs and a platform-appropriate `pgrep`/`Get-CimInstance` hint so the user can clean up themselves — we deliberately do not auto-kill anyone else's processes. `--force` is the documented escape hatch for scripted/CI workflows that have already verified no MCP clients are attached. Signed-off-by: phernandez <paul@basicmachines.co>
|
Claude finished @phernandez's task in 1m 49s —— View job Code Review
Solid, well-reasoned fix. The core POSIX inode problem is correctly diagnosed and the approach (detect + refuse rather than auto-kill) is the right call. A few items worth addressing before merge: Issues1. Process matching can false-positive on path substrings ( if "basic-memory" in joined and "mcp" in cmdline:
if "basic-memory" in cmdline and "mcp" in cmdline:This is also more consistent — 2. In Unix parlance a "zombie" process is one that has exited but whose exit code hasn't been reaped. These are the opposite — they're very much alive. 3. Missing unit test for There's no test that verifies Team Checklist
Minor Observations
Overall the implementation is clean and the rationale is well-documented. Issues #1 and #2 are straightforward fixes; issue #3 is low-risk given the subprocess test coverage but worth adding if you want the contract explicitly tested. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4bb43d2cd
ℹ️ 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".
| # Match the full `basic-memory mcp` invocation, not just any | ||
| # process whose argv mentions either word individually. | ||
| joined = " ".join(cmdline) | ||
| if "basic-memory" in joined and "mcp" in cmdline: |
There was a problem hiding this comment.
Detect
bm mcp processes before allowing reset
The new pre-flight only matches processes whose command line contains the literal basic-memory, so an MCP server started via the supported bm mcp entrypoint is not detected. In that scenario bm reset still proceeds on POSIX while the live MCP process keeps the old inode open, reproducing the phantom-results problem this change is meant to block. The matcher should include the bm alias (or detect by subcommand structure) to avoid this false negative.
Useful? React with 👍 / 👎.
Signed-off-by: phernandez <paul@basicmachines.co>
|
Claude finished @phernandez's task in 1m 49s —— View job Code Review (Updated — post
|
| Item | Status |
|---|---|
| Follows coding conventions (CLAUDE.md) | ✅ |
| Python 3.12+ type annotations | ✅ |
| 100-character line length | ✅ |
| No commented-out code | ✅ |
| Unit tests for new functions | ✅ |
| Test coverage for edge cases | ✅ |
| 100% test coverage maintained | ✅ |
| Windows compatibility | ✅ |
| No emojis / unicode in console output | ✅ |
| No hardcoded secrets | ✅ |
| Proper error handling | ✅ |
psutil dependency pinned sensibly |
✅ |
| Literate-style comments | ✅ |
Process matching false-positive risk (in joined) |
|
zombies variable naming |
|
--force bypass unit test |
|
bm mcp alias detection |
The new commit is clean and the type-annotation fix is correct. The three substantive issues from the prior round still need attention before merge.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c4b1f7142
ℹ️ 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".
| # Match the full `basic-memory mcp` invocation, not just any | ||
| # process whose argv mentions either word individually. | ||
| joined = " ".join(cmdline) | ||
| if "basic-memory" in joined and "mcp" in cmdline: |
There was a problem hiding this comment.
Restrict MCP reset guard to processes using same database
This guard aborts bm reset whenever any basic-memory ... mcp process exists, but it never checks whether that process is attached to the same memory.db path being reset. In multi-profile setups (for example, one MCP server running under a different BASIC_MEMORY_HOME while resetting an isolated test/home config), bm reset now fails with exit code 1 even though no client is using the target DB, forcing users into --force for normal workflows.
Useful? React with 👍 / 👎.
Codex P1 on #776: the previous matcher required the literal string 'basic-memory' somewhere in argv, so a process started via the supported `bm mcp` alias entrypoint slipped through and the reset proceeded against a live MCP — the exact bug the pre-flight is supposed to block. Extract the matching to _is_basic_memory_mcp() and recognize: - basic-memory / basic_memory tokens (any position) - bm script invocations across path conventions (`bm`, `/usr/local/bin/bm`, `bm.exe`, `~/.venv/Scripts/bm.exe`) using both PurePosixPath and PureWindowsPath so cross-OS test fixtures work regardless of the host platform. Test now covers both the alias forms and the negative cases (`mcp` appearing as a substring or in an unrelated server's argv). Signed-off-by: phernandez <paul@basicmachines.co>
|
Codex review on P1 — P2 — multi-profile / different |
|
Claude finished @phernandez's task in 1m 52s —— View job Code Review (Updated — post
|
| Item | Status |
|---|---|
| Follows coding conventions (CLAUDE.md) | ✅ |
| Python 3.12+ type annotations | ✅ |
| 100-character line length | ✅ |
| No commented-out code | ✅ |
| Unit tests for new functions | ✅ |
| Test coverage for edge cases | ✅ — NoSuchProcess race, empty cmdline, self-exclusion, bm alias, bm.exe, Windows path |
| 100% test coverage maintained | ✅ |
| Windows compatibility | ✅ |
| No emojis / unicode in console output | ✅ |
| No hardcoded secrets | ✅ |
| Proper error handling | ✅ |
psutil dependency pinned sensibly |
✅ |
| Literate-style comments | ✅ |
| Process matching false-positive risk | ✅ — substantially improved with _is_basic_memory_mcp |
bm mcp alias detection (Codex P1) |
✅ — fixed in 29ebfd7 |
| Multi-profile / different BASIC_MEMORY_HOME (Codex P2) | ℹ️ — explicitly punted; --force is documented escape hatch |
zombies variable naming |
|
--force bypass unit test |
The _is_basic_memory_mcp refactor is well-structured and a clear improvement. Two items (naming and a missing test) remain before merge.
Summary
psutil-backed pre-flight scan tobm resetthat detects livebasic-memory mcpprocesses and refuses to proceed when any are found, surfacing PIDs + platform-appropriate cleanup commands.--forceescape hatch for scripted/CI workflows that have already verified no MCP clients are attached.reset --reindexandreindex --search#765.Why
Reporter on #765 hit phantom search results after
bm reset --reindex. Diagnosis (verified by reporter): on POSIX,Path.unlink()removes the directory entry but the inode survives as long as any process holds the file open. A livebasic-memory mcp(Claude Desktop / VS Code / etc.) keeps reading the now-invisiblememory.dbinode and returns stale rows. Windows naturally raisesPermissionErroronunlink(), so this is a POSIX-only silent failure.Per discussion, we don't auto-kill anyone else's processes — we just detect, message, and exit non-zero.
Test plan
uv run pytest tests/cli/test_db_reset_zombie_check.py tests/cli/test_db_reset_exit.py --no-cov(11 passed)uv run ruff check+uv run ty checkclean on changed files