From afdc8d124d2240640d0f698c82e68c237290f496 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 10 Jun 2026 09:33:36 -0400 Subject: [PATCH] docs(06): capture phase context --- .planning/phases/06-ux-polish/06-CONTEXT.md | 133 ++++++++++++++++++ .../phases/06-ux-polish/06-DISCUSSION-LOG.md | 85 +++++++++++ 2 files changed, 218 insertions(+) create mode 100644 .planning/phases/06-ux-polish/06-CONTEXT.md create mode 100644 .planning/phases/06-ux-polish/06-DISCUSSION-LOG.md diff --git a/.planning/phases/06-ux-polish/06-CONTEXT.md b/.planning/phases/06-ux-polish/06-CONTEXT.md new file mode 100644 index 0000000..26516f4 --- /dev/null +++ b/.planning/phases/06-ux-polish/06-CONTEXT.md @@ -0,0 +1,133 @@ +# Phase 6: UX Polish - Context + +**Gathered:** 2026-06-10 +**Status:** Ready for planning + + +## Phase Boundary + +Smooth the rough edges surfaced during Phase 3 Gate 2 live use so the app feels slick for the non-technical Apple member (hard UX constraint). **Polish only — no new capabilities.** + +Scope is the six backlog polish items promoted into this phase: + +| Item | What it fixes | Success criterion | +|---|---|---| +| 999.6 | All-day events visually distinct from timed events | #1 | +| 999.7 | Event end auto-tracks start (any event); all-day-edit off-by-one | #2 | +| 999.8 | Recurrence can be bounded (repeat-until / count); FREQ-persistence check | #2 | +| 999.9 | A recurring series can be edited whole | #3 | +| 999.3 | Session expiry redirects cleanly to sign-in instead of hanging | #4 | +| 999.2 | No calendar/"sign-in required" flash before Authelia on cold load | #5 | + +**Explicitly OUT of this phase** (promoted to milestone 1.1, see Deferred): 999.4 (reminder/VALARM selector) and 999.5 (first-login provider setup). The roadmap flagged both as "more feature than polish"; the user confirmed they belong in the next milestone, not here. + + + + +## Implementation Decisions + +### Scope (the backlog pull) +- **D-01:** Phase 6 = the six polish items only (999.2/3/6/7/8/9). Do NOT pull in 999.4 or 999.5. +- **D-02:** 999.4 (reminders/VALARM) and 999.5 (provider setup) move to a new **milestone 1.1**, started via the proper GSD milestone flow (`/gsd-new-milestone`). 999.1 (provider-abstraction) also remains backlog/1.1 candidate. + +### Event-form end-tracking (999.7) — applies to ALL events, not just recurring +- **D-03:** This is a general bug: when the user moves the **start** date/time forward, the **end** selector does not follow — for one-time, single-day, and timed events alike. Today an end left behind the start produces a stale/absurd span. +- **D-04:** On any start change, **preserve the current duration** — timed: keep the start→end delta; all-day: keep the day-span — so the end moves with the start automatically. **Floor requirement** (the user's explicit minimum): the end must never strand behind the start day; at worst it snaps to the same day as the new start, and the user extends forward from there for genuine multi-day events. +- **D-05:** The all-day-edit off-by-one part of 999.7 is **already fixed** (commit `f645644`, CR-03: exclusive `DTEND` → inclusive on edit pre-fill, `EventForm.tsx:199`). Verify it still holds; don't re-implement. Remaining 999.7 work = the start→end auto-advance. + +### Recurrence bounding (999.8) +- **D-06:** Add a bound to the recurrence control so users stop misusing the event end-date as "repeat until." Primary control: **"repeat until "** (RRULE `UNTIL`) — matches the misuse pattern most directly. **"for N occurrences"** (RRULE `COUNT`) is acceptable to ship alongside or as the alternative; exact control set is a planning/UI-phase call. Each occurrence's duration must stay tied to start→end, NOT the recurrence span. +- **D-07:** Verify/fix the FREQ-persistence bug noted in 999.8 (a daily selection reportedly persisted as weekly). Confirm the dropdown writes the selected `FREQ`. + +### Recurring-series edit (999.9) +- **D-08:** Behavior is **whole-series edit**: editing a recurring occurrence edits the master VEVENT (title / time / RRULE) for all occurrences. Per-occurrence (`RECURRENCE-ID`) and "this and following" edits stay **deferred to v1.x** (carried from Phase 3 D-03 / Deferred Items — not reopened here). +- **D-09:** The confirmation/prompt UX for "this changes the whole series" is **delegated to `/gsd-ui-phase`** — lock the behavior, defer the wording/placement. + +### Visual feel & sync feedback (user-added, 2026-06-10) +- **D-12:** Beyond fixing individual rough edges, the user wants the app to **feel more distinct and modern** as a whole. The visual refresh is delegated to `/gsd-ui-phase`, which **should invoke the `frontend-design` skill** to drive a polished, non-generic aesthetic across the touched surfaces. Scope note: this widens "polish the rough edges" toward a light visual refresh — keep it to elevating the existing surfaces (calendar, event form, lists, sync feedback, auth splash), not a ground-up redesign; a full redesign would be its own phase. The user owns this call. +- **D-13:** **Sync indicators must actually spin.** Today the spinner styles reference `animation: spin …` but the `@keyframes spin` is defined locally inside `PushPermissionPrompt.tsx` (`:359`) rather than globally — so `SyncStateToast` (`:158`) and `LiveSyncIndicator` likely don't animate when that component isn't mounted. Hoist `@keyframes spin` to a global stylesheet (or per-component) so every sync indicator animates. Planner/researcher to confirm the exact failure. + +### Auth-flow polish (999.2 + 999.3) +- **D-10:** 999.2 — gate the app render on auth state so no calendar shell / skeleton / "Sign-in required" alert paints before Authelia. While unauthenticated and redirecting, show a single neutral full-screen "Signing you in…" splash. Reserve the "Sign-in required" dead-end only for the one-shot-guard fall-through. Root cause + proposed fix are in ROADMAP 999.2 (CalendarShell optimistic render). +- **D-11:** 999.3 — detect session expiry (401 / opaqueredirect) from ANY query or mutation (not just the initial `/api/me`) and drive a clear re-auth via top-level navigation to `/api/login`, ideally behind a brief "Your session expired — signing you back in…" interstitial. Centralize detection in `apps/pwa/src/api/client.ts` (typed `SessionExpiredError`, consistent `redirect:'manual'`) with a single TanStack Query/Mutation error handler re-arming `maybeRedirectToLogin()`. In-flight-write preservation/replay is a *nice-to-have*, not a hard requirement — acceptable to surface a clear re-auth rather than silently losing a write; planner to decide effort. + +### Claude's Discretion / delegated to UI-phase +- **All-day visual treatment (999.6):** behavior locked (must be distinguishable at a glance); the concrete treatment — full-width pill/bar vs background band vs distinct shape — is **delegated to `/gsd-ui-phase`** against the design system. +- **Series-edit prompt UX (999.9):** delegated to `/gsd-ui-phase` (see D-09). +- **Splash/interstitial copy (999.2/999.3):** exact wording open; "Signing you in…" / "Your session expired — signing you back in…" are starting points. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Phase scope & backlog source +- `.planning/ROADMAP.md` §"Phase 6: UX Polish" — goal + 5 success criteria (the locked WHAT) +- `.planning/ROADMAP.md` §"Backlog" 999.2/999.3/999.6/999.7/999.8/999.9 — each item carries a diagnosed root cause (from Phase 3 Gate 2, 2026-06-07) and a proposed fix. These are the authoritative problem statements. +- `.planning/ROADMAP.md` §"Backlog" 999.1/999.4/999.5 — deferred to milestone 1.1 (do NOT implement here) +- `.planning/PROJECT.md` — hard UX constraint (wife adoption), recurring "create + display only" v1 stance + +### Event form / write-back (999.6/7/8/9) +- `apps/pwa/src/components/EventForm.tsx` — start/end/all-day/recurrence state; `:199` exclusive→inclusive all-day fix (D-05); `:303` current all-day toggle clamp; `RecurrencePreset` usage +- `apps/pwa/src/api/client.ts` §`RecurrencePreset` (`:130`) — frequency-only today; UNTIL/COUNT to be added (D-06) +- `apps/api/src/broker/vevent.ts` — WR-04 inclusive→exclusive `DTEND` on write-back (symmetry partner for D-05) +- `apps/api/src/broker/expand.ts` — recurrence expansion (per-occurrence duration must follow start→end, D-06) +- `.planning/phases/03-event-write-back-pwa-install/03-CONTEXT.md` — D-03 recurrence create+display-only, WR-01..04 write-back contract, per-occurrence/"this-and-following" deferral (carried into D-08) + +### Visual feel & sync indicators (D-12/D-13) +- `apps/pwa/src/components/SyncStateToast.tsx` `:158` — uses `animation: 'spin …'` (D-13) +- `apps/pwa/src/components/LiveSyncIndicator.tsx` — sync-state indicator (D-13) +- `apps/pwa/src/components/PushPermissionPrompt.tsx` `:359` — where `@keyframes spin` is currently (locally) defined; needs hoisting to global (D-13) +- `frontend-design` skill — to be invoked by `/gsd-ui-phase` for the modern/distinct visual refresh (D-12) + +### Auth flow (999.2/999.3) +- `apps/pwa/src/components/CalendarShell.tsx` — optimistic render that causes the flash (999.2 root cause, D-10) +- `apps/pwa/src/api/client.ts` — `fetchMe` `redirect:'manual'`, opaqueredirect/401 detection (`:38–44`); centralization point for D-11 +- `apps/pwa/src/lib/loginRedirect.ts` — `maybeRedirectToLogin()` / one-shot `familysync.loginRedirectAttempted` guard to be re-armed (D-11) + + + + +## Existing Code Insights + +### Reusable Assets +- `EventForm.tsx`: already has start/end/all-day/recurrence state and the inclusive/exclusive all-day conversion helper (`exclusiveEndToInclusiveDate`). Extend it for end-tracking (D-04) and the recurrence bound control (D-06) rather than rebuilding. +- `client.ts`: already detects `opaqueredirect`/401 for `fetchMe`; the typed error + single error-handler pattern (D-11) generalizes the existing one-shot detection. +- `loginRedirect.ts` `maybeRedirectToLogin()`: reuse for the splash/redirect on both 999.2 and 999.3. + +### Established Patterns +- Write-back keeps an inclusive(form)↔exclusive(`DTEND`) convention split across PWA (`EventForm.tsx`) and API (`vevent.ts` WR-04). Any end-tracking change (D-04) must preserve this; all-day day-span is inclusive in the form. +- Recurrence is "create + display only" in v1 (Phase 3 D-03). 999.9 adds whole-series edit on top; do NOT add per-occurrence edit. +- Auth redirect is intentionally `redirect:'manual'` + document navigation (XHR can't follow cross-origin IdP redirects); keep that mechanism for D-10/D-11. + +### Integration Points +- 999.8 spans PWA (`EventForm` UNTIL/COUNT control) → API write (`vevent.ts` RRULE serialization) → expansion (`expand.ts`). Per-occurrence duration must derive from start→end, independent of the recurrence span. +- 999.2/999.3 both center on `client.ts` + `CalendarShell.tsx` + `loginRedirect.ts` — a single auth-gating refactor likely serves both; plan them together. + + + + +## Specific Ideas + +- The motivating bug for 999.7/999.8: a "recurring event" created start 2026-06-11, end 2026-08-13 (~2-month span) with a weekly RRULE rendered as overlapping bars across the calendar (looked duplicated). The end didn't track the start AND the series was unbounded — two distinct fixes (D-04 + D-06). +- User's framing of 999.7 (verbatim intent): end-not-tracking-start is "not just for recurrence events — it's for any event… at the very least the end selector should move to the same day as the start time and the user can move it further forward for multi-day events from there." +- Splash copy starting points: "Signing you in…" (999.2 cold load), "Your session expired — signing you back in…" (999.3 mid-use). + + + + +## Deferred Ideas + +- **999.4 — Event reminder/VALARM options** → milestone 1.1. The reminder-input half of the now-shipped Phase 5 push system; a real write-back feature, not polish. +- **999.5 — First-login provider setup (Fastmail app password)** → milestone 1.1. Multi-member onboarding; security + UX surface, not polish. +- **999.1 — Calendar provider abstraction** → backlog / milestone 1.1 candidate. +- **Per-occurrence (`RECURRENCE-ID`) and "this and following" recurring edits** → v1.x (carried from Phase 3; 999.9 delivers whole-series only). + + + +--- + +*Phase: 6-ux-polish* +*Context gathered: 2026-06-10* diff --git a/.planning/phases/06-ux-polish/06-DISCUSSION-LOG.md b/.planning/phases/06-ux-polish/06-DISCUSSION-LOG.md new file mode 100644 index 0000000..71fd992 --- /dev/null +++ b/.planning/phases/06-ux-polish/06-DISCUSSION-LOG.md @@ -0,0 +1,85 @@ +# Phase 6: UX Polish - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-06-10 +**Phase:** 06-ux-polish +**Areas discussed:** Backlog scope (999.4/999.5), All-day visual, Event-form behavior (999.7/999.8), Recurring-series edit (999.9) + +--- + +## Backlog scope — pull in 999.4 / 999.5? + +| Option | Description | Selected | +|--------|-------------|----------| +| Neither — keep 6-item polish scope | Phase 6 stays 999.2/3/6/7/8/9; 999.4/999.5 stay backlog | ✓ | +| Pull in 999.4 (reminders) | Add VALARM selector | | +| Pull in 999.5 (provider setup) | Add first-login app-password onboarding | | + +**User's choice:** Keep Phase 6 as polish; move 999.4 and 999.5 to a new milestone 1.1 and start it via the proper GSD milestone flow. +**Notes:** User wants the deferred items parked under milestone 1.1 explicitly, kicked off with `/gsd-new-milestone`. Sequencing (start 1.1 before vs after v1.0 ships) raised as a follow-up. + +--- + +## All-day visual treatment (999.6) + +| Option | Description | Selected | +|--------|-------------|----------| +| Full-width pill/bar | Google/Apple-style all-day row bar | | +| Background band / tint | Subtle full-day band | | +| Distinct shape/border | Border/icon marker only | | +| Let UI-phase decide | Capture intent, defer treatment to /gsd-ui-phase | ✓ | + +**User's choice:** Let UI-phase decide. +**Notes:** Intent locked (must be distinguishable at a glance); concrete treatment delegated to /gsd-ui-phase. + +--- + +## Event-form behavior (999.7 end-tracking + 999.8 recurrence bound) + +| Option | Description | Selected | +|--------|-------------|----------| +| Preserve duration on start change | End auto-advances to keep duration | ✓ | +| Bound by "repeat until " | RRULE UNTIL | ✓ (primary) | +| Bound by "for N occurrences" | RRULE COUNT | (acceptable alongside/alt) | + +**User's choice:** Preserve duration; recurrence bound in scope (UNTIL primary, COUNT acceptable). +**Notes:** IMPORTANT correction — end-not-tracking-start is NOT recurrence-specific; it affects every event (one-time, single-day, timed). Floor requirement: end must at least snap to the same day as the new start; user extends forward for multi-day. Off-by-one portion of 999.7 already fixed (commit f645644). + +--- + +## Recurring-series edit (999.9) + +| Option | Description | Selected | +|--------|-------------|----------| +| Whole series, with confirm | Edit master VEVENT after a confirmation | | +| Whole series, no extra confirm | Edit series with helper note only | | +| Let UI-phase decide the prompt UX | Lock whole-series behavior, defer prompt UX | ✓ | + +**User's choice:** Let UI-phase decide the prompt UX. +**Notes:** Behavior locked (whole-series edit of master VEVENT). Per-occurrence / "this and following" stay deferred to v1.x. Confirmation/prompt wording delegated to /gsd-ui-phase. + +--- + +## Visual feel & sync feedback (user-added, post-questions) + +**User's input (verbatim intent):** "For the UI polish, I want the whole thing to feel more distinct and modern. You can invoke the claude frontend-design skill for this to make it feel better. I want the sync indicators to actually spin too." + +- Modern/distinct visual refresh → /gsd-ui-phase to invoke the `frontend-design` skill (D-12). Flagged as widening "polish" toward a light refresh; kept to elevating existing surfaces, not a ground-up redesign. +- Sync indicators must actually spin (D-13) — likely root cause: `@keyframes spin` defined locally in `PushPermissionPrompt.tsx`, not global, so `SyncStateToast` / `LiveSyncIndicator` don't animate. + +## Claude's Discretion + +- All-day visual treatment (999.6) — delegated to /gsd-ui-phase +- Series-edit confirmation/prompt UX (999.9) — delegated to /gsd-ui-phase +- Splash/interstitial copy (999.2/999.3) — open; starting points provided +- Recurrence bound exact control set (UNTIL only vs +COUNT) — planning/UI call +- In-flight-write preservation on session expiry (999.3) — nice-to-have, planner decides effort + +## Deferred Ideas + +- 999.4 (reminders/VALARM) → milestone 1.1 +- 999.5 (first-login provider setup) → milestone 1.1 +- 999.1 (provider abstraction) → backlog / 1.1 candidate +- Per-occurrence & "this and following" recurring edits → v1.x