# Soft-launch beta — pre-flight checklist > Operational checklist that must reach 100% green before the first > invitation goes out. Companion to `docs/SOFT_LAUNCH_BETA_2026.md` > (the bigger picture). This file is purely the "before you press > send, has every gate been verified?" view. The whole reason the soft-launch is "soft" is that it lets you catch infrastructure surprises with 50 testers instead of 50 000. To get that benefit, the infrastructure has to actually work BEFORE the invitations land. This checklist is the gate. ## T-72h checklist (3 days before send) ### Database - [ ] `migrations/990_beta_invites.sql` applied to staging. Verify with : ```bash psql "$STAGING_DATABASE_URL" -c "SELECT count(*) FROM beta_invites;" ``` Expected : `0` (table exists, empty). - [ ] Same migration applied to prod (whenever prod tag goes out). - [ ] Backup-freshness OK on both environments : ```bash pgbackrest --stanza=veza info | head -20 ``` Most recent full or diff < 24 h old. ### Cohort CSV - [ ] CSV file built from the operator's chosen sources (mailing list + contacts + community partners). Format per `scripts/soft-launch/validate-cohort.sh` header. - [ ] `validate-cohort.sh` returns exit 0 (or exit 2 with explicit operator acknowledgement of the warnings). - [ ] Distribution sanity : `≥ 5` creators, `≥ 20` listeners, `≥ 3` distinct cohort labels, `≥ 50` total rows. ### Email infrastructure - [ ] SMTP credentials live in the operator's machine `~/.msmtprc` (or whatever `SEND_CMD` resolves to). - [ ] `templates/email/beta_invite.eml.template` reviewed — wording, cohort variable, code variable. - [ ] Test send to operator's own email : ```bash echo "ops@veza.fr,test-cohort,ops@veza.fr" > /tmp/me.csv DATABASE_URL=$STAGING_DATABASE_URL FRONTEND_URL=https://staging.veza.fr \ SEND=1 bash scripts/soft-launch/send-invitations.sh /tmp/me.csv ``` Verify the eml renders correctly in your mail client (links clickable, fonts loaded, no `{{TO_ADDR}}` literals leaking). ### Backend invite-redemption path - [ ] Visit `https://staging.veza.fr/signup?invite=`. Expected : signup form pre-fills the code, refuses to submit without it, marks the invite as `used_at = NOW()` after success. - [ ] Try an invalid code → form rejects with a clear error message. - [ ] Try the same code twice → second attempt rejects (one-time use). - [ ] Try an expired code → form rejects with "expired". ### Acceptance-gate monitoring - [ ] Run `monitor-checks.sh` once on staging — every gate either ✅ or ⚪ (unknown), no 🔴. ```bash DATABASE_URL=$STAGING_DATABASE_URL \ SENTRY_AUTH_TOKEN=... \ PROM_URL=https://prom.veza.fr \ bash scripts/soft-launch/monitor-checks.sh ``` - [ ] Schedule the cron run (or tmux session) so the gate state is visible during the bêta window without manual re-run. ### Communications - [ ] Discord `#beta-feedback` channel created, ground rules pinned. - [ ] Typeform feedback form created ; URL pasted into `templates/email/beta_invite.eml.template` if not already in the cohort label. - [ ] Status page maintenance window declared for the duration — "elevated alerting may occur during beta period." - [ ] Operators on duty for the day rota'd in the calendar (every 4 h shift, primary + backup). ## D-day checklist (the day of send) ### Last hour before send - [ ] Most recent k6 nightly green (within 30 h). - [ ] No pending high-severity Sentry issue. - [ ] No PagerDuty incident open. - [ ] HAProxy + backend healthchecks green : ```bash curl -s https://staging.veza.fr/api/v1/health | jq .status ``` - [ ] MinIO drives all online ; pgBackRest drill ran successfully in the last 7 days. ### Send - [ ] `validate-cohort.sh` exit code 0 (or 2 with explicit override). - [ ] `send-invitations.sh` in DRY-RUN mode : eml output dir reviewed. - [ ] `send-invitations.sh` with `SEND=1` : dispatch.log reviewed after run, `0` failed dispatches. - [ ] First three invitees received the email within 5 min (manual check on three different domains : gmail / proton / one custom). ### Hour 1 post-send - [ ] First sign-up landed (`SELECT count(*) FROM beta_invites WHERE used_at IS NOT NULL;` returns ≥ 1). - [ ] No spike in 5xx on Grafana "Veza API Overview". - [ ] Discord `#beta-feedback` has at least one "I'm in" message. ### Every 4 h during the bêta window - [ ] Re-run `monitor-checks.sh` (or the cron wakes you). - [ ] Triage any HIGH-severity report within 1 h (per `docs/SOFT_LAUNCH_BETA_2026.md` §"Issue triage matrix"). - [ ] Update the issues-reported table in `docs/SOFT_LAUNCH_BETA_2026.md` so the decision call has fresh data. ## D+0 18:00 UTC — decision call - [ ] Tech lead, product lead, on-call engineer all on the call. - [ ] `monitor-checks.sh` final run shown live ; verdict screenshotted. - [ ] Each acceptance-gate row from `SOFT_LAUNCH_BETA_2026.md` §"Acceptance gate" walked through verbally. - [ ] Unanimous GO or any one NO-GO documented in the meeting notes. - [ ] Decision logged in `docs/SOFT_LAUNCH_BETA_2026.md` §"Take-aways". If GO : the v2.0.0-public tag goes out the next morning. If NO-GO : the meeting decides scope of fix-pass + new acceptance date. ## Linked artefacts - `docs/SOFT_LAUNCH_BETA_2026.md` — the bigger picture (cohort definition, email template inline, day timeline, monitoring list, acceptance gate, decision protocol) - `migrations/990_beta_invites.sql` — schema this depends on - `scripts/soft-launch/validate-cohort.sh` — pre-send sanity check - `scripts/soft-launch/send-invitations.sh` — batch insert + send - `scripts/soft-launch/monitor-checks.sh` — live gate poll - `templates/email/beta_invite.eml.template` — the email recipients receive - `docs/GO_NO_GO_CHECKLIST_v2.0.0_PUBLIC.md` — the v2.0.0 checklist this unblocks