Skip to content

fix: stop IdeaLoom sync from resurrecting a note you deleted in Obsidian - #5372

Merged
atomantic merged 1 commit into
mainfrom
next/issue-5339
Aug 29, 2026
Merged

fix: stop IdeaLoom sync from resurrecting a note you deleted in Obsidian#5372
atomantic merged 1 commit into
mainfrom
next/issue-5339

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

  • Deletion is a decision, not drift. A previously-exchanged list whose vault note is gone now reports a distinct missing outcome and writes nothing. Before, exportOne re-created the note on the next write, resurrecting data the user deleted on purpose. Recreating it takes an explicit request (recreateMissing), surfaced as a "Recreate N deleted notes" action that only appears after an exchange reported one.
  • unavailablemissing. The import-side probe uses existsSync rather than the scan result, so an iCloud note that has not been downloaded stays unavailable and is never offered as a recovery candidate.
  • Auto-sync is now real, and is the least powerful path in the feature. autoSync was a stored setting nothing read. It now debounces a burst of edits into one vault write, re-reads both toggles when the write fires (so turning either off cancels queued work), and calls the exchange with the fail-closed defaults — it cannot delete a note, recreate one, or resolve a conflict.
  • No import/export feedback loop. Only the two local-write routes schedule an export; import and sync deliberately do not. Additionally, an export whose rendered Markdown already hashes equal to the note on disk is skipped rather than rewritten, so a freshly imported list cannot export itself back.
  • UI: the integration settings (enable → pick vault → enable auto-sync) are reachable for the first time; updateIdeaLoomSettings previously had no caller. The exchange summary now names every outcome including missing.
  • Docs: docs/features/brain-system.md gains the base-hash conflict table, the deletion contract, and the auto-sync limits — replacing the paragraph that deferred all three to this slice. docs/API.md names the sync body flags; docs/STORAGE.md records the conflict/deletion behavior and backup coverage.

Closes #5339.

Test plan

  • server/services/idealoomAutoSync.test.js (new): debounce coalescing, fail-closed on each of the three toggles, toggles re-read at fire time, recreateMissing never passed, a failed export not crashing the process, unresolved outcomes warned. Fake timers throughout — no production sleeps.
  • server/services/idealoomObsidian.test.js: externally deleted note → missing with nothing written, then recreated under recreateMissing; a deleted note surfaced on import without dropping the local list; a note already matching the list is skipped, not rewritten.
  • server/routes/brain.test.js: recreateMissing defaults to false and threads through when set; auto-sync is scheduled by create/update only — never by delete, import, or sync.
  • client/src/components/brain/IdeaLoomLists.test.jsx: the auto-sync toggle is unreachable until sync is enabled and a vault is chosen; recovery is offered only after an exchange reports a deleted note, and the plain export does not ask for a recreate.
  • Full server (35,453) and client (10,223) suites pass; client biome lint clean; API route catalog regenerated.

…ian (#5339)

A list that had already been exchanged kept its note path forever, so the next
export re-created a note the user had deleted in the vault. Deleting the note
is now a decision, not drift to repair: both import and export report it as a
distinct `missing` outcome and write nothing, and only an explicit "Recreate
deleted notes" request (`recreateMissing`) writes it again. An iCloud note that
is merely un-downloaded stays `unavailable`, so it is never a recovery
candidate.

Adds the opt-in automatic export the storage slice left as a dead setting:
`autoSync` now debounces a burst of edits into one vault write, re-reads both
toggles when the write fires, and calls the exchange with the fail-closed
defaults, so it can never delete a note, recreate one, or resolve a conflict.
Only a local list edit schedules it — the import and sync routes deliberately
do not — and an export whose rendered Markdown already matches the note on disk
is skipped, so a freshly imported list cannot export itself back.

Documents the base-hash conflict table, the deletion contract, and the
automatic-sync limits in the Brain, API, and storage docs, which previously
deferred all three to this slice.

Closes #5339
@atomantic
atomantic merged commit 83b22ce into main Aug 29, 2026
7 checks passed
@atomantic
atomantic deleted the next/issue-5339 branch August 29, 2026 05:33
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.

Make IdeaLoom Obsidian sync conflict-safe and document its contract

1 participant