feat(integrations): show account names and add legal pages #152

Merged
Gitchegumi merged 7 commits from fix/oauth-account-names-legal-footer into main 2026-08-16 23:08:34 -05:00
Gitchegumi commented 2026-08-16 21:30:52 -05:00 (Migrated from github.com)

Summary

  • show recognizable Twitch usernames and YouTube channel titles instead of raw provider IDs
  • support up to five linked Twitch channels per workspace with independent account rows and a shared EventSub webhook secret
  • scope tracked EventSub subscriptions by broadcaster so disconnecting one channel preserves every remaining channel and the shared secret
  • fail closed before account/token removal when Twitch cannot confirm remote subscription deletion
  • enforce workspace credential authority at OAuth link initiation, callback commitment, and disconnect
  • enforce the five-active-Twitch-channel limit server-side while allowing an existing active account to reconnect at the cap
  • serialize OAuth account activation and provider provisioning per workspace with a transaction-scoped PostgreSQL advisory lock
  • keep EventSub secret selection, persistence, and remote subscription creation inside that lock so concurrent first links use one shared secret
  • serialize Twitch disconnect with link/provision so last-account teardown cannot race an in-flight OAuth commitment
  • add a public product homepage, shared footer, privacy policy, and terms and conditions
  • clear stored OAuth token material when an account is successfully disconnected
  • add a prominently ordered Twitch and YouTube OAuth Setup guide with direct provider-console links, exact callback URIs, and a step-by-step optional Google verification checklist
  • document legal operator settings and Google OAuth branding, scope, domain, consent-screen, and demonstration-video requirements
  • add repository-wide AGENTS.md guidance covering architecture, validation, security and OAuth invariants, documentation expectations, and categorized manual branch names

Why

Connected accounts were exposing numeric provider identifiers that creators do not recognize. Twitch also only rendered the first linked account and offered no way to add another channel. The original partial-disconnect implementation rebuilt the entire workspace subscription set and could strand remaining broadcasters if Twitch retained an old subscription or rejected a replacement. The UI-only permission and channel-limit checks could also be bypassed by direct requests.

Concurrent OAuth callbacks created two additional races: a non-atomic count followed by activation could exceed the five-channel limit, and releasing the workspace lock before provisioning could let two first links generate different EventSub secrets. The lock now spans account commitment through provider provisioning, and Twitch disconnect uses that same workspace boundary, ensuring one limit decision and one shared-secret lifecycle operation per workspace at a time.

Public legal pages and a descriptive homepage are needed for a complete Google OAuth verification submission. The provider setup guide was previously buried at the end of the in-app documentation navigation, making the required Twitch and Google console configuration easy to miss. Repository guidance keeps future agent work aligned with the project's security boundaries and development workflow.

User impact

Workspace managers can identify linked accounts by name, connect multiple Twitch channels, and manage each connection separately. Disconnecting one broadcaster now removes only that broadcaster's tracked subscriptions. If Twitch rejects deletion, GitchAlerts keeps the account, tokens, shared secret, and local tracking state intact so the operation can be retried.

Concurrent OAuth completions and Twitch disconnects are serialized per workspace. Reconnecting an already-active account at the five-channel cap remains supported, simultaneous first links create all EventSub subscriptions with the same persisted webhook secret, and a last-account disconnect cannot clear shared state while another link is provisioning.

Public deployments receive same-domain legal URLs suitable for provider configuration after setting LEGAL_OPERATOR_NAME and LEGAL_CONTACT_EMAIL. Developers get direct setup instructions for the Twitch Developer Console and Google Cloud Console, including exact OAuth callbacks and the optional public-app verification process.

Migration

  • adds nullable provider_subscriptions.provider_account_id
  • safely backfills existing Twitch subscription rows only for workspaces with exactly one active Twitch account
  • adds a scoped lookup index used for per-broadcaster teardown
  • ambiguous legacy rows remain unassigned rather than being associated with the wrong broadcaster

Validation

  • web TypeScript check passed
  • database TypeScript check passed
  • Prisma client generation passed
  • web unit suite: 215 tests passed
  • database unit suite: 71 tests passed
  • focused link, callback, disconnect, concurrent cap, concurrent first-link secret, concurrent link-versus-disconnect, and Twitch provider regressions passed
  • Prettier, staged secret scan, and git diff --check passed
## Summary - show recognizable Twitch usernames and YouTube channel titles instead of raw provider IDs - support up to five linked Twitch channels per workspace with independent account rows and a shared EventSub webhook secret - scope tracked EventSub subscriptions by broadcaster so disconnecting one channel preserves every remaining channel and the shared secret - fail closed before account/token removal when Twitch cannot confirm remote subscription deletion - enforce workspace credential authority at OAuth link initiation, callback commitment, and disconnect - enforce the five-active-Twitch-channel limit server-side while allowing an existing active account to reconnect at the cap - serialize OAuth account activation and provider provisioning per workspace with a transaction-scoped PostgreSQL advisory lock - keep EventSub secret selection, persistence, and remote subscription creation inside that lock so concurrent first links use one shared secret - serialize Twitch disconnect with link/provision so last-account teardown cannot race an in-flight OAuth commitment - add a public product homepage, shared footer, privacy policy, and terms and conditions - clear stored OAuth token material when an account is successfully disconnected - add a prominently ordered **Twitch and YouTube OAuth Setup** guide with direct provider-console links, exact callback URIs, and a step-by-step optional Google verification checklist - document legal operator settings and Google OAuth branding, scope, domain, consent-screen, and demonstration-video requirements - add repository-wide `AGENTS.md` guidance covering architecture, validation, security and OAuth invariants, documentation expectations, and categorized manual branch names ## Why Connected accounts were exposing numeric provider identifiers that creators do not recognize. Twitch also only rendered the first linked account and offered no way to add another channel. The original partial-disconnect implementation rebuilt the entire workspace subscription set and could strand remaining broadcasters if Twitch retained an old subscription or rejected a replacement. The UI-only permission and channel-limit checks could also be bypassed by direct requests. Concurrent OAuth callbacks created two additional races: a non-atomic count followed by activation could exceed the five-channel limit, and releasing the workspace lock before provisioning could let two first links generate different EventSub secrets. The lock now spans account commitment through provider provisioning, and Twitch disconnect uses that same workspace boundary, ensuring one limit decision and one shared-secret lifecycle operation per workspace at a time. Public legal pages and a descriptive homepage are needed for a complete Google OAuth verification submission. The provider setup guide was previously buried at the end of the in-app documentation navigation, making the required Twitch and Google console configuration easy to miss. Repository guidance keeps future agent work aligned with the project's security boundaries and development workflow. ## User impact Workspace managers can identify linked accounts by name, connect multiple Twitch channels, and manage each connection separately. Disconnecting one broadcaster now removes only that broadcaster's tracked subscriptions. If Twitch rejects deletion, GitchAlerts keeps the account, tokens, shared secret, and local tracking state intact so the operation can be retried. Concurrent OAuth completions and Twitch disconnects are serialized per workspace. Reconnecting an already-active account at the five-channel cap remains supported, simultaneous first links create all EventSub subscriptions with the same persisted webhook secret, and a last-account disconnect cannot clear shared state while another link is provisioning. Public deployments receive same-domain legal URLs suitable for provider configuration after setting `LEGAL_OPERATOR_NAME` and `LEGAL_CONTACT_EMAIL`. Developers get direct setup instructions for the Twitch Developer Console and Google Cloud Console, including exact OAuth callbacks and the optional public-app verification process. ## Migration - adds nullable `provider_subscriptions.provider_account_id` - safely backfills existing Twitch subscription rows only for workspaces with exactly one active Twitch account - adds a scoped lookup index used for per-broadcaster teardown - ambiguous legacy rows remain unassigned rather than being associated with the wrong broadcaster ## Validation - web TypeScript check passed - database TypeScript check passed - Prisma client generation passed - web unit suite: 215 tests passed - database unit suite: 71 tests passed - focused link, callback, disconnect, concurrent cap, concurrent first-link secret, concurrent link-versus-disconnect, and Twitch provider regressions passed - Prettier, staged secret scan, and `git diff --check` passed
PorteGumi (Migrated from github.com) requested changes 2026-08-16 21:58:09 -05:00
PorteGumi (Migrated from github.com) left a comment

The friendly account identities, public homepage/legal pages, and basic shared-secret reuse path are sound. The public pages returned 200 in a production build and rendered cleanly; web build, monorepo typecheck, lint, 202 web tests, 69 database tests, git diff --check, and hosted CI all pass.

Two blockers remain in the new multi-Twitch lifecycle: the partial-disconnect path can silently disable every remaining broadcaster, and the five-account/manager restrictions exist only in the client. See the inline findings.

Baseline-only notes, not attributed to this PR: pnpm -r test still fails the unchanged shared version assertions (0.1.0 versus root 0.10.9), and pnpm format:check still reports 12 unchanged files. These same failures are present on main and are not blockers for #152.

The friendly account identities, public homepage/legal pages, and basic shared-secret reuse path are sound. The public pages returned 200 in a production build and rendered cleanly; web build, monorepo typecheck, lint, 202 web tests, 69 database tests, `git diff --check`, and hosted CI all pass. Two blockers remain in the new multi-Twitch lifecycle: the partial-disconnect path can silently disable every remaining broadcaster, and the five-account/manager restrictions exist only in the client. See the inline findings. Baseline-only notes, not attributed to this PR: `pnpm -r test` still fails the unchanged shared version assertions (`0.1.0` versus root `0.10.9`), and `pnpm format:check` still reports 12 unchanged files. These same failures are present on `main` and are not blockers for #152.
@ -97,0 +144,4 @@
if (
account.channelId &&
!(await deps.canManageChannelCredentials(session.user.id, session.user.role, account.channelId))
) {
PorteGumi (Migrated from github.com) commented 2026-08-16 21:58:09 -05:00

Blocking: this destructive workspace-wide rebuild can silently break alerts for every Twitch account that remains linked. teardownTwitchEventSub clears the shared secret and deletes local subscription records even when a remote DELETE throws or returns a non-2xx status; this route then provisions with a new secret and ignores each TwitchProvisionResult.failed. If Twitch retained the old subscriptions, the replacement POSTs conflict (409), the endpoint still returns success, and the workspace is left with a new secret but zero usable subscriptions. I reproduced that exact sequence with an exit-successful fetch stub: the remaining broadcaster rebuilt with created: 0, failed: 7. Even after successful deletes, a transient POST failure takes working broadcasters offline. Do not rebuild all remaining broadcasters as a best-effort side effect. Track subscriptions by broadcaster and remove only the disconnected account's records, or otherwise preserve the shared secret and fail closed until remote deletion/recreation is confirmed. Add a route-level partial-disconnect test covering remote DELETE failure, POST failure, and preservation of the remaining accounts.

Blocking: this destructive workspace-wide rebuild can silently break alerts for every Twitch account that remains linked. `teardownTwitchEventSub` clears the shared secret and deletes local subscription records even when a remote DELETE throws or returns a non-2xx status; this route then provisions with a new secret and ignores each `TwitchProvisionResult.failed`. If Twitch retained the old subscriptions, the replacement POSTs conflict (409), the endpoint still returns success, and the workspace is left with a new secret but zero usable subscriptions. I reproduced that exact sequence with an exit-successful fetch stub: the remaining broadcaster rebuilt with `created: 0, failed: 7`. Even after successful deletes, a transient POST failure takes working broadcasters offline. Do not rebuild all remaining broadcasters as a best-effort side effect. Track subscriptions by broadcaster and remove only the disconnected account's records, or otherwise preserve the shared secret and fail closed until remote deletion/recreation is confirmed. Add a route-level partial-disconnect test covering remote DELETE failure, POST failure, and preservation of the remaining accounts.
@ -287,13 +288,27 @@ export function IntegrationsSection({
<AdminNote provider="Twitch" envVars={['TWITCH_CLIENT_ID', 'TWITCH_CLIENT_SECRET']} />
PorteGumi (Migrated from github.com) commented 2026-08-16 21:58:09 -05:00

Blocking: canManage and MAX_TWITCH_CHANNELS only hide this button; neither restriction is enforced at the authoritative OAuth/API path. /api/auth/link accepts any authenticated user, resolves any supplied workspace slug without canManageChannelCredentials, and the sign-in callback upserts/provisions without rechecking permission or the five-active-account cap. DELETE /api/linked-accounts likewise checks account ownership but not current workspace credential-management authority, and now triggers the destructive rebuild above. A viewer/editor—or an owner demoted after linking—can call these endpoints directly, and a manager can link a sixth account by bypassing the UI. Enforce workspace credential permission at link initiation, callback commitment, and disconnect; enforce the active per-workspace Twitch count at callback time while still allowing an existing account to reconnect. Add direct-handler tests for unauthorized roles, changed permissions during OAuth, five-versus-six accounts, and reconnect-at-cap.

Blocking: `canManage` and `MAX_TWITCH_CHANNELS` only hide this button; neither restriction is enforced at the authoritative OAuth/API path. `/api/auth/link` accepts any authenticated user, resolves any supplied workspace slug without `canManageChannelCredentials`, and the sign-in callback upserts/provisions without rechecking permission or the five-active-account cap. `DELETE /api/linked-accounts` likewise checks account ownership but not current workspace credential-management authority, and now triggers the destructive rebuild above. A viewer/editor—or an owner demoted after linking—can call these endpoints directly, and a manager can link a sixth account by bypassing the UI. Enforce workspace credential permission at link initiation, callback commitment, and disconnect; enforce the active per-workspace Twitch count at callback time while still allowing an existing account to reconnect. Add direct-handler tests for unauthorized roles, changed permissions during OAuth, five-versus-six accounts, and reconnect-at-cap.
PorteGumi (Migrated from github.com) requested changes 2026-08-16 21:59:10 -05:00
PorteGumi (Migrated from github.com) left a comment

The two commits pushed during review add repository guidance and OAuth-console documentation but do not change the multi-Twitch production paths covered by the inline blockers on the previous head. Those findings therefore remain current: partial disconnect can silently strand all remaining Twitch broadcasters after remote-delete or reprovision failure, and manager/five-account restrictions remain client-only rather than enforced at OAuth commitment and disconnect.

The new documentation is internally consistent: focused Prettier validation and local Markdown-link checks pass, and git diff --check remains clean. This review supersedes the same findings posted against b0c8ff1 and ties the requested-changes state to the current head.

The two commits pushed during review add repository guidance and OAuth-console documentation but do not change the multi-Twitch production paths covered by the inline blockers on the previous head. Those findings therefore remain current: partial disconnect can silently strand all remaining Twitch broadcasters after remote-delete or reprovision failure, and manager/five-account restrictions remain client-only rather than enforced at OAuth commitment and disconnect. The new documentation is internally consistent: focused Prettier validation and local Markdown-link checks pass, and `git diff --check` remains clean. This review supersedes the same findings posted against `b0c8ff1` and ties the requested-changes state to the current head.
PorteGumi (Migrated from github.com) requested changes 2026-08-16 22:22:59 -05:00
PorteGumi (Migrated from github.com) left a comment

The partial-disconnect blocker is fixed: Twitch subscriptions are now tracked per broadcaster, selective teardown preserves the shared secret and other broadcasters, non-2xx remote deletion fails closed, and the route retains account/token state for retry. Link initiation, OAuth commitment, and disconnect also recheck credential-management authority, and the sequential five-versus-six/reconnect-at-cap cases work.

One part of the cap blocker remains: the callback performs a non-atomic count-then-upsert, so concurrent OAuth completions can exceed the server-side limit. See the inline reproduction.

Current-head validation otherwise passes: Prisma generation and schema validation, 212 web tests, 71 database tests, monorepo typecheck/lint, production web build, git diff --check, and all hosted checks.

The partial-disconnect blocker is fixed: Twitch subscriptions are now tracked per broadcaster, selective teardown preserves the shared secret and other broadcasters, non-2xx remote deletion fails closed, and the route retains account/token state for retry. Link initiation, OAuth commitment, and disconnect also recheck credential-management authority, and the sequential five-versus-six/reconnect-at-cap cases work. One part of the cap blocker remains: the callback performs a non-atomic count-then-upsert, so concurrent OAuth completions can exceed the server-side limit. See the inline reproduction. Current-head validation otherwise passes: Prisma generation and schema validation, 212 web tests, 71 database tests, monorepo typecheck/lint, production web build, `git diff --check`, and all hosted checks.
@ -224,6 +228,185 @@ if (googleEnabled) {
);
PorteGumi (Migrated from github.com) commented 2026-08-16 22:22:59 -05:00

Blocking: the cap check is still subject to a count-then-upsert race. Two managers can complete different Twitch OAuth callbacks concurrently; both can read four active accounts here, both pass the check, and both upsert, leaving six active accounts. I reproduced this against handleLinkedOAuthSignIn with a barrier around the two count calls: both returned true and the final active count was 6. Make the cap decision and LinkedAccount activation atomic per workspace (for example, a serializable transaction with conflict retry or a transaction-scoped PostgreSQL advisory lock), while preserving reconnect-at-cap behavior. Add a concurrent commitment test that starts at four and proves exactly one of two new links succeeds.

Blocking: the cap check is still subject to a count-then-upsert race. Two managers can complete different Twitch OAuth callbacks concurrently; both can read four active accounts here, both pass the check, and both upsert, leaving six active accounts. I reproduced this against `handleLinkedOAuthSignIn` with a barrier around the two count calls: both returned `true` and the final active count was 6. Make the cap decision and LinkedAccount activation atomic per workspace (for example, a serializable transaction with conflict retry or a transaction-scoped PostgreSQL advisory lock), while preserving reconnect-at-cap behavior. Add a concurrent commitment test that starts at four and proves exactly one of two new links succeeds.
PorteGumi (Migrated from github.com) requested changes 2026-08-16 22:34:03 -05:00
PorteGumi (Migrated from github.com) left a comment

The advisory transaction lock fixes the five-account count/upsert race: concurrent commitments are serialized, the fifth account succeeds, and the sixth is rejected. The new regression covers that outcome.

The lock boundary is still too narrow for Twitch's shared-secret lifecycle, however. It is released before auto-provisioning, so two accepted links below the cap can provision concurrently and create subscriptions under different secrets. See the inline reproduction.

Current-head validation otherwise passes: 213 web tests, monorepo typecheck/lint, production web build, git diff --check, and all hosted checks.

The advisory transaction lock fixes the five-account count/upsert race: concurrent commitments are serialized, the fifth account succeeds, and the sixth is rejected. The new regression covers that outcome. The lock boundary is still too narrow for Twitch's shared-secret lifecycle, however. It is released before auto-provisioning, so two accepted links below the cap can provision concurrently and create subscriptions under different secrets. See the inline reproduction. Current-head validation otherwise passes: 213 web tests, monorepo typecheck/lint, production web build, `git diff --check`, and all hosted checks.
@ -224,6 +228,185 @@ if (googleEnabled) {
);
PorteGumi (Migrated from github.com) commented 2026-08-16 22:34:03 -05:00

Blocking: releasing the workspace advisory lock before provisioning still permits a split-secret race. When two new Twitch links are both admissible (for example, a workspace starts below four accounts), their serialized account transactions complete and then both enter provisionLinkedAccount concurrently. If the workspace has no EventSub secret yet, both provisionTwitchEventSub calls can read null, generate different secrets, persist them in succession, and create remote subscriptions under both values; only the last persisted secret will verify callbacks. I reproduced both layers: handleLinkedOAuthSignIn reached provisioning with max concurrency 2, and two concurrent first-broadcaster provisions produced two distinct transport secrets while both reported 7 created subscriptions. Keep Twitch secret selection/persistence and remote provisioning inside the same per-workspace serialization boundary (or add an equivalent provider-level lock that spans the full check/create sequence), and add a concurrent first-link test asserting every subscription uses the one persisted secret.

Blocking: releasing the workspace advisory lock before provisioning still permits a split-secret race. When two new Twitch links are both admissible (for example, a workspace starts below four accounts), their serialized account transactions complete and then both enter `provisionLinkedAccount` concurrently. If the workspace has no EventSub secret yet, both `provisionTwitchEventSub` calls can read `null`, generate different secrets, persist them in succession, and create remote subscriptions under both values; only the last persisted secret will verify callbacks. I reproduced both layers: `handleLinkedOAuthSignIn` reached provisioning with max concurrency 2, and two concurrent first-broadcaster provisions produced two distinct transport secrets while both reported 7 created subscriptions. Keep Twitch secret selection/persistence and remote provisioning inside the same per-workspace serialization boundary (or add an equivalent provider-level lock that spans the full check/create sequence), and add a concurrent first-link test asserting every subscription uses the one persisted secret.
PorteGumi (Migrated from github.com) requested changes 2026-08-16 22:48:29 -05:00
PorteGumi (Migrated from github.com) left a comment

The concurrent-link split-secret race is fixed: account commitment and provisioning now share the advisory-lock boundary, and the new regression proves two first links generate one persisted secret used by all 14 subscriptions.

One cross-operation race remains because disconnect does not acquire that workspace lock. A disconnect can still perform last-account teardown while a link is provisioning under the lock, clear the shared secret, and leave the newly committed account with subscriptions signed by a removed secret. See the inline reproduction.

Current-head validation otherwise passes: 214 web tests, monorepo typecheck/lint, production web build, git diff --check, and all hosted checks.

The concurrent-link split-secret race is fixed: account commitment and provisioning now share the advisory-lock boundary, and the new regression proves two first links generate one persisted secret used by all 14 subscriptions. One cross-operation race remains because disconnect does not acquire that workspace lock. A disconnect can still perform last-account teardown while a link is provisioning under the lock, clear the shared secret, and leave the newly committed account with subscriptions signed by a removed secret. See the inline reproduction. Current-head validation otherwise passes: 214 web tests, monorepo typecheck/lint, production web build, `git diff --check`, and all hosted checks.
@ -97,0 +168,4 @@
console.error('[linked-accounts] Twitch broadcaster teardown failed', {
channelId: account.channelId,
reason: err instanceof Error ? err.message : 'unknown_error',
});
PorteGumi (Migrated from github.com) commented 2026-08-16 22:48:29 -05:00

Blocking: disconnect must participate in the same per-workspace advisory lock as link/provision. Right now a link can hold the lock, upsert its account invisibly inside the transaction, and provision subscriptions while this path runs concurrently without waiting. Disconnect can deactivate the previously visible last account, count zero because the new link is uncommitted, run full workspace teardown, and clear the shared secret; the link then commits an active account whose newly created subscriptions use the removed secret. I reproduced the missing serialization directly: while handleLinkedOAuthSignIn held its workspace lock inside provisioning, handleDeleteLinkedAccount completed and invoked full teardown instead of waiting. Wrap the complete Twitch disconnect decision/teardown/update sequence in the same workspace lock and add a concurrent link-versus-last-disconnect test proving teardown cannot run against an in-flight link.

Blocking: disconnect must participate in the same per-workspace advisory lock as link/provision. Right now a link can hold the lock, upsert its account invisibly inside the transaction, and provision subscriptions while this path runs concurrently without waiting. Disconnect can deactivate the previously visible last account, count zero because the new link is uncommitted, run full workspace teardown, and clear the shared secret; the link then commits an active account whose newly created subscriptions use the removed secret. I reproduced the missing serialization directly: while `handleLinkedOAuthSignIn` held its workspace lock inside provisioning, `handleDeleteLinkedAccount` completed and invoked full teardown instead of waiting. Wrap the complete Twitch disconnect decision/teardown/update sequence in the same workspace lock and add a concurrent link-versus-last-disconnect test proving teardown cannot run against an in-flight link.
Gitchegumi (Migrated from github.com) reviewed 2026-08-16 22:55:37 -05:00
@ -97,0 +168,4 @@
console.error('[linked-accounts] Twitch broadcaster teardown failed', {
channelId: account.channelId,
reason: err instanceof Error ? err.message : 'unknown_error',
});
Gitchegumi (Migrated from github.com) commented 2026-08-16 22:55:37 -05:00

Addressed in 9a4ba80. Twitch disconnect now uses the same per-workspace advisory-lock boundary as OAuth commitment, re-reads the selected account after acquiring the lock, and performs broadcaster teardown, deactivation, the remaining-account count, and final shared-state teardown while that lock is held. Added a concurrent link-versus-last-disconnect regression that pauses link provisioning, confirms disconnect cannot start teardown, then verifies the completed link prevents workspace teardown. Validation: focused route tests (4/4), full web suite (215/215), web typecheck/lint, Prettier, and git diff --check all pass.

Addressed in `9a4ba80`. Twitch disconnect now uses the same per-workspace advisory-lock boundary as OAuth commitment, re-reads the selected account after acquiring the lock, and performs broadcaster teardown, deactivation, the remaining-account count, and final shared-state teardown while that lock is held. Added a concurrent link-versus-last-disconnect regression that pauses link provisioning, confirms disconnect cannot start teardown, then verifies the completed link prevents workspace teardown. Validation: focused route tests (4/4), full web suite (215/215), web typecheck/lint, Prettier, and `git diff --check` all pass.
PorteGumi (Migrated from github.com) approved these changes 2026-08-16 23:07:16 -05:00
PorteGumi (Migrated from github.com) left a comment

All previously blocking multi-Twitch lifecycle issues are resolved. Link initiation, OAuth commitment, provisioning, and disconnect enforce current authority and share the same per-workspace advisory-lock boundary; the five-account cap is concurrency-safe; first-link provisioning uses one persisted EventSub secret; partial disconnect preserves other broadcasters; remote deletion fails closed; and last-account disconnect now waits for in-flight link provisioning before deciding whether to tear down shared state.

Validation at this head passes: Prisma generation/schema validation from the schema-bearing review round, 215 web tests, 71 database tests, monorepo typecheck/lint, production web build, git diff --check, and all hosted checks. Approved.

All previously blocking multi-Twitch lifecycle issues are resolved. Link initiation, OAuth commitment, provisioning, and disconnect enforce current authority and share the same per-workspace advisory-lock boundary; the five-account cap is concurrency-safe; first-link provisioning uses one persisted EventSub secret; partial disconnect preserves other broadcasters; remote deletion fails closed; and last-account disconnect now waits for in-flight link provisioning before deciding whether to tear down shared state. Validation at this head passes: Prisma generation/schema validation from the schema-bearing review round, 215 web tests, 71 database tests, monorepo typecheck/lint, production web build, `git diff --check`, and all hosted checks. Approved.
Sign in to join this conversation.
No description provided.