--- phase: 12-initial-setup-wizard plan: 07 type: execute wave: 1 depends_on: [] files_modified: - apps/pwa/src/App.tsx - apps/pwa/src/App.test.tsx - apps/pwa/src/components/SetupBanner.tsx autonomous: true gap_closure: true requirements: [SETUP-01, SETUP-04] must_haves: truths: - "After setup is complete, manually visiting /setup shows the 'already complete' surface (or redirects away) — not the wizard" - "After completing the wizard (incl. Fastmail credential) and landing in the app, the /calendar 'Set up your calendar' banner does NOT show for the operator" - "The setup gate respects the loading state to avoid a flash of the wizard" artifacts: - path: "apps/pwa/src/App.tsx" provides: "/setup route gated on setupComplete (alreadyLocked or redirect); ['me'] freshness reconciled with wizard completion" contains: "alreadyLocked" key_links: - from: "App.tsx /setup route" to: "setupQuery.data.setupComplete" via: "alreadyLocked={setupComplete === true} or Navigate to /calendar" pattern: "alreadyLocked|setupComplete" - from: "SetupBanner needsProviderSetup" to: "['me'] query freshness" via: "['me'] refetched after wizard completion so the banner reflects the claimed credential" pattern: "needsProviderSetup|invalidateQueries" --- Close UAT gaps 5 and 6 — both on the PWA app-shell gate (`App.tsx`), with the banner component (`SetupBanner.tsx`). Gap 5 (major): `App.tsx` renders `} />` with no `alreadyLocked` prop and no `setupComplete` check. The `*` gate only redirects OTHER routes TO /setup when incomplete; there is no reverse guard. When `setupComplete === true`, manually visiting /setup still mounts the full wizard. The backend already 423s mutations, so this is purely a frontend gating gap. Fix: gate the /setup route on `setupComplete` — pass `alreadyLocked={setupComplete === true}` (SetupPage already supports this prop → renders Surface 8 "Setup already complete") or `Navigate` to /calendar; respect `setupLoading` to avoid a flash. Gap 6 (major, root_cause PRELIMINARY): after finishing the wizard and landing on /calendar, the "Set up your calendar / Set up now" banner still shows. Task 1 is an investigation step to confirm the mechanism before prescribing the exact fix; Task 2 implements the confirmed fix. Purpose: A completed instance must not re-expose the wizard, and must not nag the operator to set up a calendar they already configured in the wizard. Output: /setup route gated post-completion; ['me'] reconciled so the banner does not show after wizard completion. @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/12-initial-setup-wizard/12-UAT.md @.planning/phases/12-initial-setup-wizard/12-03-SUMMARY.md @.planning/phases/12-initial-setup-wizard/12-04-SUMMARY.md # Files to edit + investigate @apps/pwa/src/App.tsx @apps/pwa/src/App.test.tsx @apps/pwa/src/components/SetupBanner.tsx # Reference (do not edit unless Task 1 investigation proves a backend linking gap) @apps/api/src/routes/me.ts @apps/api/src/routes/setup.ts @apps/api/src/auth/user.ts Task 1: Gate the /setup route on setupComplete (gap 5) apps/pwa/src/App.tsx, apps/pwa/src/App.test.tsx In apps/pwa/src/App.tsx, the `} />` (line ~139) currently mounts the wizard unconditionally. Add a reverse guard using the already-present `setupComplete` / `setupLoading` values (derived at lines ~132-133 from `setupQuery`): - While `setupLoading` is true → render the existing no-flash placeholder (`