Meldt ind som "AI-informationerne gemmes ikke". De blev gemt. De blev taget bagefter. `user_settings` blev oprettet med `user_id` som hele sin primærnøgle — én række per person, uanset scope — og upserten skrev `scope` om ved konflikt. Der er tre scopes i live: `ai`, `gmail` og `calendar`. De delte den ene række, så hver skrivning slettede de to andre, og hver eneste svarede 204. Det virker begge veje: at forbinde kalenderen slettede Gmail-forbindelsen, og Gmail slettede kalenderen. Fælden var set komme to gange. 0011 gav instansens indstillinger deres egen tabel delvis af den grund og skrev advarslen ned med rene ord; 0014 flyttede postkasserne ud og skrev, at advarslen "nær var trådt i for anden gang". Tredje gang var kalenderen, som lagde et `calendar`-scope ved siden af de to andre. Lektien værd at holde fast i: en advarsel i en migration bliver læst af den, der redigerer *den* migration, og det var ingen. Formen er rettet her, så der ikke er noget tilbage at advare om — `PRIMARY KEY (user_id, scope)`, og upserten rammer den nøgle. Tabellen bygges om, fordi SQLite ikke kan ændre en primærnøgle på plads. De rækker, der findes, kopieres som de er: hver person har højst én, og den holder det scope, der vandt sidst. Det, de tidligere skrivninger tog, er overskrevet og kan ikke hentes tilbage her — efter opdateringen skal AI-udbyderen sættes op igen, og Gmail-forbindelsen tjekkes. To prøver: én mod store-laget, hvor formen var fejlen, og én gennem handlerne, hvor den blev set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38 lines
1.8 KiB
SQL
38 lines
1.8 KiB
SQL
-- One row per person *and scope*, which is what the table always meant.
|
|
--
|
|
-- `user_settings` was created with `user_id` as its whole primary key, and its
|
|
-- upsert rewrote `scope` on conflict. So a person had exactly one settings row,
|
|
-- and whichever scope was written last was the only one that survived: saving an
|
|
-- AI provider deleted the Gmail connection, and connecting a calendar deleted the
|
|
-- AI provider. Nothing reported it — the write succeeded, and the loss was in the
|
|
-- row it landed on.
|
|
--
|
|
-- This was seen coming twice. Migration 0011 gave the instance's settings their
|
|
-- own table partly for this reason and wrote the warning down in as many words;
|
|
-- 0014 moved mailboxes out and said the warning "was nearly walked into a second
|
|
-- time". The third time was the calendar, which added a `calendar` scope beside
|
|
-- `ai` and `gmail` — and walked in.
|
|
--
|
|
-- The lesson worth keeping: a warning in a migration is read by whoever is editing
|
|
-- *that* migration, and nobody was. The shape is fixed here so there is nothing
|
|
-- left to warn about.
|
|
--
|
|
-- SQLite cannot change a primary key in place, so the table is rebuilt. Existing
|
|
-- rows are copied as they are: each person has at most one, and it holds whatever
|
|
-- scope won last. What the earlier writes lost is already gone and cannot be
|
|
-- recovered here — the values were overwritten, not orphaned.
|
|
CREATE TABLE user_settings_new (
|
|
user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
scope TEXT NOT NULL DEFAULT 'general',
|
|
values_json TEXT NOT NULL DEFAULT '{}',
|
|
updated_at INTEGER NOT NULL,
|
|
PRIMARY KEY (user_id, scope)
|
|
);
|
|
|
|
INSERT INTO user_settings_new (user_id, scope, values_json, updated_at)
|
|
SELECT user_id, scope, values_json, updated_at FROM user_settings;
|
|
|
|
DROP TABLE user_settings;
|
|
|
|
ALTER TABLE user_settings_new RENAME TO user_settings;
|