Skip to content

fix(mobile): stop blank Tailscale Adding carnival - #779

Merged
github-actions[bot] merged 6 commits into
mainfrom
fix/paid-user-connect-ux-20260722
Jul 23, 2026
Merged

fix(mobile): stop blank Tailscale Adding carnival#779
github-actions[bot] merged 6 commits into
mainfrom
fix/paid-user-connect-ux-20260722

Conversation

@IgorGanapolsky

Copy link
Copy Markdown
Owner

Summary

  • Only the tapped Tailscale candidate shows Adding…; sibling chips keep host names.
  • When Tailscale candidates exist, ConnectMacGate promotes them and demotes QR / Wi-Fi scan loaders.
  • Wi-Fi search no longer kicks a concurrent Tailscale probe; Settings drops the obsolete global adding prop.

Test plan

  • Focused Jest: TailscaleDiscoveryBanner / ConnectMacGate / ChatConnectionPanel
  • npm run typecheck
  • Full Jest: 222 suites / 1948 passed / 1 skipped
  • Continuous E2E / fresh-user gate before production OTA (latest.json was skipped while phone in use)

Made with Cursor

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions
github-actions Bot enabled auto-merge (squash) July 22, 2026 13:38
@gitar-bot

gitar-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

Gitar is working

Gitar

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates the mobile Tailscale discovery flow for adding computers. The main changes are:

  • Per-chip Adding… state in TailscaleDiscoveryBanner.
  • Tailscale candidates promoted in ConnectMacGate when available.
  • QR and Wi-Fi scan loaders hidden while Tailscale candidates are shown.
  • Chat and Settings call sites updated to stop passing the removed global adding prop.
  • Focused tests added for the Tailscale banner, gate, and chat connection panel.

Confidence Score: 4/5

This PR is close, but the Tailscale add loading state still behaves incorrectly outside the fresh-user gate.

The gate behavior matches the intended Tailscale-first and single-primary-CTA flow. One current bug remains: Chat and Settings wrappers discard the async add promise, so Adding… clears before the real add/select work completes.

hermes-mobile/src/components/ChatConnectionPanel.tsx; hermes-mobile/src/screens/SettingsScreen.tsx

T-Rex T-Rex Logs

What T-Rex did

  • Ran a focused Jest/React Native Testing Library harness that renders ChatConnectionPanel with a Tailscale discovery and exercises the Other ways flow to reproduce the add-promise behavior.
  • Observed that the discard-wrapper path discarded the promise and the UI showed the final item name with no loading indicator, reproducing the broken loading contract.
  • Observed that the returned-promise path kept the Adding… state until the deferred promise resolved, showing that not returning the add promise causes the loading issue.
  • Validated broader Hermes mobile test results and validation artifacts, including PASS for key components, a successful typecheck, and Maestro flow validation OK.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
hermes-mobile/src/components/TailscaleDiscoveryBanner.tsx Adds local per-discovery add state so only the tapped Tailscale chip shows Adding…; behavior depends on callers returning the async add promise.
hermes-mobile/src/components/ConnectMacGate.tsx Promotes Tailscale candidates above Wi-Fi/QR loaders and passes the async add handler directly in the fresh-user gate.
hermes-mobile/src/components/ChatConnectionPanel.tsx Stops passing global probing as add state, but the onAddTailscaleComputer contract still drops async completion so chip loading clears immediately in real chat callers.
hermes-mobile/src/screens/SettingsScreen.tsx Drops the obsolete global adding prop, but still wraps the async Tailscale add call with void, preventing the banner from tracking pending add state.
hermes-mobile/src/tests/TailscaleDiscoveryBanner.test.tsx Adds focused coverage for single-chip Adding… state using a pending promise.
hermes-mobile/src/tests/ChatConnectionPanel.test.tsx Covers Tailscale banner visibility and verifies global probing no longer blanks sibling chip labels.
hermes-mobile/src/tests/ConnectMacGate.test.tsx Adds regression coverage that Tailscale candidates suppress competing fresh-user Wi-Fi/QR loaders.
plan.md Updates live coordination state for the paid-user Tailscale connect UX task.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
hermes-mobile/src/components/ChatConnectionPanel.tsx:276
**Return the add promise**
`TailscaleDiscoveryBanner` now keeps `Adding…` visible only while the `onAdd` promise is pending, but this prop is still typed as `void` and real callers wrap `addDiscoveredTailscaleComputer` with `void`. In `ChatScreen` and `SettingsScreen`, pressing a chip clears `addingKey` immediately while the async catalog/select work continues, so the per-chip loading state is not effective outside the gate. Please widen `onAddTailscaleComputer` to return `Promise<void> | void` and return `addDiscoveredTailscaleComputer(discovery)` from the wrappers.

Reviews (1): Last reviewed commit: "test(mobile): remove trailing whitespace" | Re-trigger Greptile

discoveries={tailscaleDiscoveries}
adding={tailscaleDiscoveryProbing}
probing={tailscaleSearching}
onAdd={onAddTailscaleComputer}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Return the add promise
TailscaleDiscoveryBanner now keeps Adding… visible only while the onAdd promise is pending, but this prop is still typed as void and real callers wrap addDiscoveredTailscaleComputer with void. In ChatScreen and SettingsScreen, pressing a chip clears addingKey immediately while the async catalog/select work continues, so the per-chip loading state is not effective outside the gate. Please widen onAddTailscaleComputer to return Promise<void> | void and return addDiscoveredTailscaleComputer(discovery) from the wrappers.

Artifacts

Repro: focused Jest harness for ChatConnectionPanel Tailscale add promise behavior

  • Contains supporting evidence from the run (text/tsx; charset=utf-8).

Repro: Jest output showing immediate clearing when promise is discarded and retained loading when returned

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: hermes-mobile/src/components/ChatConnectionPanel.tsx
Line: 276

Comment:
**Return the add promise**
`TailscaleDiscoveryBanner` now keeps `Adding…` visible only while the `onAdd` promise is pending, but this prop is still typed as `void` and real callers wrap `addDiscoveredTailscaleComputer` with `void`. In `ChatScreen` and `SettingsScreen`, pressing a chip clears `addingKey` immediately while the async catalog/select work continues, so the per-chip loading state is not effective outside the gate. Please widen `onAddTailscaleComputer` to return `Promise<void> | void` and return `addDiscoveredTailscaleComputer(discovery)` from the wrappers.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 309d5c17bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plan.md
- `.github/workflows/mobile-ota.yml` (rollout clamp only: 100=omit flag; 1-99 staged; promote clamp), `hermes-mobile/src/__tests__/releaseSafetyContract.test.ts` (EAS-rejects-100 strings only; leave Leash tab wording), `plan.md` → **terra-ota-rollout-clamp** (T-OTA-ROLLOUT-EAS-99; evidence run 29889285465) (2026-07-22T04:12:00Z)
- `hermes-mobile/src/components/ConnectMacGate.tsx` (always-visible onboarding+Find computers; do not hide for profileScanning), `hermes-mobile/src/__tests__/ConnectMacGate.test.tsx` (profileScanning keeps onboarding), `plan.md` → **cursor-leash-lazy-spinner** (T-LEASH-LAZY-SPINNER stranger OTA unblock) (2026-07-22T03:30:00Z)
- `hermes-mobile/src/components/ConnectMacGate.tsx` (onboarding visible during bootstrap booting only — keep #755 search collapse), `hermes-mobile/src/__tests__/ConnectMacGate.test.tsx` (bootstrap booting cases), `plan.md` → **cursor-leash-lazy-spinner** (T-LEASH-LAZY-SPINNER unblock stranger CI after #755) (2026-07-22T02:46:01Z)
- `hermes-mobile/src/components/TailscaleDiscoveryBanner.tsx`, `hermes-mobile/src/components/ConnectMacGate.tsx` (candidate-first discovery state and QR demotion only), `hermes-mobile/src/components/ChatConnectionPanel.tsx` (pass per-candidate add behavior only), `hermes-mobile/src/__tests__/TailscaleDiscoveryBanner.test.tsx`, `hermes-mobile/src/__tests__/ConnectMacGate.test.tsx`, `hermes-mobile/src/__tests__/ChatConnectionPanel.test.tsx`, `plan.md` → **terra-paid-user-connect-ux** (T-PAID-USER-CONNECT-UX-20260722; reclaimed after confirming T-CONNECT-UX/#755 and T-LEASH worktrees were clean, their branches had no open PR, and their last commits were 2026-07-21; do not touch GatewayContext or discovery/profile services) (2026-07-22T13:35:00Z)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Respect the existing ConnectMacGate owner

The ownership map above still has active cursor-leash-lazy-spinner claims for hermes-mobile/src/components/ConnectMacGate.tsx and its test, and this new claim/edit takes the same files without an append-only release. In this repo that creates a live multi-owner hot-file collision instead of a block/release handoff, so another agent can overwrite this work or vice versa; add a release/blocked handoff before touching those files.

AGENTS.md reference: AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

Comment on lines +30 to +32
await onAdd(discovery);
} finally {
setAddingKey(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep Add chips disabled until the real add finishes

When this banner is used from ChatConnectionPanel or Settings, the supplied callbacks fire-and-forget addDiscoveredTailscaleComputer with void (ChatScreen.tsx:7047-7049, SettingsScreen.tsx:543-545), so await onAdd(discovery) resolves immediately and the finally clears addingKey while the async save/select is still running. In those screens the tapped chip re-enables almost instantly, Adding… is not visible, and a second tap can start another add/switch for the same candidate before the first one completes; return the promise through those callbacks or keep the in-flight state where the async work actually lives.

Useful? React with 👍 / 👎.

@IgorGanapolsky

Copy link
Copy Markdown
Owner Author

Also lands: onboarding steps now prefer saved macLabel over Tailscale discovery label (fixes MacBook header + Mac mini steps mismatch).

@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch from 9d18d4a to 6e958b0 Compare July 22, 2026 14:53
IgorGanapolsky added a commit that referenced this pull request Jul 22, 2026
Choose computer paste-Tailscale-IP hero copy; coord spacing/#779 slices.

Co-authored-by: Cursor <cursoragent@cursor.com>
IgorGanapolsky added a commit that referenced this pull request Jul 22, 2026
Choose computer paste-Tailscale-IP hero copy; coord spacing/#779 slices.

Co-authored-by: Cursor <cursoragent@cursor.com>
@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch from 74eaa49 to ee76f1e Compare July 22, 2026 15:30
IgorGanapolsky added a commit that referenced this pull request Jul 22, 2026
Choose computer paste-Tailscale-IP hero copy; coord spacing/#779 slices.

Co-authored-by: Cursor <cursoragent@cursor.com>
IgorGanapolsky added a commit that referenced this pull request Jul 22, 2026
Choose computer paste-Tailscale-IP hero copy; coord spacing/#779 slices.

Co-authored-by: Cursor <cursoragent@cursor.com>
@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch 15 times, most recently from de497b2 to 09fb719 Compare July 22, 2026 23:59
@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch from 09fb719 to a1cab25 Compare July 23, 2026 00:35
@IgorGanapolsky

Copy link
Copy Markdown
Owner Author

Rebased onto main f32ff2c3 (includes easBuildGuard clock-safe fixture from #826). Preserved Tailscale-first + #787 paste-IP resolution. Will squash-merge immediately when CLEAN+green (not waiting on automerge/Maestro race).

@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch 3 times, most recently from c5743b0 to 7e3bce5 Compare July 23, 2026 02:30
IgorGanapolsky and others added 4 commits July 23, 2026 01:41
Co-authored-by: Cursor <cursoragent@cursor.com>
Paid-user discovery was rendering every Tailscale chip as Adding while
Wi-Fi search and QR competed for attention. Track per-candidate add
state, promote Tailscale candidates as the primary path, and demote QR
plus competing Wi-Fi loaders when candidates are present.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
When MacBook is unreachable, steps said Start Hermes on Mac mini because
primaryTailscaleLabel won. Prefer the saved macLabel.

Co-authored-by: Cursor <cursoragent@cursor.com>
@IgorGanapolsky
IgorGanapolsky force-pushed the fix/paid-user-connect-ux-20260722 branch from 7e3bce5 to 9de5976 Compare July 23, 2026 05:42
@IgorGanapolsky

Copy link
Copy Markdown
Owner Author

cursor-recovery: will update branch onto main and squash-merge (all required checks green).

@IgorGanapolsky

Copy link
Copy Markdown
Owner Author

OTA freeze (2026-07-23): Expo billing declined (~$78). Do not publish preview or production OTA until further notice. mobile-ota.yml workflow disabled at repo level. Code merge OK; OTA publish blocked.

@github-actions
github-actions Bot merged commit 7cde53d into main Jul 23, 2026
13 checks passed
@IgorGanapolsky
IgorGanapolsky deleted the fix/paid-user-connect-ux-20260722 branch July 28, 2026 16:49
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.

1 participant