Skip to content

feat: add overwrite guard to write_note tool#632

Merged
phernandez merged 2 commits intomainfrom
feat/write-note-overwrite-guard
Feb 27, 2026
Merged

feat: add overwrite guard to write_note tool#632
phernandez merged 2 commits intomainfrom
feat/write-note-overwrite-guard

Conversation

@phernandez
Copy link
Member

Summary

Closes #625

  • write_note now errors by default when a note already exists, preventing silent data loss from autonomous agents (OpenClaw, Claude Code agents) that accidentally overwrite accumulated content
  • Added overwrite parameter (bool | None) — pass True to explicitly replace, False/None consults config default
  • Added write_note_overwrite_default config field (default: False) so users can globally restore pre-v0.20 upsert behavior via config or BASIC_MEMORY_WRITE_NOTE_OVERWRITE_DEFAULT env var
  • Error message suggests edit_note alternatives (append, prepend, replace_section) and read_note for inspection
  • JSON output returns structured NOTE_ALREADY_EXISTS error on conflict
  • Guard lives entirely at the MCP tool layer — no API/service changes needed
  • Updated tool annotations: destructiveHint=True, idempotentHint=False

Test plan

  • pytest tests/mcp/test_tool_contracts.py — signature includes overwrite (1 test)
  • pytest tests/mcp/test_tool_write_note.py — 40 tests pass (5 existing fixed + 6 new guard tests)
  • pytest test-int/mcp/test_write_note_integration.py — 13 tests pass (1 fixed + 1 new integration test)
  • pytest tests/mcp/test_permalink_collision_file_overwrite.py — 4 tests pass (1 fixed)
  • ruff check and pyright pass clean on modified files

🤖 Generated with Claude Code

write_note now errors by default when a note already exists, preventing
silent data loss from autonomous agents that accidentally overwrite
accumulated content. The error message suggests edit_note alternatives.

- Add `overwrite` parameter (bool | None) to write_note — explicit True
  to replace, False/None consults config default
- Add `write_note_overwrite_default` config field (default: False) so
  users can globally restore pre-v0.20 upsert behavior
- Guard lives entirely at the MCP tool layer — no API/service changes
- JSON output returns structured `NOTE_ALREADY_EXISTS` error on conflict
- Update tool annotations: destructiveHint=True, idempotentHint=False
- Fix existing tests that relied on implicit update behavior
- Add 7 new tests (6 unit + 1 integration) for the overwrite guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

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: 7c8842e059

ℹ️ 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".

if output_format == "json":
return {
"title": title,
"permalink": entity.permalink,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return stored permalink for overwrite conflicts

When write_note hits a conflict with overwrite disabled, the JSON/text error uses entity.permalink, which is recomputed from the incoming title/directory rather than looked up from the existing note. This is incorrect for notes that already have a custom frontmatter permalink (a supported flow in this repo), so the returned permalink can point to a non-existent identifier and cause the suggested read_note/edit_note follow-up calls to fail in that scenario.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@phernandez phernandez force-pushed the feat/write-note-overwrite-guard branch from d684b7a to 7c8842e Compare February 27, 2026 19:42
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

Three more test files had write_note calls that create a note then
update it without passing overwrite=True:

- test_obsidian_yaml_formatting.py: test_write_note_update_preserves_yaml_format
- test_tool_json_output_modes.py: test_write_note_text_and_json_modes
- test_tool_write_note_metadata.py: test_metadata_survives_update

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@phernandez phernandez merged commit bd5923a into main Feb 27, 2026
25 of 26 checks passed
@phernandez phernandez deleted the feat/write-note-overwrite-guard branch February 27, 2026 22:12
phernandez added a commit that referenced this pull request Feb 28, 2026
Add write_note overwrite guard (#632) to new capabilities, 8 bug fixes
(#631, #630, #30, #31, #28, plus schema_validate/Post/frontmatter fixes),
and write_note idempotency breaking change to upgrade notes. Bump commit
count from 80+ to 90+.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
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.

write_note: add guard against accidental overwrites

1 participant