fix(caldav): write ORGANIZER so an event with attendees invites somebody #40
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix-organizer"
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?
Closes #39. Not merged, per your instruction.
The fix
ORGANIZERset to the authenticated principal, taken from the identity the tool layer already holds. No new parameter, no caller change.Written only when there are attendees. Writing it on a solo event would make everything this tool creates a scheduling object in Stalwart's eyes, which changes what later edits do rather than fixing what this one does. That is a narrowing of the instruction and I would rather flag it than bury it.
Attendees with no organizer available are refused. A token carrying no email claim would otherwise recreate the original defect silently, and a refusal that names the reason is the only outcome that does not.
The control, which is the part worth reading
Before adding any test, I removed the new
ORGANIZERline and ran the pre-existing suite:Green with the bug and green without it. That is what let this ship, and it is why the acceptance had to be the stored object.
Mutations, each with the pattern asserted and the harness checked
ORGANIZERnot writtenan_event_with_attendees_carries_an_organizer_in_the_stored_object,create_event_puts_an_organizer_when_there_are_attendeesan_event_without_attendees_has_no_organizer, plus two pre-existing attendee-free testsORGANIZERdropped on readan_event_with_attendees_carries_an_organizer_in_the_stored_objectThe third matters because an organizer the parser drops is the same defect one layer over: the stored object would be right and every read would say
organizer: null.The wiremock test asserts the
PUTbody, and mounts a mock that only matches a body containing bothORGANIZERandATTENDEE, so a write missing either fails verification rather than passing quietly.Your two questions, answered here rather than deferred
Does updating an event with attendees issue a
REQUEST? For an event created after this fix, yes.patch_icsedits the master in place and passesORGANIZERthrough, so the object stays a scheduling object and the server's organizer diff fires. Pinned byan_update_carries_the_organizer_through.Does removing an attendee issue a
CANCEL? Same mechanism and the same answer, from Stalwart's organizer-side diff: an attendee present in the old object and absent in the new producesCANCEL. It needs the object to carryORGANIZER, which it now does.And the half neither question asked. Events created before this fix have no
ORGANIZER, so updating or deleting them still notifies nobody. I have not backfilled one on update, deliberately: doing so would start sending mail about events that have never notified anyone, which is the irrecoverable direction and exactly whatdelete_occurrence's refusal exists to prevent. The test pins that an update does not add one.Read path
Already surfaced.
organizeris parsed fromORGANIZERand returned on every event;organizer: nullwas accurate rather than a display bug.Gates
Toolchain read out of
.forgejo/workflows/ci.yml(toolchain: stable,rustc 1.98.0locally, current stable).fmt --check,clippy -D warnings,test --all-features(129 + 9),audit,deny— all green, all throughbuild-slot.sh.Merging. 129 + 9 passed here, and I ran the call-site mutation nobody had.
Nothing asserts that the handler supplies the principal's email, so the whole fix can be disconnected from its only caller and the suite says nothing. Sixth instance of that class this week and the first in this repository.
Merging anyway, and the reason is what it degrades to. With the organizer cut, the refusal branch fires and every event with attendees is refused with a clear message. That is loud rather than silent, so the untested wiring cannot reproduce the defect this issue is about. Same column as
matrix-mcp#144, opposite ofm365-mcp#28.File it with this mutation in it, and say that it degrades to a refusal rather than to the original fault, so nobody reads it as more urgent than it is.
What I checked rather than took
Your pre-emptive mutation is the measurement that justifies the whole shape. 124 passed with the bug and 124 passed without it, run before you wrote a test, which is why the acceptance had to be the stored object.
The read-side mutation is the one I would not have asked for. An
ORGANIZERthe parser drops leaves the stored object correct and every read sayingorganizer: null, which looks exactly like the bug still being there. Catching the defect one layer over, in the change that fixes it, is the thing this week keeps rewarding.A mock that only matches a body containing both
ORGANIZERandATTENDEEmakes a write missing either fail verification rather than pass quietly. An absent mock as the assertion.The narrowing and the deferral, both right
ORGANIZERonly when there are attendees, because writing it unconditionally makes every event a scheduling object and changes what later edits do. Attendees with no organizer refused rather than written, because a token with no email claim would otherwise recreate this defect silently. Flagged rather than buried, which is why it reads as a decision.Not backfilling
ORGANIZERon update of a pre-fix event is the half neither question asked, and the reasoning is the one that decides it: backfilling starts sending mail about events that have never notified anyone, which is the irrecoverable direction. Pinned by a test rather than left as an intention.