Skip to content

fix: surface permission sync issues requiring user action#1484

Merged
brendan-kellam merged 5 commits into
brendan/classify-permission-sync-errorsfrom
brendan/permission-sync-action-required-ux
Jul 23, 2026
Merged

fix: surface permission sync issues requiring user action#1484
brendan-kellam merged 5 commits into
brendan/classify-permission-sync-errorsfrom
brendan/permission-sync-action-required-ux

Conversation

@brendan-kellam

@brendan-kellam brendan-kellam commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Stack created with GitHub Stacks CLI

Fixes SOU-1560
Fixes SOU-1177

image image image

Summary

  • persist a structured account issue when permission sync fails closed
  • clear the issue only after permissions sync successfully
  • show a persistent action-required banner and unhealthy linked-account state
  • schedule permission recovery immediately after OAuth reauthentication
  • stop treating transient token refresh errors as reconnect-required UX

Testing

  • yarn workspace @sourcebot/backend test --run (201 tests)
  • yarn workspace @sourcebot/web test --run (1,095 tests)
  • backend and database package builds
  • Prisma schema validation
  • ESLint on touched web files

Part of stack #1483. Depends on #1482.


Note

Medium Risk
Changes how repository access is represented and communicated after permanent sync failures (security-sensitive), plus a DB migration and auth-time sync scheduling, but behavior is scoped to classified fail-closed cases with tests.

Overview
When permission sync fails closed and clears cached repo access, the backend now persists a structured permissionSyncIssue on the account (REAUTHENTICATION_REQUIRED or INSUFFICIENT_SCOPE) in the same transaction as the permission wipe, and clears it only after a successful sync completes.

The web app surfaces this through a non-dismissible permission-sync banner (action required vs. still syncing), enriches getPermissionSyncStatus with per-account issues, and updates linked accounts to show “needs attention” with reconnect/reauthorize instead of “refresh permissions.” OAuth reauthentication schedules an automatic permission-sync retry via a shared worker client; manual refresh polling now keys off full job status (including failed toasts).

Linked-account UX no longer drives recovery from tokenRefreshErrorMessage; transient refresh failures stay out of the reconnect flow.

Reviewed by Cursor Bugbot for commit a962821. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added clear warnings when permission synchronization requires reauthentication or additional access.
    • Added guided recovery through linked-account settings, including reconnect and permission-review actions.
    • Permission sync status now updates automatically and clears after successful recovery.
    • Successful reauthentication can automatically retry permission synchronization.
  • Bug Fixes

    • Improved handling of permanent versus temporary synchronization failures.
    • Prevented stale repository access from remaining available after permanent permission failures.
  • Documentation

    • Updated the unreleased changelog with the new warnings and recovery guidance.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Permission synchronization now persists classified account issues, clears cached permissions on permanent failures, exposes issue status through APIs, retries after reauthentication, and presents actionable banner and linked-account recovery states.

Changes

Permission sync issue lifecycle

Layer / File(s) Summary
Persist and classify sync issues
packages/db/prisma/schema.prisma, packages/db/prisma/migrations/..., packages/backend/src/ee/accountPermissionSyncer.ts, packages/backend/src/ee/accountPermissionSyncer.test.ts
Adds issue enum and account fields; classified failures transactionally clear cached permissions and record issue metadata, while successful syncs clear it.
Expose status and trigger recovery
packages/web/src/app/api/(server)/ee/permissionSyncStatus/*, packages/web/src/features/workerApi/*, packages/web/src/auth.ts
Returns structured account issues, centralizes worker requests, and schedules permission sync after reauthentication.
Render permission-sync banners
packages/web/src/app/(app)/components/banners/*, packages/web/src/app/(app)/layout.tsx
Passes issue status into the banner, which displays syncing or recovery guidance and refreshes when issues resolve.
Show linked-account recovery actions
packages/web/src/ee/features/sso/actions.ts, packages/web/src/ee/features/sso/components/linkedAccountProviderCard.*
Replaces token-refresh error state with permission-sync issue state and renders reconnect or scope guidance.
Document the behavior
CHANGELOG.md
Adds an Unreleased changelog entry for action-required warnings and guided recovery.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WebAuth
  participant WorkerApi
  participant PermissionSyncer
  participant Database
  participant PermissionSyncBanner
  User->>WebAuth: reauthenticate linked account
  WebAuth->>WorkerApi: request account permission sync
  WorkerApi->>PermissionSyncer: start sync job
  PermissionSyncer->>Database: record or clear permission-sync issue
  PermissionSyncBanner->>Database: poll permission-sync status
  Database-->>PermissionSyncBanner: issue or syncing status
  PermissionSyncBanner-->>User: show recovery or progress banner
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: surfacing permission sync issues that require user action.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brendan/permission-sync-action-required-ux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

This comment has been minimized.

@brendan-kellam brendan-kellam changed the title brendan/permission sync action required ux fix: surface permission sync issues requiring user action Jul 22, 2026
Comment thread packages/web/src/app/api/(server)/ee/permissionSyncStatus/api.ts
Comment thread packages/backend/src/ee/accountPermissionSyncer.ts
@brendan-kellam

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/web/src/features/workerApi/actions.ts (1)

64-74: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the underlying error before returning a generic failure.

The catch block discards the actual error (network failure, timeout, schema mismatch), unlike the equivalent scheduling path in auth.ts which logs it. This makes production failures hard to diagnose.

♻️ Suggested fix
 export const triggerAccountPermissionSync = async (accountId: string) => sew(() =>
     withAuth(({ role }) =>
         withMinimumOrgRole(role, OrgRole.MEMBER, async () => {
             try {
                 return await requestAccountPermissionSync(accountId);
-            } catch {
+            } catch (error) {
+                logger.error(`Failed to trigger account permission sync for account ${accountId}: ${error instanceof Error ? error.message : String(error)}`);
                 return unexpectedError('Failed to trigger account permission sync');
             }
         })
     )
 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/features/workerApi/actions.ts` around lines 64 - 74, Update
the catch block in triggerAccountPermissionSync to capture the underlying error
and log it before returning the existing generic unexpectedError response,
matching the diagnostic behavior used by the equivalent scheduling path in
auth.ts.
packages/web/src/auth.ts (1)

228-240: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider not awaiting the recovery sync inside events.signIn.

requestAccountPermissionSync is awaited directly in the sign-in event, which Auth.js awaits before completing the response — this can add up to the client's 5s timeout to sign-in latency for accounts with an existing permission issue if the worker is slow. Since this is a fire-and-forget scheduling call (errors are already caught/logged and don't affect sign-in outcome), consider not awaiting it so sign-in isn't delayed by worker availability.

♻️ Suggested fix
                 if (
                     updatedAccount.permissionSyncIssue !== null &&
                     env.PERMISSION_SYNC_ENABLED === 'true'
                 ) {
-                    try {
-                        if (await hasEntitlement('permission-syncing')) {
-                            await requestAccountPermissionSync(updatedAccount.id);
-                        }
-                    } catch (error) {
-                        const message = error instanceof Error ? error.message : String(error);
-                        logger.error(`Failed to schedule permission sync after reauthentication for account ${updatedAccount.id}: ${message}`);
-                    }
+                    hasEntitlement('permission-syncing')
+                        .then((entitled) => entitled ? requestAccountPermissionSync(updatedAccount.id) : undefined)
+                        .catch((error) => {
+                            const message = error instanceof Error ? error.message : String(error);
+                            logger.error(`Failed to schedule permission sync after reauthentication for account ${updatedAccount.id}: ${message}`);
+                        });
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/auth.ts` around lines 228 - 240, Update the recovery sync
block in the events.signIn flow to invoke requestAccountPermissionSync without
awaiting its completion, while retaining the existing error capture and
logger.error handling so failures remain logged without delaying sign-in.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/src/ee/accountPermissionSyncer.ts`:
- Around line 259-273: Update the fail-closed cleanup warning in the account
permission sync flow to remove account.user.email from the log message,
retaining the account.id and existing cleanup details and error message. Do not
alter the transaction or cleanup behavior.

---

Nitpick comments:
In `@packages/web/src/auth.ts`:
- Around line 228-240: Update the recovery sync block in the events.signIn flow
to invoke requestAccountPermissionSync without awaiting its completion, while
retaining the existing error capture and logger.error handling so failures
remain logged without delaying sign-in.

In `@packages/web/src/features/workerApi/actions.ts`:
- Around line 64-74: Update the catch block in triggerAccountPermissionSync to
capture the underlying error and log it before returning the existing generic
unexpectedError response, matching the diagnostic behavior used by the
equivalent scheduling path in auth.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 99b97fda-b5b7-47a9-a62e-c231dc3cc2ad

📥 Commits

Reviewing files that changed from the base of the PR and between a985fac and 2c03c15.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • packages/backend/src/ee/accountPermissionSyncer.test.ts
  • packages/backend/src/ee/accountPermissionSyncer.ts
  • packages/db/prisma/migrations/20260722144824_add_account_permission_sync_issue/migration.sql
  • packages/db/prisma/schema.prisma
  • packages/web/src/app/(app)/components/banners/bannerResolver.test.ts
  • packages/web/src/app/(app)/components/banners/bannerResolver.tsx
  • packages/web/src/app/(app)/components/banners/permissionSyncBanner.test.tsx
  • packages/web/src/app/(app)/components/banners/permissionSyncBanner.tsx
  • packages/web/src/app/(app)/layout.tsx
  • packages/web/src/app/api/(server)/ee/permissionSyncStatus/api.test.ts
  • packages/web/src/app/api/(server)/ee/permissionSyncStatus/api.ts
  • packages/web/src/auth.ts
  • packages/web/src/ee/features/sso/actions.ts
  • packages/web/src/ee/features/sso/components/linkedAccountProviderCard.test.tsx
  • packages/web/src/ee/features/sso/components/linkedAccountProviderCard.tsx
  • packages/web/src/features/workerApi/actions.ts
  • packages/web/src/features/workerApi/client.server.test.ts
  • packages/web/src/features/workerApi/client.server.ts

Comment thread packages/backend/src/ee/accountPermissionSyncer.ts
@brendan-kellam
brendan-kellam force-pushed the brendan/classify-permission-sync-errors branch from a985fac to 3cc17b8 Compare July 23, 2026 18:02
@brendan-kellam
brendan-kellam force-pushed the brendan/permission-sync-action-required-ux branch from 2c03c15 to 5873631 Compare July 23, 2026 18:02
@brendan-kellam
brendan-kellam force-pushed the brendan/classify-permission-sync-errors branch from 3cc17b8 to 515ad7e Compare July 23, 2026 20:01
@brendan-kellam
brendan-kellam force-pushed the brendan/permission-sync-action-required-ux branch from 5873631 to a962821 Compare July 23, 2026 20:01

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a962821. Configure here.

@brendan-kellam
brendan-kellam merged commit afe1994 into main Jul 23, 2026
16 checks passed
@brendan-kellam
brendan-kellam deleted the brendan/permission-sync-action-required-ux branch July 23, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants