Skip to content

fix(oauth): sign-in failed on first launch (oauth flow) (#1689)#2247

Merged
senamakel merged 7 commits into
tinyhumansai:mainfrom
CodeGhost21:cursor/a02-1689-signin-failed-first-time
May 20, 2026
Merged

fix(oauth): sign-in failed on first launch (oauth flow) (#1689)#2247
senamakel merged 7 commits into
tinyhumansai:mainfrom
CodeGhost21:cursor/a02-1689-signin-failed-first-time

Conversation

@CodeGhost21
Copy link
Copy Markdown
Contributor

@CodeGhost21 CodeGhost21 commented May 19, 2026

Summary

  • Add an OAuth auth-readiness gate that waits for BootCheckGate’s persisted core mode, a successful core.ping, and CoreStateProvider bootstrap before consuming login tokens.
  • Start deep-link auth processing when the user clicks Google/GitHub (not only when the callback arrives) so Welcome shows “Signing you in…” and focus handlers do not drop the loading state early.
  • Preflight local core startup before opening the system browser on desktop.
  • Replace the generic “Sign-in failed. Please try again.” with actionable messages when the runtime is not ready yet.

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_session against an unreachable core.

Solution

Introduce waitForOAuthAuthReadiness() (owned under app/src/components/oauth/) and wire it from desktopDeepLinkListener and OAuthProviderButton. The gate polls until openhuman_core_mode is set, invokes start_core_process for local mode, confirms core.ping, and waits for isBootstrapping to clear. Failures return user-visible guidance instead of proceeding into a doomed RPC path.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — focused Vitest coverage run on changed OAuth modules locally; full pnpm test:coverage + pnpm test:rust deferred to CI (no Rust changes in this PR)
  • Coverage matrix updated — N/A: behaviour-only change on existing Welcome OAuth flow; no new matrix row
  • All affected feature IDs from the matrix are listed in the PR description under ## Related
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: automated regression only
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Desktop OAuth sign-in on first launch after the runtime picker.
  • No migration or API contract changes.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

Commit & Branch

  • Branch: cursor/a02-1689-signin-failed-first-time
  • Commit SHA: 6019ea2

Validation Run

  • pnpm --filter openhuman-app format:check
  • pnpm typecheck
  • Focused tests: pnpm debug unit src/components/oauth/__tests__/oauthAuthReadiness.test.ts src/components/oauth/__tests__/OAuthProviderButton.test.tsx src/utils/__tests__/desktopDeepLinkListener.test.ts (18 passed)
  • Rust fmt/check (if changed): N/A — no Rust files changed
  • Tauri fmt/check (if changed): N/A

Validation Blocked

  • command: pnpm test:coverage and pnpm test:rust (full merge-gate suite)
  • error: Not run locally in this session due to runtime cost; CI coverage.yml / test.yml will execute on the PR.
  • impact: Diff-coverage gate validated in CI; changed lines covered by new/updated unit tests listed above.

Behavior Changes

  • Intended behavior change: OAuth sign-in waits for runtime readiness and reports specific errors when the core is not up yet.
  • User-visible effect: First-launch Google/GitHub sign-in should succeed after choosing Local runtime; failures explain setup/runtime issues instead of a generic retry message.

Parity Contract

  • Legacy behavior preserved: Successful sign-in still stores session via auth_store_session and routes to /home.
  • Guard/fallback/dispatch parity checks: Deep-link openhuman://auth and openhuman://oauth/* routing unchanged; only readiness timing and error copy changed.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this PR
  • Resolution (closed/superseded/updated): N/A

Made with Cursor

Summary by CodeRabbit

  • New Features

    • OAuth sign-in now includes a runtime readiness gate and a preflight launch step before opening the browser, with user-facing messages explaining block reasons and recovery steps.
    • Deep-link auth flow respects the OAuth readiness gate but still allows direct-session injection paths.
  • Bug Fixes

    • Clearer deep-link auth start/stop behavior and improved error reporting.
    • More robust boot-check dismissal in end-to-end flows.
  • Tests

    • Expanded tests covering readiness validation, deep-link handling, and the OAuth startup preflight.

Review Change Stack

…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>
@CodeGhost21 CodeGhost21 requested a review from a team May 19, 2026 21:02
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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.

Changes

OAuth readiness gating and integration

Layer / File(s) Summary
Core OAuth readiness module
app/src/components/oauth/oauthAuthReadiness.ts
Implements waitForOAuthAuthReadiness() that polls for stored core mode, starts local core in Tauri/local mode, pings core RPC, and returns { ready: true } or { ready: false; reason } (core_mode_unset, core_unreachable, bootstrap_timeout). Adds oauthAuthReadinessUserMessage() and prepareOAuthLoginLaunch() preflight.
Readiness module test coverage
app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts
Vitest suite covering unset core mode, successful readiness (and start_core_process side-effect), RPC unreachable, bootstrapping-to-ready transitions, bootstrap timeout messaging, and non-Tauri behavior.
Gateway re-export for readiness helpers
app/src/utils/oauthAppVersionGate.ts
Re-exports readiness functions and types (waitForOAuthAuthReadiness, prepareOAuthLoginLaunch, oauthAuthReadinessUserMessage, and associated types) for centralized access.
OAuth button readiness coordination
app/src/components/oauth/OAuthProviderButton.tsx, app/src/components/oauth/__tests__/OAuthProviderButton.test.tsx
Button calls beginDeepLinkAuthProcessing() before startup, awaits prepareOAuthLoginLaunch() in Tauri flows, and invokes completeDeepLinkAuthProcessing() on startup error. Tests assert lifecycle and preflight calls.
Deep-link listener readiness gating
app/src/utils/desktopDeepLinkListener.ts, app/src/utils/__tests__/desktopDeepLinkListener.test.ts
openhuman://auth handler now uses waitForOAuthAuthReadiness() (skips when key=auth), surfaces oauthAuthReadinessUserMessage() on block, aborts processing, and tests verify blocked and bypass behaviors.
UI OAuth tests: preflight mock wiring
app/test/*OAuth*.test.tsx, app/test/OAuthLoginSection.test.tsx
Multiple provider test suites hoist and reset a mockPrepareOAuthLoginLaunch and mock oauthAppVersionGate.prepareOAuthLoginLaunch to prevent the readiness preflight from blocking tests in jsdom.
E2E helper: BootCheckGate picker regex
app/test/e2e/helpers/app-helpers.ts
Updates BootCheckGate dismissal helper to use a shared heading regex (BOOT_CHECK_GATE_PICKER_HEADING_REGEX), improves polling to handle mid-mount timing, and re-checks presence after clicking continue.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • senamakel

"🐰
I nudge the core awake with care,
A tiny hop, a breath of air.
Poll, prepare, then open the gate —
OAuth starts; the sign-in's fate."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(oauth): sign-in failed on first launch (oauth flow)' accurately summarizes the main change—fixing OAuth sign-in failures on first launch by adding readiness checks before OAuth operations.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #1689: adds OAuth readiness gate with core mode validation, implements local core startup preflight, confirms core.ping success, waits for bootstrap completion, provides user-facing error messages, and includes comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly related to the OAuth sign-in readiness fix. E2E helper updates (dismissBootCheckGate) support the readiness workflow; test mocks appropriately handle the new preflight step across OAuth provider tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 19, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/src/components/oauth/__tests__/oauthAuthReadiness.test.ts (1)

25-126: ⚡ Quick win

Add test coverage for the bootstrap_timeout failure scenario.

The test suite covers core_mode_unset and core_unreachable, but is missing a test for the bootstrap_timeout case. This distinct failure mode occurs when the core RPC ping succeeds but isBootstrapping remains true (with no sessionToken) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 71526ea and 6019ea2.

📒 Files selected for processing (7)
  • 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/src/utils/oauthAppVersionGate.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 19, 2026
@YOMXXX
Copy link
Copy Markdown
Contributor

YOMXXX commented May 20, 2026

I could not push the CI fix directly to this PR head branch (CodeGhost21/openhuman:cursor/a02-1689-signin-failed-first-time): GitHub rejected the push with Permission denied to YOMXXX despite maintainerCanModify=true.

I opened #2267 as a replacement PR with this PR's first-launch OAuth readiness fix plus the CI stabilization commits:

  • mocks the new Tauri OAuth readiness preflight in the legacy provider tests
  • makes the E2E-only window.__simulateDeepLink helper fire-and-forget so WebDriver does not wait on the full auth readiness path
  • adds regression coverage for the non-blocking helper behavior

Local validation on #2267 passed: full Vitest, coverage, typecheck, focused ESLint, format checks, and GGML_NATIVE=OFF pnpm rust:check.

@DeVaLDuDe DeVaLDuDe mentioned this pull request May 20, 2026
4 tasks
…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.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
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).
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
`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.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
@senamakel senamakel self-assigned this May 20, 2026
senamakel added 2 commits May 20, 2026 14:58
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.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
senamakel added a commit that referenced this pull request May 20, 2026
## 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 -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](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
@senamakel senamakel merged commit 5cbf4ad into tinyhumansai:main May 20, 2026
24 checks passed
mtkik pushed a commit to mtkik/openhuman-meet that referenced this pull request May 21, 2026
## 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 -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](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>
CodeGhost21 added a commit to CodeGhost21/openhuman that referenced this pull request May 22, 2026
## 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 -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Signing in for first time issue

3 participants