Skip to content

Update ensure_note to preserve content when content is omitted during updates - #66

Merged
ddutchie merged 7 commits into
mainfrom
ddutchie/movenotes
Jun 26, 2026
Merged

ddutchie merged 7 commits into
mainfrom
ddutchie/movenotes

Conversation

@ddutchie

@ddutchie ddutchie commented Jun 26, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

This PR updates the ensure_note tool so that omitting the content argument during an update operation preserves the existing note content instead of defaulting to an empty string. Documentation and Zod schemas have been updated to reflect this behavior, and a new test case ensures this functionality remains intact.

Type of change

  • Bug fix
  • New feature
  • Refactor / code quality
  • Docs / changelog
  • Tests

Checklist

  • npm run type-check:all passes
  • npm run lint passes
  • npm test passes (runs npm run compile first so electron/bundle-guard.test.ts actually executes — npm run test:bundle to run just that)
  • npm run test:e2e passes (run before merging UI changes or cutting a release)
  • No hardcoded colours — CSS variables only (var(--accent), var(--text-primary), etc.)
  • No text-[Npx] pixel font classes — rem equivalents only (text-[0.714rem], text-xs, etc.)
  • New IPC handlers wrapped in handle() and return IpcResult<T>
  • New DB migrations appended (not edited) in schema.ts
  • New SQL goes in electron/db/queries.ts — single source of truth (imported by both Electron main process and MCP server); never construct a Database instance outside db/client.ts (Electron) or mcp-server.ts (MCP runtime)
  • New dependencies or devDependencies added to ROLE_MAP in scripts/generate-licenses.js and licenses.json regenerated
  • New MCP tools registered in electron/mcp/tools/index.ts dispatch + electron/lib/tool-schemas.ts Zod schema
  • If you added/removed a --external:<pkg> flag in the compile script: updated the appropriate allowlist group (RUNTIME_PROVIDED / OPTIONAL_TRANSITIVE / SUBPROCESS_ONLY / SHIPPED_NATIVE) in electron/bundle-guard.test.ts and (if SHIPPED_NATIVE) shipped the package via electron-builder.yml

Notes for reviewer

The fix involves changing how the markdown variable is derived in electron/mcp/tools/notes.ts. It now performs an explicit check content !== undefined to distinguish between an intended update to empty content and an omitted parameter.

Summary by CodeRabbit

  • New Features
    • Added unlink note from task.
    • get_task now returns blockingCardIds.
    • Added note tools: rename note (updates inbound wikilinks), bulk move notes, and list folders.
    • Enhanced note search with offset pagination and updatedAfter filtering.
  • Bug Fixes
    • ensure_note now preserves existing note content when content is omitted (clears when content is "").
    • Fixed note renaming/moves so note text isn’t overwritten.
    • Resolved a system-prompt escaping issue that could break builds.
  • Tests
    • Expanded coverage for unlinking, blocking, rename, bulk move, folder listing, and advanced search.
  • Documentation
    • Updated tool guidance/schema and system prompt for new operations and ensure_note.content behavior.

Updated the `ensure_note` tool logic to make the `content` parameter optional during updates. If the `content` field is omitted when updating an existing note, the current content will now be preserved instead of being overwritten with an empty string.

Documentation and test coverage have been updated to reflect this behavior.
@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

ensure_note now preserves existing note content when updates omit content, and task-note tools now support unlinking, report blocking cards, and persist link changes through the MCP dispatch path.

Changes

Note update behavior

Layer / File(s) Summary
Content-preserving note updates
electron/mcp/tools/notes.ts, electron/lib/tool-schemas.ts, electron/lib/tools.ts, electron/ipc/chat-executor.test.ts, changelogs/v2.2.7.md
ensure_note now skips content writes when content is omitted, reuses the updated note when writing files, and the schema, prompt text, changelog, and tests reflect that behavior.

Note management and search

Layer / File(s) Summary
Note management and search tools
electron/mcp/tools/notes.ts, electron/mcp/tools/index.ts, electron/lib/tool-schemas.ts, electron/shared/read-tools-pure.ts, electron/ipc/chat-executor.ts, electron/ipc/chat-executor.test.ts
rename_note, bulk_move_notes, list_folders, and search_notes gain dispatcher wiring, schema updates, implementation, and tests for renaming, wikilink rewrites, folder moves, folder listing, offset pagination, and updatedAfter filtering.

Task link and unlink flow

Layer / File(s) Summary
Task links, unlinking, and blockers
electron/mcp/tools/tasks.ts, electron/mcp/tools/index.ts, electron/ipc/chat-executor.ts, electron/lib/tool-schemas.ts, electron/lib/tools.ts, electron/ipc/chat-executor.test.ts, changelogs/v2.2.7.md
get_task now returns blockingCardIds, link_note_to_task accepts workspacePath and writes note files after DB updates, and unlink_note_from_task is wired through the MCP dispatcher, schema, labels, prompt text, tests, and changelog.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A bunny bounced through notes so bright,
Kept old words safe and tucked in tight.
Then links could join, or part, or move,
With folders, blockers, all in groove.
Hop hop — the tools now hum just right 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main user-facing change: ensure_note now preserves existing content when content is omitted during updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ddutchie/movenotes

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
electron/ipc/chat-executor.test.ts (1)

439-449: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the explicit empty-string case too.

This change hinges on distinguishing omitted content from an intentional clear, but the new coverage only exercises omission. Please add a companion assertion for content: "" so both sides of the contract stay protected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/ipc/chat-executor.test.ts` around lines 439 - 449, The new test in
chat-executor.test.ts only covers the omitted-content path for ensure_note; add
a companion assertion in the same test or a nearby test that calls exec with
content: "" and verifies the note is cleared, while the omitted-content case
still preserves the original content. Use the ensure_note and get_note flows
already present so both behaviors remain covered by the test suite.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/mcp/tools/notes.ts`:
- Line 57: The note update path is incorrectly reusing `existing?.content` from
the snapshot when `content` is omitted, which can overwrite newer persisted note
text. In `ensure_note` and the `updateNote` call in
`electron/mcp/tools/notes.ts`, keep `content` and `contentText` undefined when
the argument is missing so the DB layer’s omission-safe `COALESCE` behavior in
`queries.ts` preserves stored content. Then use the row returned by
`q.updateNote(...)` as the source for `writeNoteFile` instead of reconstructing
content from `snap.notes`.

---

Nitpick comments:
In `@electron/ipc/chat-executor.test.ts`:
- Around line 439-449: The new test in chat-executor.test.ts only covers the
omitted-content path for ensure_note; add a companion assertion in the same test
or a nearby test that calls exec with content: "" and verifies the note is
cleared, while the omitted-content case still preserves the original content.
Use the ensure_note and get_note flows already present so both behaviors remain
covered by the test suite.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c605f3a-5a52-4f82-9f27-856f9c761a54

📥 Commits

Reviewing files that changed from the base of the PR and between dd419ea and 5e1b8d8.

📒 Files selected for processing (4)
  • electron/ipc/chat-executor.test.ts
  • electron/lib/tool-schemas.ts
  • electron/lib/tools.ts
  • electron/mcp/tools/notes.ts

Comment thread electron/mcp/tools/notes.ts Outdated
ddutchie added 2 commits June 26, 2026 11:13
Updates the `ensure_note` logic to distinguish between omitting the content argument (which retains existing content) and explicitly passing an empty string (which clears the content). Previously, the implementation treated both cases identically by falling back to the existing content.

Test coverage was added to verify that empty string inputs now correctly update the note content to be empty.
- Added `unlink_note_from_task` tool to remove connections between task cards and notes.
- Updated `get_task` to include `blockingCardIds` in its output, allowing for easier dependency discovery.
- Updated `link_note_to_task` to synchronize metadata with the physical file system via `writeNoteFile`.
- Added unit tests for new linking and unlinking functionality.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
electron/mcp/tools/tasks.ts (1)

124-150: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Optional: extract shared link/unlink logic.

unlink_note_from_task is almost a line-for-line copy of link_note_to_task (snapshot lookup, transaction, writeNoteFile, notification), differing only in set union vs. filter and the notification text/return key. A small shared helper would reduce drift risk between the two paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/mcp/tools/tasks.ts` around lines 124 - 150, `unlink_note_from_task`
duplicates most of `link_note_to_task`, so factor the shared snapshot lookup,
transaction/update, `writeNoteFile`, and notification flow into a common helper
to reduce drift. Keep the link/unlink-specific parts in the wrappers: the
collection transform on `linkedCardIds`/`linkedNoteIds`, the notification text,
and the returned status key. Use the existing `link_note_to_task` and
`unlink_note_from_task` symbols to consolidate the repeated logic without
changing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/mcp/tools/tasks.ts`:
- Around line 96-150: `link_note_to_task` and `unlink_note_from_task` write note
files via `writeNoteFile` without the `aiWriteLock` protection, so concurrent
note edits can clobber frontmatter updates. Update the note-write flow in
`tasks.ts` to use the same lock/unlock pattern as `ensure_note`,
`append_to_note`, and `patch_note`, or refactor to a shared locked write helper
so `noteId` is always guarded before calling `writeNoteFile`.

---

Nitpick comments:
In `@electron/mcp/tools/tasks.ts`:
- Around line 124-150: `unlink_note_from_task` duplicates most of
`link_note_to_task`, so factor the shared snapshot lookup, transaction/update,
`writeNoteFile`, and notification flow into a common helper to reduce drift.
Keep the link/unlink-specific parts in the wrappers: the collection transform on
`linkedCardIds`/`linkedNoteIds`, the notification text, and the returned status
key. Use the existing `link_note_to_task` and `unlink_note_from_task` symbols to
consolidate the repeated logic without changing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f899add7-2c4a-4f96-a9d8-818a3643291f

📥 Commits

Reviewing files that changed from the base of the PR and between a505ed1 and dc5a217.

📒 Files selected for processing (7)
  • changelogs/v2.2.7.md
  • electron/ipc/chat-executor.test.ts
  • electron/ipc/chat-executor.ts
  • electron/lib/tool-schemas.ts
  • electron/lib/tools.ts
  • electron/mcp/tools/index.ts
  • electron/mcp/tools/tasks.ts
✅ Files skipped from review due to trivial changes (1)
  • changelogs/v2.2.7.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • electron/lib/tools.ts

Comment thread electron/mcp/tools/tasks.ts Outdated
ddutchie added 2 commits June 26, 2026 11:27
- Added `rename_note` tool to safely rename notes and automatically update internal wikilinks.
- Added `bulk_move_notes` tool for efficient batch reorganization of notes.
- Added `list_folders` tool to retrieve a project's active directory tree.
- Enhanced `search_notes` with `offset` for pagination and `updatedAfter` for temporal filtering.
- Implemented corresponding IPC handlers and unit tests to ensure stability and proper mutex locking for file operations.
Consolidated logic for linking and unlinking notes from tasks into a shared `applyNoteLinkChange` helper function. Introduced note locking during database transactions to prevent race conditions or partial updates when syncing note files.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/ipc/chat-executor.test.ts`:
- Around line 596-604: The offset pagination test in search_notes only verifies
result length, so it can pass even if offset is ignored. Update the test setup
around makeDb, seed, and createNote to use deterministic updated_at values, then
assert the exact IDs and ordering returned by exec(..., "search_notes", ...) for
the second page so the test proves offset is actually applied.

In `@electron/lib/tool-schemas.ts`:
- Around line 63-67: The search_notes schema in tool-schemas.ts is too
permissive for pagination and date filtering. Update the schema definition for
offset to require a non-negative integer using the existing schema builder in
that block, and change updatedAfter to use the ISO datetime validator instead of
a plain string so the contract is enforced at the schema level. Keep the change
localized to the search_notes schema entries near query, projectId, limit,
offset, and updatedAfter.

In `@electron/mcp/tools/notes.ts`:
- Around line 222-250: Move the external file writes in the note rename flow out
of the db.transaction block in notes.ts: the current updatedNote logic mixes
q.updateNote and writeNoteFile, so a failure later in the loop can roll back
SQLite while already-written files remain changed. Keep the transaction focused
on updating the database and collecting the affected note rows, then after
commit iterate the persisted rows and call writeNoteFile for each rewritten
note.
- Around line 216-225: The rename flow in notes.ts should reject title
collisions within the same project before calling q.updateNote. In the rename
handler that loads note/project and then updates the note, check whether any
other non-archived note in that project already has the normalized newTitle, and
return an error if so. Keep the check scoped to the existing note/project lookup
logic so ensure_note, q.updateNote, and the project’s note list stay consistent.

In `@electron/shared/read-tools-pure.ts`:
- Around line 194-205: The updatedAfter filter in read-tools-pure.ts is
comparing date strings lexicographically inside the note filtering logic, which
can reject valid ISO timestamps with offsets. Update the filtering in the note
query path to parse updatedAfter and each note’s updatedAt as timestamps once,
then compare epoch values in the snap.notes.filter callback so the behavior is
correct regardless of timezone format.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2a96c39-5c31-47d1-8fc5-f6e7466b7437

📥 Commits

Reviewing files that changed from the base of the PR and between dc5a217 and 623c132.

📒 Files selected for processing (9)
  • changelogs/v2.2.7.md
  • electron/ipc/chat-executor.test.ts
  • electron/ipc/chat-executor.ts
  • electron/lib/tool-schemas.ts
  • electron/lib/tools.ts
  • electron/mcp/tools/index.ts
  • electron/mcp/tools/notes.ts
  • electron/mcp/tools/tasks.ts
  • electron/shared/read-tools-pure.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • electron/mcp/tools/tasks.ts
  • electron/lib/tools.ts

Comment thread electron/ipc/chat-executor.test.ts
Comment thread electron/lib/tool-schemas.ts Outdated
Comment thread electron/mcp/tools/notes.ts Outdated
Comment thread electron/mcp/tools/notes.ts Outdated
Comment thread electron/shared/read-tools-pure.ts
- Enhance `search_notes` validation and logic:
    - Enforce non-negative integers for pagination.
    - Improve ISO datetime validation for `updatedAfter`.
    - Fix date comparison logic by converting to timestamps.
    - Add deterministic test cases for search pagination.
- Strengthen `rename_note` atomicity:
    - Add duplicate title collision check before performing updates.
    - Decouple file system writes from the database transaction to prevent inconsistencies during rollbacks.
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.

1 participant