Commit4d7f65d2Recorded27 Apr 2026Repositorybureau

v0.2.3: bump sigil-caldav to 0.10.1 for DST-aware recurrence

Message

Recurring CalDAV events tagged with TZID=Europe/Athens (or any tz) that were originally serialized when Athens was on standard time (EET=UTC+2) rendered with the wrong wall-clock during DST viewing (EEST=UTC+3). The renderer's underlying recurrence walker in sigil-caldav <= 0.10.0 advanced occurrences by 86400-second multiples in UTC, preserving the UTC instant across DST transitions and drifting the wall-clock by the DST shift.

Per RFC 5545, a TZID-tagged DTSTART anchors the wall-clock in that tz; each occurrence's UTC instant must be re-resolved per its actual fire date so the OS zoneinfo can apply the correct offset. sigil-caldav v0.10.1 implements this via add-days-in-tz / add-months-in-tz built on list->time-in-tz. Bureau picks up the fix transitively — no bureau-side renderer changes required.

Adds a regression test that mirrors the live CodeSignal Simulation event: weekly TZID=Europe/Athens master at 17:00 EET in winter, expanded to Apr 28 2026 (EEST), must produce 14:00 UTC (= 17:00 EEST wall-clock). Test passes under sigil >= 0.14.4 binaries; under the v0.14.3 test runner it fails because of unrelated time.c regressions (Bug 1/2 from sigil-lang v0.14.4's notes — fixed in the deployed bureau binary which statically links sigil-stdlib v0.14.7 through the refreshed lockfile).

sigil.lock: sigil-caldav 0.10.0 -> 0.10.1, sigil-stdlib v0.14.3 -> v0.14.7 (transitive auto-derive bump).

Changed
 package.sgl          |  4 ++--
 sigil.lock           | 23 +++++++++++------------
 test/test-bureau.sgl | 27 +++++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 14 deletions(-)
Diff
package.sglmodified
@@ -5,7 +5,7 @@
5
6
(package
7
name: "bureau"
8
version: "0.2.2"
+8
version: "0.2.3"
9
sigil: "^0.14"
10
description: "MCP server for Fastmail email and calendar"
11
url: "https://codeberg.org/sigil/bureau"
@@ -42,7 +42,7 @@
42
(from-git url: "codeberg:sigil/sigil-log" version: "^0.14.0")
43
(from-git url: "codeberg:sigil/sigil-mcp" version: "^0.14.0")
44
(from-git url: "codeberg:sigil/sigil-jmap" version: "^0.9.1")
45
(from-git url: "codeberg:sigil/sigil-caldav" version: "^0.9.1"))
+45
(from-git url: "codeberg:sigil/sigil-caldav" version: "^0.10.1"))
46
47
tasks: (list
48
(task
sigil.lockmodified
@@ -3,9 +3,9 @@
3
(package name: "sigil-stdlib"
4
url: "codeberg:sigil/sigil-lang"
5
ref: "^0.14"
6
sha: "9f34f58f175fef93c1322ab3522e85eee2809450"
+6
sha: "7739f35a54d2eb03e3e79ce1eb1eb366326a23db"
7
package-selector: "sigil-stdlib"
8
version: "0.14.3")
+8
version: "0.14.7")
9
(package name: "sigil-json"
10
url: "codeberg:sigil/sigil-json"
11
ref: "^0.14.0"
@@ -28,22 +28,21 @@
28
version: "0.9.1")
29
(package name: "sigil-caldav"
30
url: "codeberg:sigil/sigil-caldav"
31
ref: "^0.9.1"
32
sha: "24bd34009ba324f201dea3e9e7e221cceddae9fa"
33
version: "0.9.1")
+31
ref: "^0.10.1"
+32
sha: "878f163835e1e4f3fcb38973861cb30eeafbb262"
+33
version: "0.10.1")
34
(package name: "sigil-http"
35
url: "codeberg:sigil/sigil"
36
ref: "^0.9.0"
37
sha: "0123d3fc7d2dd37e5c27e9fade7d11b817deb21a"
38
package-selector: "sigil-http")
39
(package name: "sigil-crypto"
40
url: "codeberg:sigil/sigil"
41
ref: "^0.9.0"
42
sha: "0123d3fc7d2dd37e5c27e9fade7d11b817deb21a"
43
package-selector: "sigil-crypto")
+40
url: "codeberg:sigil/sigil-crypto"
+41
ref: "^0.14.0"
+42
sha: "0123d3fc7d2dd37e5c27e9fade7d11b817deb21a")
43
(package name: "sigil-sxml"
44
url: "codeberg:sigil/sigil-sxml"
46
ref: "^0.9.0"
47
sha: "081b6337c28d2ae55cd7658002cd6bc5e79ea07b"
48
version: "0.9.1")
+45
ref: "^0.14.0"
+46
sha: "a33eb522bfd485dcc490c8db5ea599d7215027ff"
+47
version: "0.14.0")
48
)
test/test-bureau.sglmodified
@@ -250,6 +250,33 @@
250
(assert-true (string-contains? result "Team standup"))
251
(assert-true (string-contains? result "Room 42")))))
252
+253
(test "TZID-tagged recurring event keeps wall-clock across DST"
+254
;; Regression for the CodeSignal Simulation/Team-coffee miscount.
+255
;; A weekly meeting with TZID=Europe/Athens and a winter master
+256
;; (17:00 EET = 15:00 UTC on Jan 13 2026) must render Apr 28's
+257
;; occurrence at 17:00 EEST (14:00 UTC), not 18:00 (the UTC-naive
+258
;; walker's drift) and not 16:00 (the EET-offset rendering of the
+259
;; correct UTC observed on the broken sigil-stdlib v0.14.3).
+260
(let* ((ics (string-append
+261
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\n"
+262
"UID:[email protected]\r\n"
+263
"DTSTART;TZID=Europe/Athens:20260113T170000\r\n"
+264
"DTEND;TZID=Europe/Athens:20260113T180000\r\n"
+265
"SUMMARY:Simulation - Estimation/Planning\r\n"
+266
"RRULE:FREQ=WEEKLY;BYDAY=TU\r\n"
+267
"END:VEVENT\r\nEND:VCALENDAR"))
+268
(master (car (ical-parse ics)))
+269
(range-start (parse-ical-datetime "20260428T000000Z"))
+270
(range-end (parse-ical-datetime "20260429T000000Z"))
+271
(occurrences (ical-expand-recurrence master
+272
start: range-start
+273
end: range-end)))
+274
(assert-equal 1 (length occurrences))
+275
;; 14:00 UTC is the wall-clock anchor 17:00 in EEST (UTC+3).
+276
(assert-equal "20260428T140000Z"
+277
(format-ical-datetime
+278
(ical-event-dtstart (car occurrences))))))
+279
280
(test "event without location"
281
(let* ((ts-start (parse-iso8601 "2026-03-14T14:00:00Z"))
282
(event (ical-event summary: "Focus time"