ghostguild-org/docs/LAUNCH_READINESS.md

6.4 KiB

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.