diff --git a/src/thoughtsync/sync.py b/src/thoughtsync/sync.py index 30a2fcd..5b09756 100644 --- a/src/thoughtsync/sync.py +++ b/src/thoughtsync/sync.py @@ -209,12 +209,22 @@ def _assign_note_fields(note: Note, ch: dict) -> None: async def _apply_note_items(db, note: Note, ch: dict) -> None: - """Replace the note's checklist items with the client's (items sync inline).""" - if note.kind != "list": - await db.execute(sa_delete(NoteItem).where(NoteItem.note_id == note.id)) - return + """Replace the note's checklist items with the client's (items sync inline). + + Applies to ANY note. This used to delete every item when the note wasn't + `kind == "list"`, which was survivable only because nothing could produce a note + holding both a body and items. M13 makes that the normal shape — a checklist is + something a note HAS, not something a note IS — and against that shape the old + guard was a data-loss path: the first sync after adding a checklist to a note + would have wiped it. + + Removed ahead of the UI that can create the state, deliberately, so there is no + window in which the two disagree. + """ items = ch.get("items") if not isinstance(items, list): + # Absent means "not telling us", not "empty". A client that omits the key + # leaves what the server has; only an explicit [] clears it. return await db.execute(sa_delete(NoteItem).where(NoteItem.note_id == note.id)) for pos, it in enumerate(items):