# Launch Readiness **Status as of 2026-04-18.** Target launch: before 2026-05-01. Single source of truth for work that must happen before cutover. P0 blocks launch. P1 is strongly preferred but survivable. Completed items have been archived — see `~/.claude/projects/-Users-jennie-Sites-ghostguild-org/memory/project_launch_readiness_archive.md`. Post-launch backlog lives in `docs/TODO.md`. --- ## Current state - Vitest: 577/577 passing. - Helcim plan consolidation migration ran against prod 2026-04-18 (Monthly plan id `50302`, Annual plan id `50303`). All six paid-flow manual tests pass via tunnel. - Remaining launch blockers: see lists below. --- ## P0 — Must fix before launch None outstanding. Privacy/Terms pages shipped, duplicate-customer bug fixed, pre-deploy migrations run. --- ## P1 — Strongly preferred before launch ### UX polish on the paid `/join` flow (noted 2026-04-18 during Test 1) - Code entry points: `app/pages/join.vue` (form + `handleSubmit:478`, `processPayment:532`), `app/composables/useHelcimPay.js` (modal trigger at 137). - Flow works end-to-end, but the single most important conversion point has three rough spots: - **Join form sits too low on the page.** First-time visitors have to scroll to find it. Raise it above the fold, or lead the page with the form and move supporting copy below. - **"Open Helcim modal" button is placed oddly.** After the first form submit, the next action (trigger the HelcimPay.js iframe) is easy to miss. Needs clearer visual hierarchy, ideally auto-opened or prominently CTA'd. - **Return-to-join-page flash after payment success.** After HelcimPay.js reports SUCCESS, the user briefly sees the join form again before the redirect to `/member/dashboard` fires. Reads as an error. - **Ideal:** wrap the whole flow (form submit → HelcimPay.js → success → dashboard) in a modal or at least an overlay with a clear step indicator, so the user never returns to the join page between states. - Not blocking launch — flow works — but should land before inviting the full pre-registrant list, since this is where first impressions happen. --- ## Deploy checklist Pre-deploy migrations have all been run. What's left: - [ ] Merge `feature/helcim-plan-consolidation` into `main`. - [ ] Set `NUXT_HELCIM_MONTHLY_PLAN_ID=50302` in Netlify production env. - [ ] Set `NUXT_HELCIM_ANNUAL_PLAN_ID=50303` in Netlify production env. **Env vars required in production (reference):** - `MONGODB_URI` - `JWT_SECRET` (or `NUXT_JWT_SECRET` — the `NUXT_` variant wins) - `RESEND_API_KEY` - `HELCIM_API_TOKEN` - `NUXT_HELCIM_MONTHLY_PLAN_ID` - `NUXT_HELCIM_ANNUAL_PLAN_ID` - `SLACK_BOT_TOKEN` - `BASE_URL` - `OIDC_COOKIE_SECRET` - `NUXT_PUBLIC_HELCIM_PORTAL_URL` --- ## Manual browser tests still needed Cannot be verified by Vitest. All require a real browser + real Helcim test card + real email. - [ ] **Event ticket purchase with payment** (HelcimPay.js iframe; use cloudflared tunnel or ngrok HTTPS). - [ ] **Pre-registrant invite → accept flow** with paid tier (exercises Helcim customer creation during acceptance). - [ ] **Magic-link login** including 15-min expiry and jti burn on reuse. - [ ] **Guest event signup** — four branches: new email + consent, new email without consent, existing guest, existing active member. Confirms cookie only sets for new/guest, and confirmation email appends `/login` link for real members. - [ ] **Mobile responsive layout** — sidebar hides ≤1024px, nav works on phone. - [ ] **`--text-dim` / `--text-faint` WCAG AA contrast check.** --- ## Bylaws decoupling — follow-ups (added 2026-04-18) Context: bylaws are being amended to remove automatic termination for nonpayment. Membership status will be fully decoupled from payment status; failed payments trigger committee outreach, not status change. Copy + UI access gates already aligned in `useMemberStatus.js` and `account.vue` (2026-04-18). Server-side status gating shipped as B2 (see archive). The behavioral changes below remain. Not blocking launch — the amendment hasn't passed yet, and the user-visible copy/UI is already consistent. Pick up once the amendment is ratified. ### B1. `cancel-subscription` flips status to `pending_payment` - `server/api/members/cancel-subscription.post.js:31,48` - When a member cancels their paid subscription, status is set to `pending_payment` and tier to `'0'`. Under the new model, cancelling a payment plan moves the member to the $0 tier — status should stay `active`. - **Fix:** change `status: 'pending_payment'` → `status: 'active'` in both the `findByIdAndUpdate` payload (line 31) and the response (line 48). Comment at line 26 also needs updating ("(not cancelled) so member can re-subscribe" → reflect new framing). - Add coverage in `tests/server/api/cancel-subscription.test.js` if it doesn't already exist. ### B3. Vestigial `pending_payment` status - Once payment is fully decoupled, `pending_payment` no longer gates anything and is functionally equivalent to `active`. Consider removing it from the enum (`server/models/member.js:38`, `server/utils/schemas.js:299`) and treating new signups as `active` from the moment of account creation. - Touches: signup flow (`helcim/customer.post.js:34`, `invite/accept.post.js:48`), admin filter UI (`app/pages/admin/members/index.vue:45,382,499,1145`, `[id].vue:69,286`), admin alerts (`server/utils/adminAlerts.js:22,100-116`, `server/models/adminAlertDismissal.js:6`), and a data migration to flip existing `pending_payment` rows to `active`. - Larger refactor — break out into its own ticket once B1 lands. ### B4. Admin "Pending Payment" filter label (cosmetic) - `app/pages/admin/members/index.vue:45,499`, `[id].vue:69` show `pending_payment` as "Pending Payment". If B3 removes the status entirely, this disappears too. If we keep `pending_payment` for now, rename in admin UI to "Payment setup incomplete" so admins also stop conflating it with membership state. --- ## Post-launch backlog See `docs/TODO.md` for: - Button minimum target size (WCAG AAA 2.5.5). - `/oidc/interaction/[uid]` routing quirk. - Admin layout migration from `guild-*` tokens to zine spec. - Admin dashboard quick-action button contrast. - Members table NAME column clipping. - OWASP ASVS L1 Phase 4 (file-upload validation pipeline, granular RBAC, credential encryption). - `tickets/available.get.js:115` `memberSavings` block reports `$0 saved` for inactive members — cosmetic; suppress comparison block when `!hasMemberAccess(member)` if it ever surfaces in UI.