fix(mcp): stop move_note reporting false success across boundaries#904
Conversation
move_note silently performed a same-project move (creating a nested folder) and reported success when an agent intended a cross-workspace/cross-project move. This closes three gaps: - Gap 1: broaden cross-boundary detection to reject a leading path segment that is a known project name AND the cloud workspace shape '<workspace>/projects/<x>/...', while leaving legitimate same-project nested moves (e.g. 'projects/2025/file.md') untouched. - Gap 2: validate outcome vs. intent after move_entity returns; if the resulting file_path diverges from the requested destination, report failure instead of '✅ moved successfully'. This is the robust backstop. - Gap 3: run the cross-boundary guard AFTER destination_folder is resolved into destination_path, so the destination_folder path no longer bypasses detection. No destination-project/workspace parameter is added — real cross-project moves remain a separate, larger feature. This change only makes the tool honest. Closes #881 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
|
Claude finished @phernandez's task in 2m 15s —— View job Code Review: fix(mcp): stop move_note reporting false success across boundaries
Overall this is a solid, well-motivated fix. The three-gap framing is clear, the comments follow the project's Trigger/Why/Outcome style, and the test coverage is good. One medium-severity false-positive bug and a few smaller issues below. Team Checklist Results
IssuesMedium — Detection 2 false positive: interior
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7d1c63eee
ℹ️ 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".
| for index in range(1, len(path_parts) - 1): | ||
| if path_parts[index] == "projects": | ||
| return _format_cross_project_error_response( | ||
| identifier, destination_path, current_project, path_parts[0] |
There was a problem hiding this comment.
Narrow workspace-shape detection to actual boundary paths
For a normal same-project move into a nested folder named projects, such as archive/projects/moved.md or team/projects/2026/moved.md, this new check rejects the request before move_entity runs even though the path is a valid relative path within the project. The tool otherwise allows arbitrary nested folders and the added integration test only preserves top-level projects/...; matching any interior projects segment makes legitimate organization layouts unusable unless users rename their folder.
Useful? React with 👍 / 👎.
Detection 2 in _detect_cross_project_move_attempt scanned every interior path segment for a literal "projects" and rejected the move, producing false positives for legitimate same-project nested folders such as notes/projects/my-project/note.md or team/2026/projects/alpha/note.md. Replace the interior-scan loop with a single targeted check that fires only for the actual cloud workspace shape <workspace>/projects/<x>/... — i.e. when path_parts[1] == "projects" and len(path_parts) >= 3. Detection 1 (leading segment equals a known project name) is unchanged. Addresses Codex P2 and claude-review medium false-positive findings. Tests: - test-int: legitimate interior 'projects' move (index 2) succeeds - tests: unit coverage of _detect_cross_project_move_attempt for workspace-shape rejection, interior-projects acceptance, and top-level projects acceptance - existing workspace-shape rejection and top-level projects success tests retained Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
|
Claude finished @phernandez's task in 3m 1s —— View job Code Review: fix(mcp): stop move_note reporting false success across boundaries
This is a solid, well-motivated fix. The two-commit structure is clear: commit 1 ( Team Checklist Results
Medium Bug from Prior Review — Resolved ✅The loop
Remaining Minor —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20e79d9b03
ℹ️ 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".
| return _format_cross_project_error_response( | ||
| identifier, destination_path, current_project, path_parts[0] | ||
| ) |
There was a problem hiding this comment.
Report the destination project, not workspace
For workspace-shaped destinations like team-space/projects/alpha/moved.md, this branch formats the error with target_project=path_parts[0], so the guidance says the target project is team-space and even suggests write_note(..., project="team-space"). In the documented <workspace>/projects/<x>/... shape, <x> is the project being targeted, so users following the error message will try to write into the workspace name instead of the actual project (alpha).
Useful? React with 👍 / 👎.
… error For a <workspace>/projects/<x>/... destination the guidance named path_parts[0] (the workspace) as the target project and suggested write_note(project=<workspace>); the project being targeted is <x> at index 2. Report that instead (Codex review on #904). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ae705c962
ℹ️ 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".
| # In "<workspace>/projects/<x>/...", <x> (index 2) is the project being targeted, | ||
| # so report that as the target project — not the workspace at index 0 — so the | ||
| # guidance points the user at the real project name to write into. | ||
| if len(path_parts) >= 3 and path_parts[1] == "projects": |
There was a problem hiding this comment.
Allow normal folders before
projects
For same-project destinations with a second path segment named projects, such as archive/projects/moved.md or the comment's own notes/projects/my-project/note.md, this guard still rejects the move as cross-project even though these are valid relative folders inside the active project. Fresh evidence is that the current code only pins projects to index 1, which still matches common nested organization paths and even treats moved.md as the target project for a three-segment path.
Useful? React with 👍 / 👎.
|
Claude finished @phernandez's task in 1m 55s —— View job Code Review: fix(mcp): stop move_note reporting false success across boundaries
Three commits, clean progression. Commit Team Checklist Results
Minor (Unresolved from review 2) —
|
|
Follow-up from reviewing the recently closed PRs: I agree with the earlier review finding that the I checked current read_note("{identifier}")
delete_note("{identifier}", project="{active_project.name}")This branch fires after read_note("{result.file_path}")
delete_note("{result.file_path}", project="{active_project.name}")Low probability because the backstop only fires on an unexpected service divergence, but it is exactly the path where the guidance needs to be most precise. |
Summary
move_notesilently performed a same-project move (creating a nested folder) and reported✅ Note moved successfullywhen an agent intended a cross-workspace/cross-project move, misleading the agent into thinking the note crossed the boundary. This PR makes the tool honest about the unsupported case. It does not add real cross-project moves (a separate, larger feature needing a destination-project/workspace parameter end-to-end).Closes #881.
What changed
src/basic_memory/mcp/tools/move_note.py:destination_folderis resolved intodestination_path. Previously the guard ran whiledestination_pathwas still""fordestination_foldercallers, so boundary-shaped folders bypassed detection entirely.move_entityreturns, compare the normalized requested destination against the resultingresult.file_path. If they diverge in a way the caller did not ask for, return a failure (moved: False/# Move Failed - Unexpected Result Location) instead of unconditionally printing✅ Note moved successfully._detect_cross_project_move_attemptnow rejects two clear cross-boundary shapes — a leading path segment equal to a known project name, and the cloud workspace shape<workspace>/projects/<x>/...(interiorprojectssegment). Legitimate same-project moves into new nested folders (including a top-levelprojects/2025/...) are not flagged.Testing
Commands run (SQLite, no Docker):
uv run ruff check . --fixanduv run ruff format .— clean.uv run ty check src tests test-int—All checks passed!uv run pytest tests/mcp/test_tool_move_note.py test-int/mcp/test_move_note_integration.py -q— 67 passed.uv run pytest tests/mcp/ test-int/mcp/ -q— 875 passed, 1 unrelated transient failure intest_delete_note_integration.py(Could not load model BAAI/bge-small-en-v1.5embedding-model load during the large parallel run); it passes on isolated rerun (9 passed) and touches no move_note code.New integration tests (
test-int/mcp/test_move_note_integration.py):test_move_note_workspace_shaped_path_rejected— workspace-shaped destination fails with cross-project error; note stays put; nested path is not created.test_move_note_destination_folder_boundary_rejected— thedestination_folderbypass (Gap 3) is now detected.test_move_note_workspace_shaped_path_rejected_json— JSON output reportsmoved: False/CROSS_PROJECT_MOVE_NOT_SUPPORTED.test_move_note_new_nested_folder_still_succeeds— legitimate same-project nested move still succeeds (false-positive guard).New unit tests (
tests/mcp/test_tool_move_note.py,TestMoveNoteOutcomeValidation):file_pathviamonkeypatchonKnowledgeClient.move_entitybecause the real service stores the requested path verbatim, so divergence cannot occur through the live stack — this is the legitimate "inject a failure" case for a mock.Risk / validation
<workspace>/projects/<x>shape is rejected, so normal nested moves are unaffected (verified by existingtest_move_note_allows_safe_pathsand the new false-positive test). The outcome-validation backstop only fires when the landing path genuinely differs from the request, which never happens for valid same-project moves.Closes #881
🤖 Generated with Claude Code