Release Notes — Jun 25, 2026 — dev → prod promotion (Event Check-in + backend hardening)
membervu/membervu-saas!223) — everything accumulated on dev since the Jun 19 release (!200). 94 commits · 149 files · ~36 issues. Verify it on staging (https://stg-rcme.membervu.com; admin portal https://stg-admin.membervu.com), which builds from dev. The headline is a new feature — Event Check-in (a staff "access code" model, EPIC #412); the rest is a two-wave backend quality-hardening sweep (money totals, status engine, tenant isolation, uploads) plus a small frontend bug sweep.
🧭 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).
This release adds Event Check-in. The twist worth understanding before testing: a check-in "access code" is not an account or a role. An admin generates a short-lived code for a specific event; on-site staff open a public page, type the code + their name, and get a scoped session that can only check people in for that one event. No login, no RBAC role.
Recommended path: (1) read the env + login box; (2) test the Event Check-in flow end-to-end (§🎟️); (3) test money parity (§🛡️ #443/#450) — a high-blast-radius hardening change; (4) test the membership status engine (§🛡️ #447/#453); (5) work the rest top-to-bottom. Full module re-review map: campaign note (see footer) §5.
⚙️ Before you start — environment & logins
| Surface | URL | Login to use |
|---|---|---|
| Org portal (tenant) | stg-rcme.membervu.com | Admin admin@rcme.membervu.com / Admin123!; Events Mgr events@demo.membervu.com / Events123! |
| Staff check-in (NEW) | stg-rcme.membervu.com/rcme/events/<eventId>/check-in/staff | No login — enter the access code + your name |
| Super-admin portal | stg-admin.membervu.com | Super Admin superadmin@zeniark.com / SuperAdmin123! |
| Email inbox (MailHog) | stg-webmail.membervu.com | — |
- New rate limiter: the staff code-redemption endpoint is rate-limited. A
429there means "too many redeem attempts", not a bad code — wait or ask for a backend restart. - Login + registration rate limits still apply (a
429= throttled). - Sessions stay alive via the refresh token shipped last release (Jun 19) — the old 15-min auto-logout is gone.
- Money totals are now computed one way. A single canonical projector (#443/#450) feeds invoice detail, listing, reporting exports, and finance summary. If a total looks wrong, it'll look wrong in all of them — cross-check the same invoice in several places.
- Payment gateway is still inert. A paid walk-in (#424) creates a real Invoice + Payment record, but there is no live gateway charge — verify the records, not a live capture.
- Registration is now idempotent (#448): re-registering the same member for the same event reactivates the existing entry instead of creating a duplicate.
memberOnlyticket types reject non-members. - 5 DB migrations ship — 4 cleanly additive; one (#448 idempotency) runs a defensive de-duplication that needs a prod DB snapshot before deploy (see §Deploy).
🎟️ Event Check-in — NEW feature (EPIC #412) — #413–#428
Admin side (generate/manage codes): org portal → Events → Check-in (
/admin/events/check-in; roles ADMIN / EVENT_MANAGER / SUPER_ADMIN). Staff side (redeem a code, no login): public page
/<tenant>/events/<eventId>/check-in/staff. Enter code + name → scoped check-in portal. Pre-req: an event with registered attendees, and an access code generated by an admin.
✨ The flow
| # | Change | What to verify on stage |
|---|---|---|
| #417 / #418 / #420 / #422 | Staff access-code session model — admin generates a time-boxed, event-scoped code; staff redeem code + name to enter a scoped portal. | As admin, generate a code for an event. Open the staff page (no login) → enter the code + a name → you land in the check-in portal for that event only. A wrong/expired code is rejected. |
| #419 | Scoped-token middleware enforces the event boundary (B1 security) + immutable audit trail. | The scoped session cannot act on a different event (try reusing the session against another event's check-in API → denied). Every check-in writes an audit entry that can't be edited/deleted from the portal. |
| #414 / #423 | Attendee search in the portal. | Search a registrant by name/email → result appears → check them in. |
| #415 | Member / guest designation pill on results. | Each result shows whether the attendee is a member or a guest. |
| #426 / #424 / #425 | Walk-in tab + walk-in registration; paid walk-ins create a real Invoice + Payment via a shell guest member; best-effort welcome email. | In the Walk-in tab, register an on-site guest. For a paid walk-in → confirm an Invoice + Payment record is created; a welcome email arrives (check stg-webmail.membervu.com). |
| #421 | Audit-trail viewer panel. | Open the audit panel → every check-in / walk-in / undo action is listed with who + when. Export works (#419). |
| #416 | Reconciliation — consolidated reconcile stats on the attendance report. | Run reconcile → the attendance report shows consolidated stats (checked-in vs no-show vs registered) in one place. |
| #427 / #428 | Platform parity — super-admin portal check-in (codes, walk-in, audit). | On stg-admin.membervu.com, do the same code/walk-in/audit flow for a tenant's event — same isolation + audit guarantees. |
🛡️ Backend quality-hardening — #431–#453 (two sweeps)
Mostly invisible correctness/security work, but several items change behavior testers should re-verify. Grouped by what to check.
| # | Area | Change & what to verify |
|---|---|---|
| #443 / #450 | money high-blast | All invoice totals now flow through one canonical projector. Open the same invoice in detail view, the listing, a reporting export, and the finance summary → every total must match. Spot-check an invoice with discounts / line-items / credits. |
| #447 / #453 | membership | Unified status-transition engine + single expiry calc. Drive activate / suspend / reactivate / expire → only valid transitions allowed; the expiry date is identical across all activation paths (approval, renewal, admin reactivation). |
| #446 | auth security | Deactivate now revokes JWTs immediately. Deactivate a logged-in user → their next request is 401 (no waiting for token expiry). |
| #448 | events | Registration idempotency + memberOnly tickets. Register → cancel → re-register the same member → no duplicate. A memberOnly ticket rejects a non-member. Quantity = 0 is rejected. |
| #444 / #451 | uploads security | Filename sanitization on every upload (#444/G16) + presign for logos/avatars; proofUrl is now key-only (#451/G11). Upload a file with a hostile name (../, control chars, =cmd) → neutralized; the audit row stores the sanitized name. Existing stored images/proofs still render. |
| #433 / #449 | communications | Broadcast preview + Send-Test wiring (#433); resolveBroadcastRecipients + segment-filter validation + token strip (#449). Preview/Send-Test render; an invalid segment filter is rejected; merge-tag tokens are stripped/escaped (carries last release's #252 XSS hardening). |
| #445 | infra | attachTenantPrisma wired into production server + startup catalog check (tenant-isolation NFR-10). No direct UI behavior; backend should boot cleanly and stay tenant-isolated. |
| #436 / #438 / #431 / #432 | misc | Two-token member search (#436); DRAFT-event past-start guard (#438 — can't set a draft's start in the past); member status-history dedup (#431); membership-type amount rejects whitespace/invalid currency (#432); createManualInvoice with a bad memberId returns 404, not 500. |
🐛 Frontend bug sweep — #430, #434, #437, #439
| # | Fix | What to verify on stage |
|---|---|---|
| #430 | Confirmation-page buttons are block-level (full clickable area). | On a confirmation page, the whole button area is clickable (not just the text). |
| #434 | Prospects edit-status modal resets on close/save. | Open the prospect status modal, change something, close → reopen → it's reset (no stale state). Same after save. |
| #437 | Robust 404 for public event detail. | Open a bad/nonexistent public event slug → a graceful "not found", not a crash. |
| #439 | Member /events/:slug not redirected to the public view during session hydration. | As a logged-in member, open an event by slug → you stay on the member event view (no flash/bounce to the public page while the session loads). |
| — | Default membership-type pre-select in approve-prospect modal; full-width clickable /invoices rows. | Approve-prospect modal pre-selects the default type; clicking anywhere on an invoice row opens it. |
🔐 Platform — #427/#428 (check-in parity) + tenant-detail fields
| Change | What to verify on stage (admin portal) |
|---|---|
| Super-admin check-in parity (codes / walk-in / audit) — #427/#428. | Covered in the Event Check-in section above — run it from stg-admin.membervu.com against a tenant's event. |
Split + attestation fields exposed to SUPER_ADMIN in tenant detail. | On a tenant's detail page in the admin portal, the commercial split + attestation fields are visible to a super-admin (and only to a super-admin). |
Suggested review order
- New feature: Event Check-in end-to-end (#413–#428) — generate code → staff redeem → search → check in → walk-in → audit → reconcile; isolation + revoke + audit-immutability gates.
- Money parity: same invoice across detail / list / export / finance summary (#443/#450).
- Membership status engine: transitions + single expiry calc (#447/#453).
- Auth: deactivate → immediate 401 (#446).
- Registration idempotency + member-only tickets (#448).
- Uploads/security: hostile filename neutralized; existing images/proofs still render (#444/#451).
- Communications: broadcast preview/Send-Test + segment validation (#433/#449).
- Frontend sweep + misc (#430/#434/#437/#439, #432/#436/#438).
- Skip: live gateway/webhook flows (inert — paid walk-in creates records only).
🚀 Deploy / migration notes (ops + reviewer gate)
prisma migrate deploy only; never reset/reseed on prod.
add_event_checkin_access_code(#413) — new table. Additive.add_event_checkin_audit_log(#413) — new enum + table. Additive.add_registration_walkin_fields(#413) — 7 nullable columns + 1 nullable FK. Additive.ticket_type_member_only(#448) —memberOnly BOOLEAN DEFAULT false. Additive.- ⚠️
event_registration_unique_idempotency(#448) — runs a defensive dedupDELETEbefore creating a partial unique index. No-op on a healthy DB, but theDELETEis not auto-reversible → 🔴 take a prod DB snapshot before deploying.
auth-service/src/utils/rateLimiter.ts — keep prod's env-tunable limits and add the new #418 check-in session limiter. Do not revert prod's env-driven values.
🔙 Rollback: redeploy prod from
rollback/prod-pre-release-2026-06-25 (d447c58c) — code rollback only; do not reverse-migrate a live DB.
Source of truth: the membervu/membervu-pm tracker + draft MR membervu/membervu-saas!223. This page is the tester/reviewer-facing view; the full campaign note (focused test plan, deploy gate, module re-review matrix) is tester-files/03_EXECUTION_REPORTS/prod-release-2026-06-25/RELEASE_NOTES.md; the permanent archive entry is tester-files/07_RELEASE_NOTES/2026-06-25-release-notes.md. Builds on the Jun 19, 2026 release. Verified present on dev on 2026-06-25. Gateway flows inert in all QA environments.