# `PhoenixKit.Migrations.Postgres.V114`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.165/lib/phoenix_kit/migrations/postgres/v114.ex#L1)

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 `key` column has a
    unique index, so two `integration:openrouter:work` rows 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

1. For every row in `phoenix_kit_settings` whose `key` starts with
   `integration:`:

   a. Parse `provider` and `name` from the key (handling the legacy
      V0 shape `integration:google` without a name as
      `provider=google`, `name="default"`).
   b. Ensure `value_json` has `"provider"` and `"name"` populated
      (idempotent — leaves correct values alone).
   c. Ensure `module = 'integrations'` (was already set for
      integrations created via `add_connection/3`, but legacy rows
      pre-`@settings_module` may have it NULL).
   d. Rewrite the `key` column to the row's `uuid`.

2. 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.

# `down`

# `up`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
