• bvandeusen released this 2026-04-29 16:04:58 -04:00 | 287 commits to main since this release

    Closes the 4/29 dentist-appointment investigation. Two complementary structural fixes plus the held-back EventSlideOver UX refactor.

    Event-store data integrity

    #160 — Duration replaces end_dt at the storage layer. Schema migration 0043 adds duration_minutes (with >= 0 CHECK constraint) and drops end_dt. Wire format unchanged — end_dt is now a derived @property. End-before-start is structurally inexpressible. The corrupt prod row that triggered this work backfills to duration_minutes=NULL automatically.

    Behavioral upgrade: moving an event's start now slides the effective end forward (preserving duration), instead of corrupting or hard-rejecting.

    #161 — Tool layer disambiguates multi-match. When update_event or delete_event get a query that matches 2+ events, the tool returns success: false with a candidates array. The model picks one by passing event_id on the next call. Pre-fix: both tools silently took matches[0], which is exactly how the dentist event got applied to the wrong row.

    Calendar UX

    EventSlideOver redesigned as a centered modal. Auto-save on close (X / Esc / backdrop click / Enter in form). Trash icon in the header (edit mode only). Invalid form in edit mode → toast + discard; in create mode → silent discard. Removes the right-edge floor band where the destructive Delete button was visually invisible.

    Reverted in-flight

    94b169f (A+B hotfix) is reverted by b7e7073 — superseded by #160. The hotfix made bad data filterable; #160 makes it inexpressible.


    46 calendar/events tests pass. Ruff clean. Schema migration runs automatically on container restart. Tag push triggers the build.

    Downloads