ghostguild-org/docs/LAUNCH_READINESS.md

101 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
### In-app billing management; demote Helcim portal to escape hatch
- Helcim's hosted portal requires a separate password the member never set during `/join`. First-touch flow is "click link → see Helcim login → click Forgot password → wait for email → set password → sign in." Reads as broken.
- **Ship in-app equivalents for the 80% case:**
- Past invoices / receipts list on `/member/account` — new server route pulls from Helcim invoice API by `helcimCustomerId`; render a simple list with date, amount, download/view link.
- Change card — reuse `useHelcimPay` composable to get a new `cardToken`, then new server route updates the customer's default payment method and the active subscription's payment method.
- **Keep** the existing "Manage billing in Helcim →" link but relabel to something like "Advanced billing in Helcim →" so it reads as an escape hatch for disputes/edge cases, not the primary surface.
- Rough scope: 12 days. Two new `server/api/helcim/*` routes + two new sections on `/member/account`.
---
## 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.