Release Notes — Jul 1, 2026 — dev → prod promotion (renewable admin sessions + cross-portal cookie fixes + membership periods)
membervu/membervu-saas!264 — MERGED), everything accumulated on dev since the Jun 29 release (!249). 51 commits · 124 files · ~16 issues. Verify on staging (https://stg-rcme.membervu.com; admin portal https://stg-admin.membervu.com) and on the prod release build now that it has landed. The headline is a session-management wave: super-admin sessions are now renewable (they used to silently log you out), and two cross-portal cookie collisions are fixed. Plus a billing batch (manual-dues activation, new membership periods), a couple of events fixes, and security hardening.
🧭 New to MemberVu? Start here
MemberVu is a multi-tenant membership platform with two web apps: the org portal (stg-rcme.membervu.com — admins + members of a club) and the super-admin portal (stg-admin.membervu.com, separate login — Zeniark platform operators).
The change most likely to affect your testing is session behavior: the super-admin portal now stays logged in across a normal work session instead of dropping you (#531), and the org + admin portals no longer step on each other's cookies when both are open in the same browser (#539/#540). If you test both portals side-by-side, that's exactly the scenario these fixes target.
Recommended path: (1) read the env + login box; (2) test renewable admin sessions (§🔐 #531) and the two-portal cookie scenario (§🔐 #539/#540); (3) test manual-dues activation (§💳 #527) and new membership periods (§💳 #504); (4) work the rest top-to-bottom.
⚙️ Before you start — environment & logins
| Surface | URL | Login to use |
|---|---|---|
| Org portal (tenant) | stg-rcme.membervu.com | Admin admin@rcme.membervu.com / Admin123!; Treasurer treasurer@demo.membervu.com / Treasurer123! |
| Member view | stg-rcme.membervu.com | Member testmember@rcme.membervu.com / Member123!; Expired expired@demo.membervu.com / Expired123! |
| Super-admin portal | stg-admin.membervu.com | Super Admin superadmin@zeniark.com / SuperAdmin123! |
| Email inbox (MailHog) | stg-webmail.membervu.com | — |
- Super-admin sessions are now renewable (#531): staying in the admin portal past the old ~15-min access-token window should no longer bounce you to login — the session refreshes in the background. Getting logged out mid-work is now a bug worth flagging.
- Two-portal testing: if you log into the org portal and the super-admin portal in the same browser, logging into/out of one should not log you out of the other, and admin requests should not get spurious
403s (#539/#540). If a session on one portal breaks the other, that's the exact regression these fixes address. - Login + registration rate limits still apply (a
429= throttled, not a failure). Membership application (/membership/apply) is now rate-limited too (#501). - The payment gateway is still inert in QA (records only, no live capture) — use "Record Payment" for a manual/offline payment.
- Manual dues approval now activates the member (#527). Approving a bank-transfer dues payment should flip the member to active in the same step — you no longer need a separate activation action. Verify the membership status actually changes.
- Two new membership billing periods — QUARTERLY and LIFETIME (#504) — are selectable on membership types. Existing MONTHLY/ANNUAL members are unaffected.
- Every tenant must have an app domain. The tenant's
appDomain(the bare hostname, e.g.rcme.membervu.com) is now required — a tenant with an unresolvable origin now fails loud with a clear error instead of silently building a broken link (#509). Any place that emails a tenant-origin link should either work or show a clear failure — never a malformed URL. - 4 DB migrations ship — 3 additive plus one constraint (make
appDomainNOT NULL), which is self-protected by a paired backfill. No destructive step. Deploy isprisma migrate deployonly (see §Deploy).
🔐 Auth, session & security — #530, #531, #532, #533, #539, #540
| # | Change | What to verify on stage |
|---|---|---|
| #531 high-blast | Renewable super-admin sessions — platform login now issues a refresh token (server-side allow-list), so admin-portal sessions renew in the background instead of dying at the access-token boundary. | On stg-admin.membervu.com, log in and keep working past ~15 min (or until the access token would have expired) → you are not bounced to login; the session refreshes silently. Log out → you're fully signed out (refresh token revoked). |
| #532 | Admin-portal refresh client + soft re-auth — the admin app proactively refreshes and re-authenticates without a hard reload. | While using the admin portal, an in-flight session refresh happens without a full page reload / without losing your place. |
| #533 | Shared session module — both portals now use one implementation of the session/refresh logic (internal refactor). | Regression-only: org portal login/refresh and admin portal login/refresh both still behave correctly (no functional change expected — confirm nothing broke). |
| #530 | Access-token & impersonation TTL alignment — access-token lifetime matches prod magnitude; impersonation token TTL is single-sourced at 15 min. | When a super-admin impersonates a member, that impersonated session is short-lived (~15 min) and clearly bounded; normal sessions use the standard lifetime. |
| #539 two-portal | Cross-portal refresh-cookie isolation — platform and tenant refresh cookies now have distinct names + scoped paths so they don't collide in a shared browser cookie jar. | Open the org portal and admin portal in the same browser and log into both. Logging in/out of one does not silently log you out of the other; each portal's session refreshes independently. |
| #540 two-portal · security | Bearer preferred over stale cookie — a lingering auth_token cookie no longer shadows a valid admin Authorization: Bearer header (which previously caused spurious 403s). | With both portals open, perform admin actions in the super-admin portal → they succeed (no unexpected 403 Forbidden) even if an org-portal auth_token cookie is present. |
💳 Billing & payments — #527, #504, #503, #502, #497
| # | Change | What to verify on stage |
|---|---|---|
| #527 money | Manual bank-transfer dues approval activates the member — approving a manual dues payment now flips membership to active in the same step. | Submit a bank-transfer dues payment for an inactive/pending member, then approve it as admin → the member becomes ACTIVE immediately (no separate activation needed). Confirm the status change and the membership dates. |
| #504 | QUARTERLY + LIFETIME membership periods — new billing-period options on membership types. | Create/edit a membership type → QUARTERLY and LIFETIME are selectable and save correctly. A LIFETIME member's dues logic + emails don't error on the (null) next-due date. Existing MONTHLY/ANNUAL types are unchanged. |
| #503 | Void-invoice lifted to all sources — voiding is no longer restricted to a subset of invoice origins. | Void an invoice created from different sources (dues, event, manual) → the void action is available and works for each. |
| #502 | Humanized invoice status across the full surface via a single util. | Invoice status labels read as human text (e.g. "Partially Paid", "Voided") consistently across list, detail, and exports — not raw enums. |
| #497 money | Discount cap-guard correctness — a PENDING allocation must not inflate amountPaidCents. | With a discount cap in play, a pending payment/allocation does not count toward the paid total or the discount cap; only settled amounts do. Cross-check the balance. |
🎟️ Events — #524 + staff check-in link
| # | Change | What to verify on stage |
|---|---|---|
| #524 | EVENT_ENDED instead of ACCESS_CODE_EXPIRED when an event is over — clearer member-facing error. | Try to use a check-in access code for an event that has already ended → the message says the event has ended, not that the code expired. |
| — | Staff check-in URL surfaced in the Access Codes panel. | In an event's Access Codes panel, the staff check-in URL is visible and copyable, so staff can open the check-in surface directly. |
🛡️ Security & hardening — #501, #509, #523
| # | Change | What to verify on stage |
|---|---|---|
| #501 security | Rate-limit unauthenticated membership applications — POST /membership/apply is now throttled (closes a spam vector). | Submitting many membership applications rapidly from one client eventually returns 429 (throttled). A normal single application still works. |
| #509 | Fail loud on unresolvable tenant origin — buildTenantOrigin now throws HTTP 400 instead of returning a bad origin; Tenant.appDomain is backfilled + made required. | Tenant-origin links in emails (invites, resets, event links) point at the correct tenant host. There should be no malformed links (e.g. https://rcme.http/...); a misconfigured tenant fails with a clear error rather than sending a broken link. |
| #523 | Async S3 avatar presign migration — member avatar URLs are presigned on the async path; bare S3 keys presigned inline when the redirect service is off. | Member avatars load everywhere they appear (member list, profile, attendee exports) — no broken images or expired-link errors. |
🔧 CI / build (ops) — #262 + guardrails
| # | Change | What to verify on stage |
|---|---|---|
| #262 | Frontend builds copy the shared libs/ workspace — required so both portals resolve the shared session module (#533). | Ops-only: the frontend images build successfully on prod/stage (no module-resolution error). No member-facing change — see §Deploy. |
| G10 / G2 | CI guardrails refined — G10 email-timezone check is now precise (multi-line { timeZone } aware); G2 tenant-origin check no longer false-positives on comments. | Ops-only: notification/email dates still render in the tenant timezone (not UTC). No behavior change for testers beyond correct email dates. |
Suggested review order
- Renewable admin sessions (#531) — stay logged into the super-admin portal past the old expiry window; confirm no silent logout.
- Two-portal cookie scenario (#539/#540) — org + admin portal in one browser; neither breaks the other's session and no spurious admin 403s.
- Manual-dues activation (#527) — approving a bank-transfer dues payment activates the member in the same step.
- New membership periods (#504) — QUARTERLY + LIFETIME selectable and saved; LIFETIME doesn't error.
- Billing correctness — void-all-sources (#503), humanized status (#502), discount cap vs pending allocation (#497).
- Events — EVENT_ENDED message (#524) + staff check-in URL.
- Hardening — application rate limit (#501), tenant-origin links fail loud / no malformed URLs (#509), avatars load (#523).
- Skip: live gateway/webhook capture (inert in QA — records only). Use "Record Payment" for manual payments.
🚀 Deploy / migration notes (ops + reviewer gate)
prisma migrate deploy only (applied in timestamp order); never reset/reseed on prod. Three are additive; one is a constraint that is self-protected by its paired backfill:
20260630000000_backfill_tenant_appdomain(#509) — idempotent backfill of anyNULLTenant.appDomainto<slug>.membervu.com. Data-only, safe to re-run.20260630000001_tenant_appdomain_not_null(#509) —ALTER … SET NOT NULL. Safe because the backfill above runs first. Post-deploy check:SELECT count(*) FROM "Tenant" WHERE "appDomain" IS NULL;= 0.20260630000002_add_quarterly_lifetime_membership_periods(#504) — enumADD VALUE IF NOT EXISTS 'QUARTERLY' / 'LIFETIME'. Additive; existing rows unaffected.20260630000003_platform_refresh_tokens(#531) — newPlatformRefreshTokentable +PlatformAdmin.tokenVersion INTEGER NOT NULL DEFAULT 0. Additive (backfills in place).
COPY libs/ into the build context (required for the shared session module). Build the frontend images from the repo root so libs/ is present, and invalidate the frontend build cache so the new step is honored.
⚙️ Config on prod:
PLATFORM_REFRESH_TOKEN_TTL_SECONDS is new but optional (default 43200 = 12 h — shorter than the tenant refresh TTL because platform admins are higher-privilege). The SESSION_COOKIE_DOMAIN / SAMESITE / SECURE trio should already be set from the Jun 29 deploy — re-confirm, since renewable admin sessions rely on the same cookie machinery. Confirm the cron secret is still set.
✅ Merge: clean
dev → prod merge — 0 conflicts this cycle (no reconciliation branch needed).
🔙 Rollback: redeploy prod from
rollback/prod-pre-release-2026-07-01 (60c28ec1) — code rollback only; all migrations are forward-safe, do not reverse-migrate a live DB.
Source of truth: the membervu/membervu-pm tracker + MR membervu/membervu-saas!264 (merged), direct dev → prod promotion. This page is the tester/reviewer-facing view; the full release note (deploy gate, config, rollback) is membervu-saas/docs/runbooks/2026-07-01-prod-release-dev-to-prod.md. Builds on the Jun 29, 2026 release. Verified present on dev on 2026-07-01. Gateway flows inert in all QA environments.