Skip to content

feat: compatibility hints for sync, multiget and DELETE quirks - #822

Merged
tobixen merged 5 commits into
masterfrom
feat/sync-multiget-hints
Oct 24, 2026
Merged

tobixen merged 5 commits into
masterfrom
feat/sync-multiget-hints

Conversation

@tobixen

@tobixen tobixen commented Oct 24, 2026

Copy link
Copy Markdown
Member

Disclaimer: AI-generated code and text below the line, generated by Claude Opus 5.5, from the prompts:

Have a look into https://github.com/PyCalendar/calendaring-sync/blob/c40782bc7afe7b350fe63bf27ef13374c280ea8b/docs/reference/server-compatibility.rst and see if there is anything we should add to the tests from that page
All of them should be probed. Please consider what feature tags we should put the results under and open up suggestions in my editor.
work on /tmp-handover-sogo-hints-2026-10-23.md (sic) [a handover note asking for probes and caldav hints for three SOGo behaviours found by calendaring's conformance suite]

(the commits carry the full prompt record)


Stacked on #821; review that first.

Compatibility features and server profiles for behaviours a sync client depends on, found by calendaring-sync's server compatibility page and measured with caldav-server-tester on 22 servers:

  • SOGo: delete.if-match, save-load.event.floating and save-load.icalendar.attendee-params.
  • DELETE of a missing object: delete.missing and delete.missing.if-match.
  • Sync members: sync-token.initial-sync, sync-token.delete.created-and-deleted and sync-token.recreated.
  • Multiget and PROPFIND: multiget.missing-href, multiget.limit and propfind.limit.
  • Rewritten objects: save.etag.matches-stored, a server rewriting what it stores while answering with a strong ETag.

The Stalwart test container now runs with WebDav.maxResults 50, so the limits can be probed with few objects.

Infomaniak loses its ungraceful sync-token.delete hint: with #821 its deletions sync fine.

Infomaniak (SabreDAV) answers a member removed since the last sync
token with a response-level 404 and an empty propstat with 418.  The
legacy parser validated that propstat and raised, so every sync after
a deletion failed.  A 404 response now has its propstats dropped.

Prompt: [follow-up on a handover document from an earlier session, which reported the 418 issue:] caldav library bugs should be fixed - if it's too heavy lifting for an already overloaded session the right fix is to file it.
Followup-Prompt: Other session should be done now, it's at the end of the push gate logic.  When doing changes in the caldav library, please do it at the top of the pull request it makes, to avoid having to rebase pull requests and rerun pipelines
Followup-Prompt: yes [go ahead with the plan, including fixing the 418 bug]
Followup-Prompt: the other session has merged #820.  Create a new PR. (...) [#820 is #820]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Three compatibility features, each with a probe in
caldav-server-tester: delete.if-match (SOGo deletes on a stale
If-Match), save-load.event.floating (SOGo pins a floating event to
TZID=Etc/UTC) and save-load.icalendar.attendee-params (SOGo adds
RSVP=TRUE).  The SOGo profile gets the measured verdicts.

Prompt: work on /tmp-handover-sogo-hints-2026-10-23.md (sic) [a handover note asking for probes and caldav hints for three SOGo behaviours found by calendaring's conformance suite: DELETE ignoring a stale If-Match, floating event times stored as UTC, ATTENDEE parameters rewritten]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Compatibility features for behaviours a sync client depends on, as
found by calendaring-sync: delete.missing(.if-match), the sync-token
initial-sync, created-and-deleted and recreated features, multiget
(missing-href, limit), propfind.limit and save.etag.matches-stored.
The Stalwart test container gets WebDav.maxResults 50, so the limits
can be probed with few objects.  Profiles are not updated yet.

Prompt: Have a look into https://github.com/PyCalendar/calendaring-sync/blob/c40782bc7afe7b350fe63bf27ef13374c280ea8b/docs/reference/server-compatibility.rst and see if there is anything we should add to the tests from that page
Followup-Prompt: All of them should be probed.  Please consider what feature tags we should put the results under and open up suggestions in my editor.
Prompts (comments made inline in the document while reviewing it):
> (on the DELETE with If-Match on a missing object) We don't have any other features under delete.* yet.  There is a sync-token.delete and delete-calendarxo (sic) ... but I have no better ideas than the recommended.  It's Robur that is the special case with non-existing-raises-not-found.object - does robur return 404 when trying to delete the object?  If it returns 403, then use the alternative.
> (on calendar-multiget) The check for stalwarts (sic) page problem should also be added
> (on the initial sync) We have a sync-token.limit.initial-sync, should it be renamed sync-token.initial-sync.limit ?
> (on the order of work) ok
> check the external servers too, defined under ~/caldav-server-tester
Followup-Prompt: (answer to a clarifying question) Keep the name [sync-token.limit.initial-sync]
Followup-Prompt: (answer to a clarifying question) The checker code should be generic.  If testing with 60 items and the limit was set down to 50 in the stalwart image, then we're confirming the problem with stalwart, but we're not checking anything on any other servers.  I think we lower it in docker and have an environment variable to set the number of items probed.  (wasn't there also a limit header or xml attribute that could be sent?)
Followup-Prompt: if foo.bar is unsupported, but foo.bar.zoo is suppored (sic), then that may be a hint that the hierarchy is not well-enough thought through.  foo.bar.zoo should typically be dependent on foo.bar
Followup-Prompt: (answer to a clarifying question) save.etag.matches-stored
Followup-Prompt: (summary: open the handover document about the sync probes in the editor; inline comments follow)
Prompts (comments made inline in the document while reviewing it):
> (on dropping the child feature) This makes sense - except, when too many different behaviours gets (sic) packed into one feature it often makes sense to split it up into children.  The problem here is that we're potentially dealing with a child that is "Not Applicable" if the parent is unsupported, can be "Not Applicable" if the parent is supported and can never be unsupported, only broken.  This simply does not fit into the feature hiearchy (sic) as it was designed.  There is no grading "Not Applicable".  Maybe the solution is to add such a grading.
> matches-stored ... that may make sense.  "supported" if stored is not rewritten.   "supported" if stored is rewritten and the etag is weak or not given.  "broken" if stored is rewritten and etag is strong.  Am I right?
> (on servers rewriting with a strong ETag) So they would get "broken"
> (on servers rewriting without a strong ETag) And they would get "supported".
> (on servers storing as sent) And they would also get "supported".
Followup-Prompt: yes [go ahead with broken for a rewrite under a strong ETag, then the rest of the handover]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
What caldav-server-tester measured on 22 servers for the features of
the previous commit, as profile entries: DELETE If-Match broken or
412 on a missing object, missing hrefs left out of a multiget, the
initial-sync, created-and-deleted and recreated quirks, Stalwart's
limits, and the servers that rewrite an object under a strong ETag.
Every entry was re-checked by a tester run against the profile.
Infomaniak loses its ungraceful sync-token.delete: with the parser
fix below this commit, its deletions sync fine.

Prompt: (summary: open the handover document about the sync probes in the editor; inline comments follow)
Prompts (comments made inline in the document while reviewing it):
> matches-stored ... that may make sense.  "supported" if stored is not rewritten.   "supported" if stored is rewritten and the etag is weak or not given.  "broken" if stored is rewritten and etag is strong.  Am I right?
> (on servers rewriting with a strong ETag) So they would get "broken"
> (on Stalwart reporting created-and-deleted correctly, unlike calendaring-sync's page) Do more research, maybe?  Report back in the pull request, tag sashank
> (on the Infomaniak 418 finding) caldav library bugs should be fixed - if it's too heavy lifting for an already overloaded session the right fix is to file it.
Followup-Prompt: yes [go ahead with broken for a rewrite under a strong ETag, then the rest of the handover, which lists writing these profiles as a next step]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
save-load.event.floating named the time of calendaring-sync's example
event (09:00 UTC); what SOGo does is pin any wall-clock time to UTC.
delete.missing.if-match now says the conditional DELETE answers as the
plain one does, as RFC 9110 section 13.2.1 has it and as the tester now
grades it, rather than always 404.

Prompt: [fix issues found during the review process]
Prompts (comments made inline in the document while reviewing it):
> (on grading delete.missing.if-match against the plain DELETE's answer) Make sure both `delete.missing` and `delete.missing.if-match` is (sic) checked.

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
@tobixen
tobixen changed the base branch from fix/sync-404-propstats to master October 24, 2026 13:13
@tobixen
tobixen merged commit 7c99da5 into master Oct 24, 2026
2 checks passed
@tobixen
tobixen deleted the feat/sync-multiget-hints branch October 25, 2026 18:00
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