refactor(database): the key store's primary key includes kind
Merged
CH
5d ago+15043+15043+01234567890123456789012345678901234567890123456789-154-154-012345678901234567890123456789
P04c of the "Bringing Codex or Claude Code Max plans into Luke" rollout (plan). This is the second step of the split that started in #4205.
What changes
- Migration
20261020212152_account_provider_key_primary_key_kind: a singleALTER TABLEdrops the old(user_id, provider)primary key and addsaccount_provider_key_pkeywithPRIMARY KEY USING INDEXon #4180's unique indexaccount_provider_key_user_provider_kind_idx. Nothing is rebuilt, and the table always has a key. The statement is wrapped in aDOblock that checks for the old constraint, so it is idempotent. It was generated withpnpm --filter @luke/database db:generate --hints '[{"type":"create","kind":"primary_key",…}]'as #4205 describes, then the SQL was replaced with the swap. The snapshot now has the three-column PK and no separate index. - Store (
provider-key-store.ts):upsertRowno longer has thesetWherekind match, andsaveProviderKeyno longer has theUniqueViolationcatch.saveProviderKeynow returnsvoid, because it always keeps the row. The rewrap'swhereand the full-table read now includekind. - Procedures (
provider-keys-procedures.ts): thesubscriptionHeldpre-check and bothother_kind_heldanswers are removed. An Anthropic API key and aclaude_setup_tokenfor the same account and provider now sit side by side. Each is saved and removed by its own kind. - Wire: the
other_kind_heldrejection literal is removed, along with its Settings wording and the two JSON-schema goldens (regenerated withLUKE_UPDATE_FIXTURES=1; the diff is only the dropped enum member). Clients that already shipped still decode every answer. No server answers that rejection any more.
Safety
The deployment before this one must upsert on the three columns. That is #4205 (e22ea70eb), merged at 21:15 UTC. This PR is merged only after production includes that commit.
Tests
- Store: an API key and a setup-token coexist for one account and provider. Each is replaced by its own kind. Removing one kind leaves the other. Another account's rows are untouched.
- Procedures: a token saved beside a key, and a key beside a token. Neither is rejected, and only the key is checked with the provider. Removing either one leaves the other.
- Rewrap: one account holds both kinds for Anthropic, and both move onto the new version.
check:migrationsandcheck:contractionspass. The PGlite store tests apply the swap.
The developer waived WORKFLOW.md's Preview/Mac runs for this rollout. Nothing here is user-visible.
Made with Luke