Flow
The question each step asks before it acts
1Main script
Is the most basic table missing? Only then install from zero.
2Chore columns
Is the guard column missing? Then add the new columns.
3Name copy
Should run every time, since it only fills blank names.
4Break-schedule helper
Is one table missing? Then it installs from zero, which can wipe real data.
Every time, the scheduling app's migrate script ran the installer first. The installer starts by dropping every table. On an empty database, no harm. On the real one, with the stores' staff, shifts and chores, it wipes the app before the import starts.
Where the pressure came from
The agent was turning chores into templates. A chore used to be a line of text with maybe a person and a date. Now it has a name, a priority, a repeat rule, a time window, limits on how often it lands, and side tables for who may do it. Adding that to the installer is easy. Moving a live database to the new shape, without a wipe, is the real job.
Asking the database first
The upgrade doesn't remember past runs. It asks the database what's there: this table, this column, this index, this key. Each step runs only if its piece is missing. One new column stands guard for the big block of new columns. On an old database the upgrade adds what's missing. On a current one it should do nothing.
Old chores get a name copied from their old text. And the installer now runs only when the most basic table is missing, which means the database is truly empty.
The second door, still open
A helper that adds default break schedules also ran the installer. Now it only does that when one table is missing. But one missing table isn't an empty database. A database full of real data that lacks that table would still get wiped.
The guard covers too much
The name copy sits behind the same guard as the new columns. If the columns get added and the copy fails, the next run skips both, and those chores stay nameless. The copy only touches blank names, so it can run every time.
The copy belongs outside the guard, where it can run every time. That change hasn't been made yet, so the risk is still live.
One more thing: every old chore becomes an active, daily task the moment the upgrade runs, and nothing says so.
A typo standing guard
The old script loaded that helper by the wrong file name. Unless I'm misreading it, it would have crashed near the top, before the wipe. The new change fixed the name. So the new check is now the only thing in the way at the entry point.
Untested, and only a backup to go back to
There are no tests yet, and going back means restoring a backup. The chore table is now written down in two places, with nothing that compares them, so the day they drift apart I won't hear about it.