Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.2 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12-initial-setup-wizard | 07 | ui |
|
|
|
|
|
|
|
|
|
11min | 2026-06-16 |
Phase 12 Plan 07: UAT Gap-Closure (gaps 5 & 6) Summary
The /setup wizard no longer re-mounts after completion (shows Surface 8 'Setup already complete'), and the '/calendar' setup banner no longer nags the operator after they finish the wizard — fixed by reverse-gating the route and making the ['me'] query fresh on shell entry.
Performance
- Duration: ~11 min
- Started: 2026-06-16T21:19Z
- Completed: 2026-06-16T21:24Z
- Tasks: 2
- Files modified: 4 (3 modified, 1 created)
Accomplishments
- Gap 5 closed:
/setupis now reverse-gated onsetupComplete. After completion, manually visiting/setuprendersSetupPage alreadyLocked→ Surface 8 "Setup already complete" (the backend already 423s setup mutations; this is the matching frontend gate). Loading state renders a no-flash placeholder;setupComplete===falsestill mounts the active wizard. - Gap 6 closed: the "Set up your calendar" banner no longer persists after the operator completes the wizard. Root cause confirmed as a
['me']cache-staleness gap (mechanism ii), NOT a backend linking gap. Set['me']staleTimeto 0 in bothApp.tsx(boot) andSetupBanner.tsxso a pre-claim stale entry is refetched on entry to the authenticated shell —needsProviderSetupthen reflects the just-claimed credential and the banner hides. - Regression coverage added: new
SetupBanner.test.tsx(3 tests) + 2 new App reverse-gate tests. Full PWA suite green at 258 tests; typecheck clean.
Gap 6 — Root Cause Investigation (Task 2 Step A)
The UAT root_cause was flagged PRELIMINARY. Reading apps/api/src/auth/user.ts upsertUser confirmed the first-login claim branch (lines ~118-130) updates the unclaimed row via where(eq(users.id, unclaimed.id)) — it preserves the same users.id. Because member_credentials is keyed on userId, the wizard-stored CalDAV credential stays linked to the claimed operator row, so the DB correctly returns needsProviderSetup=false after first OIDC login.
Conclusion: mechanism (i) (a backend claiming/linking gap) is NOT the cause — consistent with the 12-03 summary and threat-register disposition T-12-10 = accept. The cause is mechanism (ii): ['me'] had staleTime: 5 * 60 * 1000, so a cache entry populated before the claim (e.g. a pre-auth visit) served needsProviderSetup=true for up to 5 minutes after the operator authenticated post-wizard. No backend change was made; the fix is purely client-side cache freshness.
Task Commits
- Task 1: Gate the /setup route on setupComplete (gap 5) —
fdcb4dc(feat)- (also carried the App.tsx
['me']staleTime → 0 edit, staged together; the SetupBanner-side change + its test landed in Task 2)
- (also carried the App.tsx
- Task 2: Diagnose + fix the persistent calendar banner (gap 6) —
2b3569f(fix)
Files Created/Modified
apps/pwa/src/App.tsx— reverse-gated/setuproute element (loading placeholder /alreadyLockedSurface 8 / active wizard); boot['me']staleTime→ 0 with mechanism note.apps/pwa/src/components/SetupBanner.tsx—['me']staleTime5min → 0 (gap-6 freshness); no dismiss button added; success-only dismissal contract restated in comments.apps/pwa/src/App.test.tsx— SetupPage mock now respectsalreadyLocked; 2 new reverse-gate tests (already-complete surface + active wizard on/setup).apps/pwa/src/components/SetupBanner.test.tsx— NEW: banner absent whenneedsProviderSetup=false, present (no dismiss button) when true, and stale-cache refetch hides the banner on mount (staleTime 0).
Decisions Made
- D-12-07-GAP6-MECH — gap 6 is mechanism (ii)
['me']staleness, not a linking gap (evidence: id-preserving claim inupsertUser). - D-12-07-STALE0 —
['me']staleTimeset to 0 in App.tsx + SetupBanner.tsx; success-only dismissal contract preserved. - D-12-07-LOCKED-PROP —
/setupreverse-gate uses thealreadyLockedprop (Surface 8), notNavigate, per the UAT expectation that manual/setupnavigation shows the "already complete" surface.
Deviations from Plan
None — plan executed as written. The plan's expected gap-6 finding (mechanism ii) was confirmed by the Task 2 Step A investigation; the prescribed staleTime fix was applied. No architectural changes; no backend changes required.
Issues Encountered
- Pre-existing PWA lint errors (out of scope).
pnpm lintinapps/pwareports 22 errors insrc/api/setupClient.contract.test.ts(no-unsafe-*) andsrc/routes/SetupPage.test.tsx:152(no-unused-vars). Neither file was touched by this plan; both were last modified in earlier Phase-12 commits. The four files this plan touched lint clean (exit 0). Logged to.planning/phases/12-initial-setup-wizard/deferred-items.mdand left untouched per the executor SCOPE BOUNDARY rule. Recommend a follow-up lint-cleanup quick task.
Verification
apps/pwafull suite: 258 tests passed (22 files);App.test.tsx8 passed;SetupBanner.test.tsx3 passed.pnpm typecheck(apps/pwa): clean.grep -nE "alreadyLocked" apps/pwa/src/App.tsx→/setuproute gated on setupComplete (Surface 8).grepfor new dismiss/X button inSetupBanner.tsx→ none added (only contract comments).- Touched-files lint:
eslintover the 4 files → exit 0.
Known Stubs
None.
User Setup Required
None — no external service configuration required.
Next Phase Readiness
- Both UAT major gaps (5 and 6) are closed in code with regression tests. Ready for re-UAT of the post-completion
/setupsurface and the post-wizard calendar banner. - Remaining UAT gaps (if any from 12-05/12-06) are tracked in their own gap-closure plans; this plan scoped only gaps 5 & 6.
- Pre-existing PWA lint debt deferred (see deferred-items.md) — does not block this plan's UI behavior.
Self-Check: PASSED
- All 5 created/modified files present on disk.
- All 3 commits (
fdcb4dc,2b3569f,96c4913) present in git history.
Phase: 12-initial-setup-wizard Completed: 2026-06-16