fix(oauth): sign-in failed on first launch (oauth flow) (#1689)#2247
Conversation
…nsai#1689) First-launch Google/GitHub sign-in often failed because the auth deep link ran before BootCheckGate finished and the embedded core answered RPC. Gate OAuth on core mode + core.ping + bootstrap completion, start processing earlier from the Welcome buttons, and surface actionable errors instead of a generic failure message. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an OAuth readiness gate that ensures core mode is set, optionally starts the local core (Tauri), verifies RPC reachability, exposes failure reasons and messages, provides a short preflight for browser OAuth launch, and integrates these checks into the OAuth button and deep-link flows with tests and re-exports. ChangesOAuth readiness gating and integration
Sequence Diagram(s)sequenceDiagram
participant Button as OAuthProviderButton
participant DeepLink as deepLinkAuthState
participant Preflight as prepareOAuthLoginLaunch
participant Readiness as waitForOAuthAuthReadiness
participant Core as Local Core Process
Button->>DeepLink: beginDeepLinkAuthProcessing()
Button->>Preflight: prepareOAuthLoginLaunch()
activate Preflight
Preflight->>Core: request start (Tauri/local)
Preflight->>Readiness: short readiness check
deactivate Preflight
Readiness->>Readiness: poll core mode & RPC ping
Button->>Button: Build & launch OAuth URL
alt OAuth success (deep-link callback)
DeepLink->>DeepLink: storeSession(...)
else Error
Button->>DeepLink: completeDeepLinkAuthProcessing()
Button->>Button: Set error state
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts (1)
25-126: ⚡ Quick winAdd test coverage for the
bootstrap_timeoutfailure scenario.The test suite covers
core_mode_unsetandcore_unreachable, but is missing a test for thebootstrap_timeoutcase. This distinct failure mode occurs when the core RPC ping succeeds butisBootstrappingremainstrue(with nosessionToken) until timeout. Since it has its own user-facing message and represents a real failure path, it should be tested to prevent regressions.✅ Suggested test case
Add this test after line 117:
it('returns bootstrap_timeout when ping succeeds but bootstrap never finishes', async () => { vi.mocked(getCoreStateSnapshot).mockReturnValue({ isBootstrapping: true, isReady: false, snapshot: { sessionToken: null, auth: { isAuthenticated: false, userId: null, user: null, profileId: null }, currentUser: null, onboardingCompleted: false, chatOnboardingCompleted: false, analyticsEnabled: false, meetAutoOrchestratorHandoff: false, localState: { encryptionKey: null, onboardingTasks: null }, runtime: { screenIntelligence: null, localAi: null, autocomplete: null, service: null }, }, teams: [], teamMembersById: {}, teamInvitesById: {}, }); const result = await waitForOAuthAuthReadiness(600); expect(result).toEqual({ ready: false, reason: 'bootstrap_timeout' }); expect(oauthAuthReadinessUserMessage('bootstrap_timeout')).toMatch(/wait a few seconds/i); });🤖 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 `@app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts` around lines 25 - 126, Add a new test for waitForOAuthAuthReadiness that simulates a successful core ping but a forever-bootstrapping core: mock testCoreRpcConnection to resolve { ok: true } and mock getCoreStateSnapshot to always return isBootstrapping: true, isReady: false with snapshot.sessionToken: null; call waitForOAuthAuthReadiness with a short timeout and assert the result equals { ready: false, reason: 'bootstrap_timeout' } and that oauthAuthReadinessUserMessage('bootstrap_timeout') matches the expected user-facing text.
🤖 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.
Nitpick comments:
In `@app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts`:
- Around line 25-126: Add a new test for waitForOAuthAuthReadiness that
simulates a successful core ping but a forever-bootstrapping core: mock
testCoreRpcConnection to resolve { ok: true } and mock getCoreStateSnapshot to
always return isBootstrapping: true, isReady: false with snapshot.sessionToken:
null; call waitForOAuthAuthReadiness with a short timeout and assert the result
equals { ready: false, reason: 'bootstrap_timeout' } and that
oauthAuthReadinessUserMessage('bootstrap_timeout') matches the expected
user-facing text.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2d067b72-3b8b-4ae4-8fc0-6cf1e86ab982
📒 Files selected for processing (7)
app/src/components/oauth/OAuthProviderButton.tsxapp/src/components/oauth/__tests__/OAuthProviderButton.test.tsxapp/src/components/oauth/__tests__/oauthAuthReadiness.test.tsapp/src/components/oauth/oauthAuthReadiness.tsapp/src/utils/__tests__/desktopDeepLinkListener.test.tsapp/src/utils/desktopDeepLinkListener.tsapp/src/utils/oauthAppVersionGate.ts
|
I could not push the CI fix directly to this PR head branch ( I opened #2267 as a replacement PR with this PR's first-launch OAuth readiness fix plus the CI stabilization commits:
Local validation on #2267 passed: full Vitest, coverage, typecheck, focused ESLint, format checks, and |
…s gate The readiness gate introduced in 6019ea2 also fires on the `key=auth` deep-link path that E2E (and dev tooling) use to inject a pre-signed session JWT. That path doesn't call `consume_login_token`, so the long bootstrap poll inside the gate just blocks `window.__simulateDeepLink` past WebDriver's script timeout and wedges the smoke spec. Skip the gate for `key=auth` — `storeSession` is the only RPC it makes and the core is already up in every code path that uses this URL. Legacy `app/test/OAuth{Twitter,GitHub,Discord,LoginSection}.test.tsx` also didn't mock the new `prepareOAuthLoginLaunch` preflight, so the gate's 8 s wait stalled every Tauri-mode click and made `openUrl` assertions time out — fixes the Vitest CI job and the diff-coverage gate that piggybacks on it.
Conflict resolution highlights: * OAuthProviderButton.tsx — keep both pre-flights. Main added a backend /health check (issue tinyhumansai#1985) that fails fast on Cloudflare 504 / DNS outages before opening the system browser, and replaced the local `getBackendUrl()` call with `preflight.backendUrl`. This branch added `prepareOAuthLoginLaunch()` (issue tinyhumansai#1689), the Tauri-only runtime readiness preflight that waits for the embedded core. Wire both: run `checkBackendHealthy` first (cheap; aborts on outage), then run `prepareOAuthLoginLaunch` only when `isTauri()`. Drop the unused `beginDeepLinkAuthProcessing` / `completeDeepLinkAuthProcessing` imports that main removed. * OAuthProviderButton.test.tsx — keep the `prepareOAuthLoginLaunch` expectation; drop the now-stale `beginDeepLinkAuthProcessing` / `getBackendUrl` expectations. * app/test/OAuth{Twitter,GitHub,Discord,LoginSection}.test.tsx — merge the two hoisted-mock blocks so the legacy suite mocks BOTH the new `checkBackendHealthy` pre-flight (main) and `prepareOAuthLoginLaunch` (this branch).
`dismissBootCheckGate` was matching against legacy h2 text
("Choose core mode" / "Connect to your core") that no longer renders —
the i18n strings are now "Select a Runtime" / "Connect to Your Runtime"
(`app/src/lib/i18n/en.ts:1071-1072`). The helper therefore never detected
the picker, never clicked Continue, and never persisted
`openhuman_core_mode` to localStorage.
Pre-tinyhumansai#2247 that was harmless: the deep-link handler called
`consumeLoginToken` immediately, so the picker hovering above the WebView
didn't block the mock-backend assertions. After tinyhumansai#2247 the new OAuth
auth-readiness gate hard-depends on `openhuman_core_mode` being set, so
all three deep-link-consume mega-flow scenarios time out at 30s waiting
for the gate to lift.
Update the heading regex (centralised in a constant) to the current
strings and stop returning early on the first poll when the picker isn't
visible yet — BootCheckGate can land its first paint a beat after `#root`
gains children, and the 5s deadline already bounds the wait.
No production code touched; affects E2E test harness only.
Addresses CodeRabbit nitpick on PR tinyhumansai#2247: the bootstrap_timeout case was the only failure mode in waitForOAuthAuthReadiness lacking direct test coverage. The existing suite covers core_mode_unset and core_unreachable but not the path where core.ping succeeds while isBootstrapping never clears.
## Summary - Restores the first-launch desktop OAuth flow from #2247 while unblocking the failing CI surfaces. - Keeps the runtime readiness gate before launching OAuth in Tauri so first-launch sign-in waits for core/auth readiness. - Stabilizes legacy OAuth provider tests by mocking the new readiness preflight in Tauri-mode unit tests. - Makes the E2E-only `window.__simulateDeepLink` helper fire-and-forget, matching production `onOpenUrl` behavior so WebDriver does not block on auth readiness. - Keeps auth readiness focused on core-mode selection plus core RPC reachability, so first-login callbacks are not blocked by CoreStateProvider bootstrap. - Adds regression coverage for the non-blocking deep-link helper path. ## Problem - #2247 fixes first-launch OAuth readiness, but its PR branch is not writable from this maintainer account, and CI is currently red. - Frontend unit and coverage jobs fail because legacy provider tests exercise Tauri mode without mocking the new readiness preflight. - Linux E2E times out because `browser.execute(async () => await window.__simulateDeepLink(...))` waits on the full auth callback path, including readiness waits. - The PR checklist also had the diff coverage item unchecked. ## Solution - Mock `prepareOAuthLoginLaunch()` in the legacy Google/GitHub/Discord/Twitter OAuth tests, preserving their existing URL/openUrl assertions while isolating the readiness preflight. - Change `__simulateDeepLink` to schedule `handleDeepLinkUrls()` without awaiting it, which matches the real desktop deep-link listener's fire-and-forget handler. - Treat CoreStateProvider bootstrap as observational logging rather than a hard auth-readiness gate; core mode plus a successful core RPC ping are the required login preconditions. - Dismiss the runtime picker before E2E auth deep-link simulation so raw mega-flow auth callbacks also commit a core mode before readiness checks. - Treat the E2E default local core mode as an auth-readiness core mode when no explicit localStorage marker exists. - Add a desktop deep-link listener unit test proving the E2E helper resolves immediately while the auth readiness path continues asynchronously. - This PR supersedes #2247 because the original contributor fork rejected direct pushes from `YOMXXX` despite `maintainerCanModify=true`. ## Submission Checklist > If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items. - [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) - [x] **Diff coverage ≥ 80%** — changed lines (Vitest + cargo-llvm-cov merged via `diff-cover`) meet the gate enforced by [`.github/workflows/coverage.yml`](../.github/workflows/coverage.yml). Run `pnpm test:coverage` and `pnpm test:rust` locally; PRs below 80% on changed lines will not merge. - [x] Coverage matrix updated — N/A: behavior/test stabilization for existing OAuth sign-in flow; no feature row added/removed/renamed. - [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no matrix feature IDs changed. - [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy)) - [x] Manual smoke checklist updated if this touches release-cut surfaces ([`docs/RELEASE-MANUAL-SMOKE.md`](../docs/RELEASE-MANUAL-SMOKE.md)) — N/A: no release smoke procedure changed. - [x] Linked issue closed via `Closes #NNN` in the `## Related` section ## Impact - Desktop OAuth first-launch sign-in is more reliable because OAuth launch still waits for readiness before opening the external browser. - E2E-only deep-link simulation no longer blocks WebDriver script execution on long auth readiness waits. - No new runtime dependency, migration, storage, or security surface. - Web OAuth behavior is unchanged. ## Related - Closes: Closes #1689 - Follow-up PR(s)/TODOs: Supersedes #2247 because the original head branch is not writable from this fork. --- ## AI Authored PR Metadata (required for Codex/Linear PRs) > Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`. ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `fix/2247-oauth-ci-readiness` - Commit SHA: `3367058b145a37566dcd377198b2881a977ce3cd` ### Validation Run - [x] `pnpm --filter openhuman-app format:check` (via pre-push hook) - [x] `pnpm typecheck` - [x] Focused tests: - `pnpm debug unit test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx` - `pnpm debug unit src/components/oauth/__tests__/OAuthProviderButton.test.tsx src/components/oauth/__tests__/oauthAuthReadiness.test.ts` - `pnpm debug unit src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts src/components/oauth/__tests__/OAuthProviderButton.test.tsx test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/configPersistence.test.ts src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit` - `pnpm test:coverage` - `pnpm --dir app exec eslint src/utils/desktopDeepLinkListener.ts src/utils/__tests__/desktopDeepLinkListener.test.ts test/OAuthTwitter.test.tsx test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx --ext .ts,.tsx --max-warnings=0` - [x] Rust fmt/check (if changed): `cargo fmt --manifest-path ../Cargo.toml --all --check`, `cargo fmt --manifest-path src-tauri/Cargo.toml --all --check`, and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook - [x] Tauri fmt/check (if changed): `cargo fmt --manifest-path app/src-tauri/Cargo.toml --all --check` and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A. Local macOS/Tahoe `rust:check` requires the documented `GGML_NATIVE=OFF` workaround for `whisper-rs-sys`/`-mcpu=native`; validation passed with that env var. ### Behavior Changes - Intended behavior change: first-launch Tauri OAuth launch waits for runtime readiness, and E2E deep-link simulation no longer blocks the WebDriver script on asynchronous auth completion. - User-visible effect: desktop first-launch sign-in should complete reliably instead of racing core/auth initialization. ### Parity Contract - Legacy behavior preserved: web OAuth redirects and Tauri `openUrl()` URL construction remain unchanged. - Guard/fallback/dispatch parity checks: existing provider tests still cover Google/GitHub/Discord/Twitter web and Tauri branches; deep-link listener test covers readiness failure and async helper behavior. ### Duplicate / Superseded PR Handling - Duplicate PR(s): #2247 - Canonical PR: this PR - Resolution (closed/superseded/updated): #2247 could not be updated directly because `git push git@github.com:CodeGhost21/openhuman.git HEAD:cursor/a02-1689-signin-failed-first-time` was rejected with `Permission denied to YOMXXX`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * OAuth sign-in adds a readiness gate that checks local runtime availability, surfaces clear user-facing messages when sign-in can’t proceed, and runs a short preflight on supported desktop builds before launching provider flows. * Deep-link sign-ins coordinate lifecycle steps for more reliable handling across environments; E2E helpers now treat auth deep links to bypass boot checks when appropriate. * Config lookup supports an E2E default core-mode fallback. * **Tests** * Expanded tests for OAuth readiness, deep-link lifecycle, launch preparation, desktop flows, and config fallbacks. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2267?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Ghost Scripter <ghostscripter@zerolend.xyz> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: 李冠辰 <liguanchen@xiaomi.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
# Conflicts: # app/src/components/oauth/OAuthProviderButton.tsx # app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx # app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts # app/src/components/oauth/oauthAuthReadiness.ts # app/src/utils/__tests__/desktopDeepLinkListener.test.ts # app/src/utils/desktopDeepLinkListener.ts # app/test/OAuthDiscord.test.tsx # app/test/OAuthGitHub.test.tsx # app/test/OAuthLoginSection.test.tsx # app/test/OAuthTwitter.test.tsx
## Summary - Restores the first-launch desktop OAuth flow from tinyhumansai#2247 while unblocking the failing CI surfaces. - Keeps the runtime readiness gate before launching OAuth in Tauri so first-launch sign-in waits for core/auth readiness. - Stabilizes legacy OAuth provider tests by mocking the new readiness preflight in Tauri-mode unit tests. - Makes the E2E-only `window.__simulateDeepLink` helper fire-and-forget, matching production `onOpenUrl` behavior so WebDriver does not block on auth readiness. - Keeps auth readiness focused on core-mode selection plus core RPC reachability, so first-login callbacks are not blocked by CoreStateProvider bootstrap. - Adds regression coverage for the non-blocking deep-link helper path. ## Problem - tinyhumansai#2247 fixes first-launch OAuth readiness, but its PR branch is not writable from this maintainer account, and CI is currently red. - Frontend unit and coverage jobs fail because legacy provider tests exercise Tauri mode without mocking the new readiness preflight. - Linux E2E times out because `browser.execute(async () => await window.__simulateDeepLink(...))` waits on the full auth callback path, including readiness waits. - The PR checklist also had the diff coverage item unchecked. ## Solution - Mock `prepareOAuthLoginLaunch()` in the legacy Google/GitHub/Discord/Twitter OAuth tests, preserving their existing URL/openUrl assertions while isolating the readiness preflight. - Change `__simulateDeepLink` to schedule `handleDeepLinkUrls()` without awaiting it, which matches the real desktop deep-link listener's fire-and-forget handler. - Treat CoreStateProvider bootstrap as observational logging rather than a hard auth-readiness gate; core mode plus a successful core RPC ping are the required login preconditions. - Dismiss the runtime picker before E2E auth deep-link simulation so raw mega-flow auth callbacks also commit a core mode before readiness checks. - Treat the E2E default local core mode as an auth-readiness core mode when no explicit localStorage marker exists. - Add a desktop deep-link listener unit test proving the E2E helper resolves immediately while the auth readiness path continues asynchronously. - This PR supersedes tinyhumansai#2247 because the original contributor fork rejected direct pushes from `YOMXXX` despite `maintainerCanModify=true`. ## Submission Checklist > If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items. - [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) - [x] **Diff coverage ≥ 80%** — changed lines (Vitest + cargo-llvm-cov merged via `diff-cover`) meet the gate enforced by [`.github/workflows/coverage.yml`](../.github/workflows/coverage.yml). Run `pnpm test:coverage` and `pnpm test:rust` locally; PRs below 80% on changed lines will not merge. - [x] Coverage matrix updated — N/A: behavior/test stabilization for existing OAuth sign-in flow; no feature row added/removed/renamed. - [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no matrix feature IDs changed. - [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy)) - [x] Manual smoke checklist updated if this touches release-cut surfaces ([`docs/RELEASE-MANUAL-SMOKE.md`](../docs/RELEASE-MANUAL-SMOKE.md)) — N/A: no release smoke procedure changed. - [x] Linked issue closed via `Closes #NNN` in the `## Related` section ## Impact - Desktop OAuth first-launch sign-in is more reliable because OAuth launch still waits for readiness before opening the external browser. - E2E-only deep-link simulation no longer blocks WebDriver script execution on long auth readiness waits. - No new runtime dependency, migration, storage, or security surface. - Web OAuth behavior is unchanged. ## Related - Closes: Closes tinyhumansai#1689 - Follow-up PR(s)/TODOs: Supersedes tinyhumansai#2247 because the original head branch is not writable from this fork. --- ## AI Authored PR Metadata (required for Codex/Linear PRs) > Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`. ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `fix/2247-oauth-ci-readiness` - Commit SHA: `3367058b145a37566dcd377198b2881a977ce3cd` ### Validation Run - [x] `pnpm --filter openhuman-app format:check` (via pre-push hook) - [x] `pnpm typecheck` - [x] Focused tests: - `pnpm debug unit test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx` - `pnpm debug unit src/components/oauth/__tests__/OAuthProviderButton.test.tsx src/components/oauth/__tests__/oauthAuthReadiness.test.ts` - `pnpm debug unit src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts src/components/oauth/__tests__/OAuthProviderButton.test.tsx test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/configPersistence.test.ts src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit` - `pnpm test:coverage` - `pnpm --dir app exec eslint src/utils/desktopDeepLinkListener.ts src/utils/__tests__/desktopDeepLinkListener.test.ts test/OAuthTwitter.test.tsx test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx --ext .ts,.tsx --max-warnings=0` - [x] Rust fmt/check (if changed): `cargo fmt --manifest-path ../Cargo.toml --all --check`, `cargo fmt --manifest-path src-tauri/Cargo.toml --all --check`, and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook - [x] Tauri fmt/check (if changed): `cargo fmt --manifest-path app/src-tauri/Cargo.toml --all --check` and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A. Local macOS/Tahoe `rust:check` requires the documented `GGML_NATIVE=OFF` workaround for `whisper-rs-sys`/`-mcpu=native`; validation passed with that env var. ### Behavior Changes - Intended behavior change: first-launch Tauri OAuth launch waits for runtime readiness, and E2E deep-link simulation no longer blocks the WebDriver script on asynchronous auth completion. - User-visible effect: desktop first-launch sign-in should complete reliably instead of racing core/auth initialization. ### Parity Contract - Legacy behavior preserved: web OAuth redirects and Tauri `openUrl()` URL construction remain unchanged. - Guard/fallback/dispatch parity checks: existing provider tests still cover Google/GitHub/Discord/Twitter web and Tauri branches; deep-link listener test covers readiness failure and async helper behavior. ### Duplicate / Superseded PR Handling - Duplicate PR(s): tinyhumansai#2247 - Canonical PR: this PR - Resolution (closed/superseded/updated): tinyhumansai#2247 could not be updated directly because `git push git@github.com:CodeGhost21/openhuman.git HEAD:cursor/a02-1689-signin-failed-first-time` was rejected with `Permission denied to YOMXXX`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * OAuth sign-in adds a readiness gate that checks local runtime availability, surfaces clear user-facing messages when sign-in can’t proceed, and runs a short preflight on supported desktop builds before launching provider flows. * Deep-link sign-ins coordinate lifecycle steps for more reliable handling across environments; E2E helpers now treat auth deep links to bypass boot checks when appropriate. * Config lookup supports an E2E default core-mode fallback. * **Tests** * Expanded tests for OAuth readiness, deep-link lifecycle, launch preparation, desktop flows, and config fallbacks. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2267?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Ghost Scripter <ghostscripter@zerolend.xyz> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: 李冠辰 <liguanchen@xiaomi.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary - Restores the first-launch desktop OAuth flow from tinyhumansai#2247 while unblocking the failing CI surfaces. - Keeps the runtime readiness gate before launching OAuth in Tauri so first-launch sign-in waits for core/auth readiness. - Stabilizes legacy OAuth provider tests by mocking the new readiness preflight in Tauri-mode unit tests. - Makes the E2E-only `window.__simulateDeepLink` helper fire-and-forget, matching production `onOpenUrl` behavior so WebDriver does not block on auth readiness. - Keeps auth readiness focused on core-mode selection plus core RPC reachability, so first-login callbacks are not blocked by CoreStateProvider bootstrap. - Adds regression coverage for the non-blocking deep-link helper path. ## Problem - tinyhumansai#2247 fixes first-launch OAuth readiness, but its PR branch is not writable from this maintainer account, and CI is currently red. - Frontend unit and coverage jobs fail because legacy provider tests exercise Tauri mode without mocking the new readiness preflight. - Linux E2E times out because `browser.execute(async () => await window.__simulateDeepLink(...))` waits on the full auth callback path, including readiness waits. - The PR checklist also had the diff coverage item unchecked. ## Solution - Mock `prepareOAuthLoginLaunch()` in the legacy Google/GitHub/Discord/Twitter OAuth tests, preserving their existing URL/openUrl assertions while isolating the readiness preflight. - Change `__simulateDeepLink` to schedule `handleDeepLinkUrls()` without awaiting it, which matches the real desktop deep-link listener's fire-and-forget handler. - Treat CoreStateProvider bootstrap as observational logging rather than a hard auth-readiness gate; core mode plus a successful core RPC ping are the required login preconditions. - Dismiss the runtime picker before E2E auth deep-link simulation so raw mega-flow auth callbacks also commit a core mode before readiness checks. - Treat the E2E default local core mode as an auth-readiness core mode when no explicit localStorage marker exists. - Add a desktop deep-link listener unit test proving the E2E helper resolves immediately while the auth readiness path continues asynchronously. - This PR supersedes tinyhumansai#2247 because the original contributor fork rejected direct pushes from `YOMXXX` despite `maintainerCanModify=true`. ## Submission Checklist > If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items. - [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) - [x] **Diff coverage ≥ 80%** — changed lines (Vitest + cargo-llvm-cov merged via `diff-cover`) meet the gate enforced by [`.github/workflows/coverage.yml`](../.github/workflows/coverage.yml). Run `pnpm test:coverage` and `pnpm test:rust` locally; PRs below 80% on changed lines will not merge. - [x] Coverage matrix updated — N/A: behavior/test stabilization for existing OAuth sign-in flow; no feature row added/removed/renamed. - [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no matrix feature IDs changed. - [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy)) - [x] Manual smoke checklist updated if this touches release-cut surfaces ([`docs/RELEASE-MANUAL-SMOKE.md`](../docs/RELEASE-MANUAL-SMOKE.md)) — N/A: no release smoke procedure changed. - [x] Linked issue closed via `Closes #NNN` in the `## Related` section ## Impact - Desktop OAuth first-launch sign-in is more reliable because OAuth launch still waits for readiness before opening the external browser. - E2E-only deep-link simulation no longer blocks WebDriver script execution on long auth readiness waits. - No new runtime dependency, migration, storage, or security surface. - Web OAuth behavior is unchanged. ## Related - Closes: Closes tinyhumansai#1689 - Follow-up PR(s)/TODOs: Supersedes tinyhumansai#2247 because the original head branch is not writable from this fork. --- ## AI Authored PR Metadata (required for Codex/Linear PRs) > Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`. ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `fix/2247-oauth-ci-readiness` - Commit SHA: `3367058b145a37566dcd377198b2881a977ce3cd` ### Validation Run - [x] `pnpm --filter openhuman-app format:check` (via pre-push hook) - [x] `pnpm typecheck` - [x] Focused tests: - `pnpm debug unit test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx` - `pnpm debug unit src/components/oauth/__tests__/OAuthProviderButton.test.tsx src/components/oauth/__tests__/oauthAuthReadiness.test.ts` - `pnpm debug unit src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts src/components/oauth/__tests__/OAuthProviderButton.test.tsx test/OAuthTwitter.test.tsx` - `pnpm debug unit src/utils/__tests__/configPersistence.test.ts src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/utils/__tests__/desktopDeepLinkListener.test.ts` - `pnpm debug unit` - `pnpm test:coverage` - `pnpm --dir app exec eslint src/utils/desktopDeepLinkListener.ts src/utils/__tests__/desktopDeepLinkListener.test.ts test/OAuthTwitter.test.tsx test/OAuthGitHub.test.tsx test/OAuthDiscord.test.tsx test/OAuthLoginSection.test.tsx --ext .ts,.tsx --max-warnings=0` - [x] Rust fmt/check (if changed): `cargo fmt --manifest-path ../Cargo.toml --all --check`, `cargo fmt --manifest-path src-tauri/Cargo.toml --all --check`, and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook - [x] Tauri fmt/check (if changed): `cargo fmt --manifest-path app/src-tauri/Cargo.toml --all --check` and `GGML_NATIVE=OFF pnpm rust:check` via pre-push hook ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A. Local macOS/Tahoe `rust:check` requires the documented `GGML_NATIVE=OFF` workaround for `whisper-rs-sys`/`-mcpu=native`; validation passed with that env var. ### Behavior Changes - Intended behavior change: first-launch Tauri OAuth launch waits for runtime readiness, and E2E deep-link simulation no longer blocks the WebDriver script on asynchronous auth completion. - User-visible effect: desktop first-launch sign-in should complete reliably instead of racing core/auth initialization. ### Parity Contract - Legacy behavior preserved: web OAuth redirects and Tauri `openUrl()` URL construction remain unchanged. - Guard/fallback/dispatch parity checks: existing provider tests still cover Google/GitHub/Discord/Twitter web and Tauri branches; deep-link listener test covers readiness failure and async helper behavior. ### Duplicate / Superseded PR Handling - Duplicate PR(s): tinyhumansai#2247 - Canonical PR: this PR - Resolution (closed/superseded/updated): tinyhumansai#2247 could not be updated directly because `git push git@github.com:CodeGhost21/openhuman.git HEAD:cursor/a02-1689-signin-failed-first-time` was rejected with `Permission denied to YOMXXX`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * OAuth sign-in adds a readiness gate that checks local runtime availability, surfaces clear user-facing messages when sign-in can’t proceed, and runs a short preflight on supported desktop builds before launching provider flows. * Deep-link sign-ins coordinate lifecycle steps for more reliable handling across environments; E2E helpers now treat auth deep links to bypass boot checks when appropriate. * Config lookup supports an E2E default core-mode fallback. * **Tests** * Expanded tests for OAuth readiness, deep-link lifecycle, launch preparation, desktop flows, and config fallbacks. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2267?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Ghost Scripter <ghostscripter@zerolend.xyz> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: 李冠辰 <liguanchen@xiaomi.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Summary
core.ping, and CoreStateProvider bootstrap before consuming login tokens.Problem
Fresh Windows/macOS installs reported Google/GitHub sign-in failing on the Welcome screen with a generic error (#1689). The auth deep-link handler only waited ~1.5s for bootstrap while BootCheckGate was still up or the embedded core had not answered RPC, then called
consume_login_token/auth_store_sessionagainst an unreachable core.Solution
Introduce
waitForOAuthAuthReadiness()(owned underapp/src/components/oauth/) and wire it fromdesktopDeepLinkListenerandOAuthProviderButton. The gate polls untilopenhuman_core_modeis set, invokesstart_core_processfor local mode, confirmscore.ping, and waits forisBootstrappingto clear. Failures return user-visible guidance instead of proceeding into a doomed RPC path.Submission Checklist
pnpm test:coverage+pnpm test:rustdeferred to CI (no Rust changes in this PR)N/A: behaviour-only change on existing Welcome OAuth flow; no new matrix row## Relateddocs/RELEASE-MANUAL-SMOKE.md) —N/A: automated regression onlyCloses #NNNin the## RelatedsectionImpact
Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
cursor/a02-1689-signin-failed-first-timeValidation Run
pnpm --filter openhuman-app format:checkpnpm typecheckpnpm debug unit src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/components/oauth/__tests__/OAuthProviderButton.test.tsx src/utils/__tests__/desktopDeepLinkListener.test.ts(18 passed)Validation Blocked
command:pnpm test:coverageandpnpm test:rust(full merge-gate suite)error:Not run locally in this session due to runtime cost; CIcoverage.yml/test.ymlwill execute on the PR.impact:Diff-coverage gate validated in CI; changed lines covered by new/updated unit tests listed above.Behavior Changes
Parity Contract
auth_store_sessionand routes to/home.openhuman://authandopenhuman://oauth/*routing unchanged; only readiness timing and error copy changed.Duplicate / Superseded PR Handling
Made with Cursor
Summary by CodeRabbit
New Features
Bug Fixes
Tests