f0533dc073
The Gitea-OAuth callback's `auth.provision_user` matched an existing `users` row only by `gitea_id`. A human who signed in first via OTC owns an email-only row (`gitea_id` NULL); a later Gitea-OAuth sign-in with the same email missed that row and INSERTed a new one, colliding on the `idx_users_email` case-insensitive unique index → IntegrityError → 500 callback. Fix (mirror of `otc.provision_or_link_user`): when no `gitea_id` row exists, reconcile by email and link the OAuth identity (gitea_id/gitea_login/profile) onto the existing email row. Owner-zero (§6.1) bootstrap applied on link (matching the fresh-insert path); `permission_state` preserved so linking never changes admission status as a side effect. One §9-surfaced framework fragility of two. backend 680 green (+3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>