From 856813ba3a083f23694b8554104b3e50abcb1363 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Sun, 6 Sep 2026 17:43:48 -0500 Subject: [PATCH] fix(connections): distinguish local setup from provider handoff (#12947) ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Connections give those agents access to external services. > - Connection setup first asks who may use the credential. > - Some providers need a local method selection before OAuth starts. > - The Access button said it would open GitHub even when it opened another local step. > - This pull request names the actual next action and shows the external arrow only for a provider handoff. > - Users can distinguish local setup from leaving Paperclip. ## Linked Issues or Issue Description Refs: #12943 The browser audit found a second, separate clarity problem. GitHub showed "Continue to GitHub" twice: once to open its local method selection and once to start OAuth. The first label promised the wrong action. This change fixes that label without adding or removing a setup step. ## What Changed - Use "Continue" when Access opens another local OAuth setup step. - Keep "Continue to " and the external arrow when Access starts OAuth directly. - Test GitHub defaults, pre-enrollment setup, direct Notion OAuth, and dialog behavior. - Document the two GitHub button actions. - Preserve the external-handoff arrow in the direct-OAuth Storybook fixture. ## Verification - Targeted connection tests: 114 passed. - `pnpm --filter @paperclipai/ui typecheck`: passed. - `pnpm --filter @paperclipai/ui build`: passed. - `node scripts/check-token-gates.mjs`: passed. - `git diff --check`: passed. - After the Storybook review fix: 100 connection-flow tests, UI typecheck, token gates, and Storybook build passed; visually confirmed the Gmail handoff arrow and local GitHub step labels. - Actual test-drive browser: confirmed default personal identity and Any agent; clicked Continue to the local method screen; switched PAT and OAuth; returned to Access with selections intact; cancelled without a duplicate connection. - Live OAuth, local MCP identity, and a real staging sandbox task passed during the audit. This patch does not change those paths. - Full local suite has known unrelated macOS path/runtime test failures from the audit. All CI passed on head `936ce09f4e1faaf09f9fb778c2cd4c65c2a41972`, including typecheck, build, all test shards, browser tests, and canary dry run. Greptile: 5/5; no unresolved comments. ## Risks - Low risk. This changes button text and an icon cue only. There is no new schema, permission, consent, or credential behavior. - Providers with multiple methods now say Continue before their method screen. Direct OAuth providers keep their existing wording. ## Model Used - OpenAI Codex assisted with code, tests, terminal checks, and actual browser verification. The runtime does not expose the exact model ID or context-window size, so those details are unavailable. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub references) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (targeted tests; full-suite caveats above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- doc/connections/GITHUB.md | 4 ++ .../connections/ConnectionSetupFlow.tsx | 18 ++++---- ui/src/pages/apps/AppsConnect.test.tsx | 41 +++++++++++++++++-- .../permitted-vs-installed.stories.tsx | 1 + 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/doc/connections/GITHUB.md b/doc/connections/GITHUB.md index f79febf07f..eb894ce016 100644 --- a/doc/connections/GITHUB.md +++ b/doc/connections/GITHUB.md @@ -7,6 +7,10 @@ only in its existing encrypted secret system. ## Self-hosted setup +The Access step uses **Continue** to open the local setup screen. +**Continue to GitHub** on that screen starts the provider handoff. The first +button does not imply that the browser is leaving Paperclip yet. + A self-hosted instance needs one Paperclip Cloud approval before its first managed connection. After approval, setup returns to step 2 and continues to GitHub without another instance approval or a service restart. diff --git a/ui/src/features/connections/ConnectionSetupFlow.tsx b/ui/src/features/connections/ConnectionSetupFlow.tsx index 266dd58039..1886358b70 100644 --- a/ui/src/features/connections/ConnectionSetupFlow.tsx +++ b/ui/src/features/connections/ConnectionSetupFlow.tsx @@ -1868,14 +1868,12 @@ export function ConnectionSetupFlow({ : linkAuthMode === "oauth" ? "oauth" : "api_key"; - // The primary label names the next effect, so an OAuth handoff never arrives - // unannounced. - const accessMethodIsKnown = !entry - || Boolean(connectionMethodKey) - || credentialSourceMethods.length === 1; - const accessSubmitLabel = accessStepAuthKind === "oauth" && accessMethodIsKnown + // Name the actual next effect: multi-method apps and enrollment still have + // a local setup screen, even when OAuth is already the selected method. + const accessContinuesToProvider = Boolean(directOAuthEntry); + const accessSubmitLabel = accessContinuesToProvider ? `Continue to ${entry?.name ?? "sign-in"}` - : "Save and continue"; + : accessStepAuthKind === "oauth" ? "Continue" : "Save and continue"; const stepIndex = (zapierSource || entry) && step !== "gallery" && step !== "success" ? SELECTED_APP_STEP_INDEX[step] @@ -2132,6 +2130,7 @@ export function ConnectionSetupFlow({ capabilities={galleryQuery.data?.capabilities} githubIdentity={entry?.slug === "github"} submitLabel={accessSubmitLabel} + continuesToProvider={accessContinuesToProvider} identityLoading={Boolean(automaticOAuthEntry) && directOAuthLookupPending} preserveAgentAccess={Boolean(automaticOAuthEntry && (resumableOAuthConnection || reconnectConnection))} pending={connectMutation.isPending || oauthStartMutation.isPending} @@ -3581,6 +3580,7 @@ export function AccessStep({ capabilities, githubIdentity = false, submitLabel, + continuesToProvider = false, identityLoading = false, preserveAgentAccess = false, pending = false, @@ -3605,6 +3605,8 @@ export function AccessStep({ } | null; githubIdentity?: boolean; submitLabel: string; + /** Only show an external-handoff cue when this action starts provider OAuth. */ + continuesToProvider?: boolean; /** Wait for a durable OAuth connection before showing a reconnect identity. */ identityLoading?: boolean; /** Reconnect changes credentials only; existing install reach stays intact. */ @@ -3842,7 +3844,7 @@ export function AccessStep({ > {pending ?