Two themes: closing the 1.10.2 sync-robustness hotfix loop (the three follow-ups filed against #105 / #106 / #107) and turning the booking-frequency-limits surface from a half-wired feature into a real one — first by hiding capped slots in the picker, then by adding per-team-member caps.
Added
- Hide booking slots when a frequency cap is reached (closes #115, PR #116) —
compute_slotsnow runsapply_frequency_limit_filterafter slot generation: for each configured(max, period)it counts existing confirmed+pending bookings per containing host-local period, then drops slots that fall in any capped period. The submit-time check (would_exceed_frequency_limit) stays as a race-condition backstop, but the picker no longer shows times the submit-time check would reject. Limit-reached page also got proper styling (template render instead of bareHtml("...")) - Per-member booking frequency limits (closes #117, PR #118) — new
booking_frequency_limits.per_memberflag (migration 051) so a host can express "1 demo/day per team member" instead of "1 demo/day team-wide". Threaded through three sites:would_exceed_frequency_limittakesOption<&str> assigned_user_idand scopes the count by assignee;pick_group_memberexcludes candidates already at their per-member cap so the picker doesn't route to a doomed user;apply_frequency_limit_filterhides a slot only when every eligible team member is at cap. UI gets a "Per team member" checkbox on each limit row, hidden on personal event types
Fixed
- CalDAV resource is HEAD-checked before cancelling a booking (closes #105, PR #108) — sync-collection's "deleted" entries are now treated as a hint, not a verdict: before cancelling a confirmed booking we HEAD the resource href and only act if the server confirms 404. Two new regression tests (one for the BlueMind phantom-deletion case, one for the legitimate deletion case)
cancel_orphaned_bookingis scoped to its own source/account (closes #106, PR #111) — previously did a global UID-only lookup against the bookings table, so a sync running on source A could cancel a booking whose CalDAV event lived under source B (different calendar, different account). Now joins throughevent_types → accounts → caldav_sourcesand only acts on rows whose source matches the one currently syncing- Property-level 404s inside
<d:propstat>are ignored (closes #107, PR #109) — the sync-collection parser previously treated any 404 status code inside a<d:response>as a deletion, including the per-property 404 some servers emit when one of the requested DAV properties is absent on an otherwise-live resource. Parser now distinguishes resource-level status from propstat-level status and only routes resource-level 404s into the deletion handler - Team event type frequency limits actually persist (PR #114) —
edit_group_event_type_formnever queriedbooking_frequency_limits, andcreate_group_event_type/update_group_event_typenever wrote to it. Toggling the cap on a team event type was a silent no-op. Three-way fix mirrors the working personal-event-type flow
Internal
- 650 tests total (up from 634 in 1.10.2), all green on pre-commit
- Migration 051 (
booking_frequency_limits.per_member) render_claim_errorrenamed torender_booking_action_errorsince the template (booking_action_error.html) isn't claim-specific- Coverage CI:
under_tarpaulin()now usescfg!(tarpaulin)(the previousCARGO_TARPAULIN_VERSIONenv-var check never fired, so the racy tracing-capture tests were leaking through the supposed-to-skip guard);Cargo.tomlregisterscfg(tarpaulin)via theunexpected_cfgslint config