Read only: a conflict that ours wins is recorded again on every run, growing contact notes and the timeline #21
Labels
No labels
bug
documentation
enhancement
performance
security
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-dav#21
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo-dav at
97abc9779, reading the conflict branches of the sync and running them against the test suite's fake server.When a record changed on both sides and the local change is the later one, the sync writes the losing remote version down (a note on the contact, an entry on the application's timeline) and sets the link's ETag to the remote one only in memory, counting on the push that follows to save the link. In read-only mode that push is skipped before anything is saved, so the next run sees the same remote change as new and writes the same note again. It does so on every run, for as long as read-only stays on.
src/postulo_dav/sync.py:472-484(_sync_contacts: appends "Changed on the phone on …" tocontact.notes, saves the contact, thenlink.etag = resource.etagwithout saving the link);src/postulo_dav/sync.py:498-501(if read_only: report.skipped += 1; continue);src/postulo_dav/sync.py:611-618(_sync_interviews:record_event(... "Changed on the phone, but changed here later; kept ours."), thenlink.etag = resource.etag);src/postulo_dav/sync.py:637-640(the same read-only skip).updated_atto now.updated_atwas just moved, ours always wins again.Reproduced: three read-only runs left three "Changed on the phone on" paragraphs in the contact's notes, with the link's ETag still
"v1"; four runs on an interview left four "kept ours" entries on the timeline. Each run reports only "1 skipped". The same repetition follows outside read-only mode whenever the push after an ours-wins decision fails: aDavError(for example a server refusing the PUT) propagates after the note is saved, so the next run records it again.link.etag = resource.etag; link.save(update_fields=["etag", "updated_at"])) in both branches, before deciding whether to push. A skipped or failed push then leaves the local change pending without recording the same conflict again.Done when
tests/test_dav.py, one for a contact and one for an interview, that create a both-sides change with ours later on a read-only connection, sync three times, and assert exactly one note paragraph and exactly one "kept ours" timeline entry.Fixed by pull request #35, merged as
fb8bbd7ae.