# Restoring the clinic database — runbook

Both production incidents to date happened **after a restore**, not during one:
a restored database once carried an empty `migrations` table (running `migrate`
would have re-created every table over live data), and once an empty
`sequences` table (every patient registration then failed with a 500, because
the counter re-issued an MRN that already existed — and retries reserve the
same colliding number forever, so it never heals on its own).

A restore is not finished when the data is back. It is finished when the steps
below all pass.

## Before you restore

1. **Stop the scheduler** so nothing runs mid-restore:
   `Stop-ScheduledTask -TaskName "Clinic-Scheduler"`
2. Note which archive you are restoring (`C:\laragon\backups\daily\*.zip.enc`,
   decrypt per the backup script's key) and **copy it somewhere else first** —
   never restore from your only copy.

## Restore

3. Restore the dump into the `clinic` database with the `clinic_migrate` user
   (the runtime `clinic_app` user has no DDL rights and cannot create tables).

## After you restore — all four, every time

4. **Check the migrations ledger is populated:**

   ```bash
   php artisan tinker --execute="echo DB::table('migrations')->count();"
   ```

   Expect a number in the dozens. **Zero means STOP** — do not run `migrate`;
   it would rebuild the schema over your data. Re-restore from an archive that
   includes the `migrations` table.

5. **Realign the document counters:**

   ```bash
   php artisan sequences:reconcile --dry-run
   php artisan sequences:reconcile
   ```

   This walks every counter (MRN, appointment, invoice, prescription, lab
   order…) and advances any that fell behind the documents that exist. It only
   ever moves forward, and ignores references the current numbering template
   would never have produced.

6. **Clear runtime caches** (settings are cached; a restore changes them
   underneath the cache):

   ```bash
   php artisan cache:clear
   php artisan view:clear
   ```

7. **Prove the system can write:** register a throwaway test patient through
   the UI, confirm the MRN allocates, then archive it. If registration 500s,
   go back to step 5.

## Finish

8. Restart the scheduler: `Start-ScheduledTask -TaskName "Clinic-Scheduler"`
9. Next morning, confirm `backups:verify` reported healthy (it runs at 08:00
   and writes an ERROR log line if the nightly backup has stopped).

## Rules that make restores safe to need

- The nightly archive must exist **off this machine** before it counts as a
  backup. A dump on the disk it protects dies with that disk.
- Never run `migrate:fresh`, `db:wipe` or `migrate --database=mysql` against
  an environment you have not just confirmed with
  `php artisan tinker --execute="echo config('database.connections.mysql.database');"`.
- One restore operator at a time, for the same reason as one test run at a
  time: the database cannot referee two.
