V107: Pin AI endpoints to a specific integration row via
integration_uuid + add the missing unique index on name.
Prior to this migration, phoenix_kit_ai_endpoints.provider stored a
provider-type tag ("openrouter") and the integrations system had to
guess which connection row to use at request time via
find_first_connected/1 — necessary compensation for AI never having
recorded the user's actual choice. This column closes that gap: each
endpoint now references the integration row directly, so renaming or
re-labelling integrations doesn't change which one an endpoint uses,
and the resolver's default-based fallback chain becomes dead code.
Up
- Adds nullable
integration_uuid uuidcolumn tophoenix_kit_ai_endpoints. - Adds a btree index on the new column for FK-style lookups.
- Backfills
integration_uuidfrom the existingproviderstrings:- For
provider = "openrouter:my-key"→ match the exact storage rowintegration:openrouter:my-key. - For
provider = "openrouter"(bare) → pick the most-recently-validatedintegration:openrouter:*row, breaking ties onuuid ASC(UUIDv7 is time-ordered, so smaller uuid ≈ older row → ASC = oldest first when timestamps tie). - Endpoints with no matching integration row stay NULL; the user re-picks via the endpoint form.
- For
- Adds a UNIQUE index on
lower(name)so duplicate endpoint names are rejected at the DB layer.Endpoint.changeset/2has registeredunique_constraint(:name)since the schema was first extracted, but the V34 migration that createdphoenix_kit_ai_endpointsnever created the index — duplicate names were silently accepted. The hand-rolled test migration inphoenix_kit_aimasked this for years; surfaced when that test migration was removed (2026-04-29).
Down
Drops the unique index, the integration_uuid index, and the integration_uuid column. Lossy — endpoint→integration choices made via the new picker are discarded.