A finished interface can hide an unfinished system. RSVQuick could create a styled invitation, attach questions, generate a QR code, and record a guest’s reply. That happy path was real. So were three risks behind it: anonymous clients queried application tables, responses and survey answers were written separately, and the browser decided whether the final seat was still available.
Start with evidence, not a rewrite
The original checkout built successfully. We pinned that exact upstream commit with an annotated baseline tag, saved the production dependency audit and build output, and measured the only JavaScript bundle before changing anything. The baseline contained 41 production advisories: 21 high and 3 critical. It also had a 1,081,500-byte gzip main bundle, no tests, no CI workflow, and no versioned database migration.
Every before-number in this article comes from the tagged commit
fixmymvp-baseline-2026-07-25. Every after-number comes from
saved machine-readable evidence in the rescue repository.
The browser was doing the database’s job
The guest page fetched the invitation, survey questions, choices, and response rows directly. It counted accepted replies in the browser and disabled the submit button when the event looked full or closed. On submit, it inserted a response first and survey answers second.
A disabled button is useful feedback, but it is not a capacity guarantee. Any second browser can make the same decision from the same stale count.
The failure modes were concrete
Public clients knew too much
The guest experience only needed a safe invitation view and an accepted count. Direct table access widened the data exposed to anonymous code.
Two guests could see one seat
Both browsers could read the same count, decide capacity remained, and insert independently.
Half a response could survive
A response and its survey answers were separate client operations. A second failure could leave partial data behind.
The repository was not the system
Table definitions and deployed access policies were absent, so a clean environment could not recreate the application contract.
Move the rule to the boundary that can enforce it
The rescue adds one versioned migration for the full schema, constraints, indexes, row-level policies, and two deliberately narrow public functions. Authenticated hosts keep normal CRUD access through owner-scoped policies. Anonymous guests lose all direct table access.
Make the last seat impossible to claim twice
The new submit_rsvp() function takes an invitation, a reply,
any survey answers, and an idempotency key. Inside one transaction it takes
an advisory lock for that invitation, reloads the authoritative row, checks
the close date and accepted count, validates every choice, then inserts the
response and answers together. A retry with the same key returns the
original result instead of duplicating it.
The decision is serialized at the database. Guest B receives an explicit capacity message; no second accepted row is created.
Prove the behavior at three levels
A critical rule is not finished when the implementation looks right. The rescue checks the small decision helpers, the database contract itself, and the user-visible race through real browser contexts.
The browser suite exercises protected routes, a real password-recovery email, login and logout, host creation and tenant isolation, required surveys, invalid, expired, full, and declined invitations, plus phone-width layout and keyboard navigation. Its final scenario opens two isolated guest contexts, makes them submit for one remaining seat together, and requires exactly one confirmation and a final owner count of 1/1. Zero-exclusion axe scans cover the landing, login, dashboard, and invitation experiences.
The measured result
Production advisories fell from 41 to 2; high and critical findings fell from 24 to 0.
The main JavaScript entry fell from 1,081,500 to 366,183 gzip bytes, a 66.1% reduction; route chunks keep the public RSVP experience to another 26,420 gzip bytes.
The repository moved from no tests to 125 automated checks and from no CI to a three-job GitHub Actions release gate.
A clean local environment can now recreate the database from one migration and test its authorization contract.
Median desktop Lighthouse scores moved from 91 to 99 for performance and 67 to 100 for accessibility, with 100 for best practices and SEO after the rescue.
A rescue is a handoff, not a magic trick
RSVQuick did not need its product idea replaced. It needed the invisible parts of the product to become explicit: who can access what, where the final-seat decision lives, how partial writes are prevented, how a clean environment is rebuilt, and which tests prove the promise still holds.