V114: Switch integration storage rows to uuid-only keys.
Before V113, each integration row in phoenix_kit_settings had a
composite key of the shape integration:<provider>:<name> (e.g.
"integration:google:default"). That key construction baked the
human-chosen name into the row's identity, which forced two
unfortunate constraints:
- Names had to match a strict regex (
[a-zA-Z0-9][a-zA-Z0-9\-_]*) because they were path-style segments in the key column. - Names had to be unique per provider — the
keycolumn has a unique index, so twointegration:openrouter:workrows would collide at insert time.
Both restrictions were storage-layout artifacts, not product decisions. Operators wanted "My Company Drive" and a second OpenRouter account also called "personal" without the system pushing back.
V114 lifts both by collapsing the storage key to just the row's UUID.
The module column (already set to "integrations" for every
integration row via @settings_module) becomes the sole row-class
discriminator, and provider + name live purely in value_json.
Migration steps
For every row in
phoenix_kit_settingswhosekeystarts withintegration::a. Parse
providerandnamefrom the key (handling the legacy V0 shapeintegration:googlewithout a name asprovider=google,name="default"). b. Ensurevalue_jsonhas"provider"and"name"populated (idempotent — leaves correct values alone). c. Ensuremodule = 'integrations'(was already set for integrations created viaadd_connection/3, but legacy rows pre-@settings_modulemay have it NULL). d. Rewrite thekeycolumn to the row'suuid.Stamp the table comment with
'114'.
All work happens in a single transaction (handled by the outer
migrator). Per-row updates use only the row's uuid for routing —
the new shape is key = uuid, so the row's PK is what we touch.
Down migration
The down path is best-effort: duplicate (provider, name) pairs
cannot be represented in the old shape, so on a name collision we
suffix -<8-char-tail> to keep the rewrite well-defined. The tail
is taken from UUIDv7's random segment (substring(uuid::text from 25 for 8)), not the leading timestamp prefix — multiple rows
inserted in the same millisecond would otherwise produce identical
prefixes and collide on the supposedly-unique suffixed key.
Round-trip down → up therefore changes those names by a suffix.
Acceptable for a one-shot data migration that operators rarely
roll back.