feat: iOS EventKit calendar payload — tags, location, status (Slice 3 of calendar sync payload) #244

Merged
owlburtoe merged 2 commits from feat/calendar-sync-slice-3-ios-payload into main 2026-05-28 17:23:27 -04:00
Owner

Slice 3 of the Calendar Sync Payload enrichment (spec: docs/superpowers/specs/2026-05-28-calendar-sync-payload-design.md, Section D). Builds on merged Slices 1 (#225, facility address) and 2 (#242, assignment tags). iOS-only; no backend/openapi contract change.

Changes

  • Payload: CalendarSyncPayload gains tags, location, and assignmentStatus (a calendar-seam CalendarAssignmentStatus enum mapped from the generated type — generated types stay out of the struct/formatters).
  • Formatters (new CalendarTitleFormatter.swift): pure CalendarTitleFormatter.format(base:tags:) (first 3 tags + , +N overflow) and CalendarNotesFormatter.format(scheduleName:tags:) (full uncapped tag list). Parity-locked by a shared JSON fixture (docs/specs/fixtures/calendar-renderer-fixtures.json) that the Slice 4A Node ICS renderer will also load.
  • Event construction: extracted to a pure, testable makeEvent; sets LOCATION, busy/tentative availability by status, and branches all-day (null start/end → single all-day day, no alarm) vs timed (30-min alarm, overnight roll preserved). All-day uses a same-day endDate — EventKit treats all-day end as inclusive, so the spec's ICS-flavored next-day end would render a bogus 2-day block.
  • Store: CalendarSyncStore fetches the dept facility once per session (cached by deptSlug, invalidated on actor change / logout, best-effort so a facility hiccup never blocks shift sync), maps formattedAddress → location and tags → tags.
  • ShiftdKit: facility(in:) decodes GET /api/d/{deptSlug}/facility into a flat DepartmentFacility DTO (the spec op is unnamed and FacilityWithAddress is an allOf schema the generator nests).

Test plan

  • App target: 145/145 ShiftdTests pass (new CalendarFormatterTests, EventKitCalendarSyncServiceTests, CalendarSyncMappingTests)
  • ShiftdKit: 50/50 pass
  • Parity fixture asserted exactly in Swift; same file feeds the Slice 4A Node renderer
  • Note: iOS is not in Forgejo CI, so these were validated locally via XcodeBuildMCP (iPhone 17 sim). PR CI exercises only backend/frontend, which are untouched.

Incidental

Second commit repairs two pre-existing iOS test literals that failed to compile once ShiftdKit regenerated: StaffUpcomingAssignment now requires tags[] (Slice 2) and Schedule requires coverageBasis. iOS test drift, unrelated to this feature, surfaced because there is no iOS CI.

Slice 3 of the Calendar Sync Payload enrichment (spec: docs/superpowers/specs/2026-05-28-calendar-sync-payload-design.md, Section D). Builds on merged Slices 1 (#225, facility address) and 2 (#242, assignment tags). iOS-only; no backend/openapi contract change. ## Changes - **Payload**: `CalendarSyncPayload` gains `tags`, `location`, and `assignmentStatus` (a calendar-seam `CalendarAssignmentStatus` enum mapped from the generated type — generated types stay out of the struct/formatters). - **Formatters** (new `CalendarTitleFormatter.swift`): pure `CalendarTitleFormatter.format(base:tags:)` (first 3 tags + `, +N` overflow) and `CalendarNotesFormatter.format(scheduleName:tags:)` (full uncapped tag list). Parity-locked by a shared JSON fixture (`docs/specs/fixtures/calendar-renderer-fixtures.json`) that the Slice 4A Node ICS renderer will also load. - **Event construction**: extracted to a pure, testable `makeEvent`; sets `LOCATION`, busy/tentative availability by status, and branches all-day (null start/end → single all-day day, no alarm) vs timed (30-min alarm, overnight roll preserved). All-day uses a same-day endDate — EventKit treats all-day end as inclusive, so the spec's ICS-flavored next-day end would render a bogus 2-day block. - **Store**: `CalendarSyncStore` fetches the dept facility once per session (cached by deptSlug, invalidated on actor change / logout, best-effort so a facility hiccup never blocks shift sync), maps `formattedAddress` → location and `tags` → tags. - **ShiftdKit**: `facility(in:)` decodes `GET /api/d/{deptSlug}/facility` into a flat `DepartmentFacility` DTO (the spec op is unnamed and `FacilityWithAddress` is an allOf schema the generator nests). ## Test plan - [x] App target: 145/145 ShiftdTests pass (new CalendarFormatterTests, EventKitCalendarSyncServiceTests, CalendarSyncMappingTests) - [x] ShiftdKit: 50/50 pass - [x] Parity fixture asserted exactly in Swift; same file feeds the Slice 4A Node renderer - Note: iOS is not in Forgejo CI, so these were validated locally via XcodeBuildMCP (iPhone 17 sim). PR CI exercises only backend/frontend, which are untouched. ## Incidental Second commit repairs two pre-existing iOS test literals that failed to compile once ShiftdKit regenerated: `StaffUpcomingAssignment` now requires `tags[]` (Slice 2) and `Schedule` requires `coverageBasis`. iOS test drift, unrelated to this feature, surfaced because there is no iOS CI.
Slice 3 of the calendar sync payload spec (Section D). Extends
CalendarSyncPayload with tags, location, and assignmentStatus; adds pure
CalendarTitleFormatter/CalendarNotesFormatter (Section B rules) parity-locked
by docs/specs/fixtures/calendar-renderer-fixtures.json (shared with the Slice
4A Node renderer). Event construction now sets LOCATION, busy/tentative
availability by status, and branches all-day (null start/end, no alarm) vs
timed. CalendarSyncStore fetches the dept facility once per session (cached by
deptSlug, invalidated on actor change/logout, best-effort), maps formattedAddress
into location, and passes tags through. Adds ShiftdAPIClient.facility(in:).
fix(ios): repair generated-type test literals after ShiftdKit regen
All checks were successful
Code Scanning / Gitleaks secret scan (pull_request) Successful in 7s
Code Scanning / Semgrep OSS source scan (pull_request) Successful in 33s
Security, Type Check & Runtime / Dependency Audit (pull_request) Successful in 9m38s
Security, Type Check & Runtime / Migration Guardrails (pull_request) Successful in 9m34s
Security, Type Check & Runtime / Type Check (pull_request) Successful in 10m13s
Security, Type Check & Runtime / Backend Runtime Smoke (pull_request) Successful in 10m12s
Release Artifacts / Validate release candidate (pull_request) Successful in 10m52s
Release Artifacts / Build and push Docker release images (pull_request) Has been skipped
E2E Tests / e2e (pull_request) Successful in 14m25s
e66d6e1261
StaffUpcomingAssignment now requires tags[] (Slice 2) and Schedule requires
coverageBasis; pre-existing test literals omitted both and failed to compile
once ShiftdKit regenerated. Adds the missing required fields.
owlburtoe deleted branch feat/calendar-sync-slice-3-ios-payload 2026-05-28 17:23:27 -04:00
Sign in to join this conversation.
No description provided.