Skip to content

feat: probe search.text.uid - #47

Merged
tobixen merged 14 commits into
mainfrom
work/search-uid-pr
Oct 30, 2026
Merged

tobixen merged 14 commits into
mainfrom
work/search-uid-pr

Conversation

@tobixen

@tobixen tobixen commented Oct 28, 2026

Copy link
Copy Markdown
Member

Commit message:

feat: probe search.text.uid

Check whether a calendar-query with a UID filter answers the one object
with that UID.  iCloud fails it with 412 (ungraceful); a server ignoring
the filter returns everything (unsupported); an empty answer, or the
wrong object, is broken.
Needs the feature from the matching caldav change, so the tests skip
without it.  See https://github.com/python-caldav/caldav/issues/783

Prompt: (paraphrased) Add the feature search.text.uid to compatibility_hints.py and create checks for it.

Assisted-By: Claude Sonnet 5.5
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>

CheckDeleteIfMatch, CheckFloatingEvent and CheckAttendeeParams, for
three SOGo behaviours.  The floating probe reads the server's raw text:
icalendar 7.3.0 writes a TZID=Etc/UTC time back with a 'Z', so a parsed
object looks broken where SOGo's stored text is valid.  Baikal, Radicale
and Xandikos come out full on all three.

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>
CheckSyncMembers (what a sync report lists), CheckMultiget (a missing
href, and limits on big multiget and PROPFIND answers, probed only
with CALDAV_SERVER_TESTER_LIMIT_OBJECTS set), and two extensions:
CheckDeleteIfMatch tries a DELETE of a missing object, CheckPutEtag
compares the stored bytes with what was sent (broken where a
rewritten object gets a strong ETag).  The created-and-deleted member
gets a sync interval of its own: Stalwart reports it only beside a
change to a member it already had.  The raw sync report helpers
move to a _RawSync mixin shared with CheckSyncTokenLimit.

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".
> (on Stalwart reporting created-and-deleted correctly, unlike calendaring-sync's page) Do more research, maybe?  Report back in the pull request, tag sashank
Followup-Prompt: yes [go ahead with broken for a rewrite under a strong ETag, then the rest of the handover]
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 checked.

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
The entries for delete.missing.if-match, the sync members and
multiget.missing-href now say what the probes grade after the review:
the If-Match answer is compared with the plain DELETE's, a re-created
member reported only as removed is named, and a missing href answered
as if it existed is broken.  Purelymail and Infomaniak join the 412
list.

Prompt: (...) Add that commit to feat/sync-multiget-probes. (...) [the CHANGELOG catch-up the end-of-session audit proposed]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Whether to probe a sync token whose history the server discarded.
Stalwart can be made to do it, but only through an admin purge the
checker cannot make as a client, so it is left as an open question
with the arguments against it.

Prompt: (...) File the Stalwart problem in such a way that I can consider it later - we shouldn't have too many server-special quirks in the checker though. (...) [expired sync tokens via Stalwart's DataRetention.maxChangesHistory]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
caldav's propfind() parses the reply at once and raises on the 507
Stalwart adds for the collection, so the probe saw an exception where
there was a 207 listing 50 members and graded it as a 507 for the
whole PROPFIND, a description that then reached the caldav profile.
The PROPFIND now goes out raw, as the multiget does, and the cap is
reported as the number of members listed, since it counts the
calendar's other members too.  A 507 for the whole answer still reads
as unsupported.

Prompt: See [the caldav review findings document] - the other session claims that the actual probing of Stalwart is inconsistently described now.  Is there anything in the context or in the files in this project that can shed a light on it? [it found the propfind.limit description and the Stalwart profile disagreeing]
Followup-Prompt: yes [fix the probe here; pass the profile wording and a possible caldav issue to the other session]

Assisted-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
save-load.icalendar.participant: an event carrying an RFC 9073
PARTICIPANT is stored (CCS and Bedework 3 answer 403, robur 400, GMX
and Ox 500).  save-load.icalendar.vtimezone: the VTIMEZONE sent with an
event comes back (CCS drops it, keeping the TZID).  The PUT-and-read-
back skeleton the floating and attendee-params probes each had is now
one base class, _RoundTrip, shared by all four.

Fixes #38
Fixes #39

Prompt: (...) But those issues you just filed seems like things that are needed for the conformance tests, so please work on them. [#38, /39, /40, /41 and python-caldav/caldav#829]

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
search.limit: a calendar-query for every event, sent along with the
multiget and PROPFIND limit probes, so it shares their objects and
CALDAV_SERVER_TESTER_LIMIT_OBJECTS.  Stalwart stops at its maxResults
with a 507; Baikal and CCS answered all 60.

Fixes #41

Prompt: (...) But those issues you just filed seems like things that are needed for the conformance tests, so please work on them. [#38, /39, /40, /41 and python-caldav/caldav#829]

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Four round-trip probes, for an X- property (in an event and in a task),
an unknown property (ESTIMATED-DURATION), an ATTENDEE on a task, and
COMPLETED on a task without STATUS.  Ox fails all four; the other
docker servers keep everything.  A task Ox answers with 201 and then
404 is graded broken, but only where the same task without the
ATTENDEE reads back.  _RoundTrip now stores a task in the task
calendar, and where tasks cannot be stored grades a task-only feature
outside save-load.todo unknown.

Fixes #40

Prompt: (...) But those issues you just filed seems like things that are needed for the conformance tests, so please work on them. [#38, /39, /40, /41 and python-caldav/caldav#829]

Prompts (comments made inline in the document while reviewing it):
> (on whether a task that vanishes after a 201 is 'unsupported' or 'broken') broken

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Two findings from the compset-multiple handover that no issue or
TODO entry tracked: Bedework refuses tzdata link names as TZID, and
the delete.if-match hint has no consumer.

Prompt: Probably the "also found"-items in the old handover doc is on the github issue tracker, please verify.  (...)  And continue working with the handover. [the handover of a previous session listed Bedework TZID link names and delete.if-match among findings; those two were on no tracker]
Followup-Prompt: fold #1 into an open PR. [the TODO commit sat on a local branch; it goes onto the VTIMEZONE probe PR]

Prompts (comments made inline in the document while reviewing it):
> (on a review finding about the TODO naming no project) Add a link to github pycalendar/calendaring

Assisted-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
#37

Adds CheckSyncMembers and CheckMultiget (a missing href, and limits on
big multiget and PROPFIND answers), a DELETE of a missing object with
If-Match, and a check that a rewritten object does not get a strong ETag.

Commits:
- db6a774 feat: probe sync, multiget and DELETE quirks
- 7726ab5 docs: CHANGELOG follows the probe review fixes
- f8cd221 docs: TODO on probing expired sync tokens
- 21671b6 fix: read the PROPFIND limit from the raw reply

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
…IMEZONE, search limit, Ox losses

#42

Probes what calendaring's conformance suite ran into: PARTICIPANT and
VTIMEZONE round trips, a result limit on calendar-query, and properties
and task data that Ox drops silently.

Commits:
- 0c246fc feat: probe PARTICIPANT and VTIMEZONE round trips
- 536d943 feat: probe a result limit on calendar-query
- 5f6c271 feat: probe data dropped silently from objects
- f9b5529 docs: TODO on Bedework TZIDs and delete.if-match

Fixes #38
Fixes #39
Fixes #40
Fixes #41

Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
The component-set probe folded three questions into one grade.  It
now sets supported-components.property-exists (the former
get-supported-components, read from the test calendar) and .honoured
(a calendar takes exactly what it lists, RFC 4791 section 5.2.3), and
create-calendar.with-supported-component-types asks again whether
MKCALENDAR yields the requested set.  A new .multiple child asks the
same for a set of several types.  Bedework 3.10, DAViCal and Stalwart
are honoured: broken.  Zimbra's VFREEBUSY is ignored, and a VJOURNAL
is saved too.  Needs caldav 3.7.0, which has the
supported-components features.

Prompt: Do proper research and fix the probe now [the component-set probe graded Bedework 3.10 full although a VTODO-only calendar there still accepts a VEVENT]
Followup-Prompt: Perhaps we need several children features here to catch all the degrees of behaviour?
Followup-Prompt: Perhaps put in a comment note in the compatibility matrix that the proposed .multiple that this is already covered by save-load.todo.mixed-calendar [superseded below: .multiple became a feature of its own]
Followup-Prompt: go for version 2 then [existence and obedience as sibling features under a grouping node, creation kept apart]
Followup-Prompt: go for it, and then implement [after an independent review of the design]
Followup-Prompt: finish it yourself [the tester side, after the implementing agent hit its usage limit]
Followup-Prompt: yes, zimbra should be up now.  nextcloud should also be up?  The pin can be bumped.  It can be tested locally towards the local caldav installation, and on github the pin for caldav ... it should be 3.7.0 shouldn't it?  ... will cause the integration tests to fail fast rather than slow

Prompts (comments made inline the "proposed alternative solutions" document while reviewing it):
> (on RFC 4791 section 5.2.3) I read [this] that the existence of the [supported-components] property is one feature, enforcement of the property is a subfeature.
> then comes the problem when the feature is unsupported ... then that "unlocks" another feature  "the server does accept all component types".  This is not very easy to squeeze into the feature tree as it is.  We had some similar problem with the etag given on servers rewriting vs not rewriting the icalendar data, please check up if we can do something similar here.
> [and] This has per se nothing to do with calendar creation if I understand it correctly.
> (on RFC 4791 section 5.3.1.1) [Therefore we should have a] child property under calendar creation on weather (sic) the calendar creation matches the support levels of supported-calendar-component
> Also, although .multiple is related to (sic) .mixed-calendar it's not the same thing being measured, so we should add this one too.
> Please consider an "option E" based on this
Prompts (2nd iteration):
> (on the proposed feature tree) I'm not sure if this is possible.  If get-supported-components is unsupported, the default for get-supported-components.honoured will also be unsupported.
Prompt (on a suggestion to regrade Robur in the matrix in the caldav library):
> (on a review finding about Robur's honoured hint) This should be fixed in caldav-server-tester - the features defined for a server can only deviate if one of the sides returns "fragile" or "unknown".  If the caldav-server-tester tests only with VTODO and not VJOURNAL, it needs fixing.
> (I also argued for raising the caldav version pin to 3.7.0 - it doesn't matter during local testing, but it makes the github CI fail fast instead of churning CPU and then failing.  Probably a longer-term solution should be made here, this is a constant itch)

Assisted-By: Claude Sonnet 5.5, Claude Opus 5.5
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
save.no-uid-conflict is graded by a plain PUT, and a refusal that is
not the RFC's 403/409 with the precondition is a 'quirk' with the keys
'status' and 'precondition' - without the precondition only if nothing
was stored and another UID goes through.  New children: .if-none-match
(Bedework's bare 412) and .href-given.

Prompt: Please consider the `save.no-uid-conflict` "feature".  The last
two commits were done by Sonnet, but I think they should be rolled
back or amended.  I've written some thoughts in [a notes file], please
read through.  If it makes sense, then see if you can do a better
job on implementing it.  If something is unclear or not well enough
thought through, get back to me.
Prompt: (the notes: a bare refusal is a 'quirk' with keys for status
and XML element, after the tester checks that GET gives 404 and the
same PUT with another UID succeeds; a href-given child)
Prompts (comments made inline in the document while reviewing it):
> Fair point - leave `.no-uid-conflict` ungraceful for servers reporting
> 403 with no XML error.  (...)
> I think this is a feature (it obeys "SHOULD" in the RFC) and not a
> behaviour.
> I agree with most of the proposals/recommendations in this document,
> so go ahead and implement.

Assisted-By: Claude Opus 5.5
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
Check whether a calendar-query with a UID filter answers the one object
with that UID.  iCloud fails it with 412 (ungraceful); a server ignoring
the filter returns everything (unsupported); an empty answer, or the
wrong object, is broken.
Needs the feature from the matching caldav change, so the tests skip
without it.  See python-caldav/caldav#783

Prompt: (paraphrased) Add the feature search.text.uid to compatibility_hints.py and create checks for it.

Assisted-By: Claude Sonnet 5.5
Reviewed-by: Tobias Brox <tobias@redpill-linpro.com>
@tobixen
tobixen force-pushed the work/search-uid-pr branch from f2b74a0 to 11bf74d Compare October 30, 2026 11:07
@tobixen
tobixen merged commit 6f94cb2 into main Oct 30, 2026
12 checks passed
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