Skip to content

fix(google-ads): offer reconnect from the empty account picker - #315

Closed
tonyswartz wants to merge 5 commits into
every-app:mainfrom
tonyswartz:fix/gads-picker-reconnect
Closed

fix(google-ads): offer reconnect from the empty account picker#315
tonyswartz wants to merge 5 commits into
every-app:mainfrom
tonyswartz:fix/gads-picker-reconnect

Conversation

@tonyswartz

Copy link
Copy Markdown

What

When Google Ads account discovery returns no candidates, the picker now shows a Reconnect with Google button, with copy pointing at the most common cause (the Google Ads permission checkbox left unchecked on Google's consent screen).

Why

The empty state was a dead end: the Disconnect button only renders after a successful connect, and the card's Connect button is hidden once a grant exists. A user who skipped the consent checkbox (which surfaces server-side as accountsUnavailable, a 403 scope error, not the 401 that triggers requiresReconnect) had no way to redo the OAuth flow. Hit in production by Tony ~23:47Z tonight, minutes after first connect.

Reconnecting is safe and sufficient: the self-hosted OAuth callback upserts the grant by (user, provider, Google account id) and prompt=select_account consent forces the chooser + consent screen on every pass.

Verification

  • prettier --check clean, tsc --noEmit clean
  • Full vitest suite: 1177 passed, 140 files

🤖 Generated with Claude Code

tonyswartz and others added 5 commits September 3, 2026 13:23
Deletes a tracker config with all of its keywords, runs, and snapshots.
Children are removed explicitly in one atomic runBatch (snapshots, runs,
keywords, config) so the operation works whether or not the SQLite
connection enforces foreign keys, and identically on Postgres.

Deletion is refused with CONFLICT while a rank check is genuinely in
flight; a stale active-looking run (dead workflow instance, per
reconcileActiveRankCheckRun) does not block and is swept up by the
cascade.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(rank-tracking): add delete_rank_tracker MCP tool
- Google Ads OAuth connect flow (self-hosted + hosted) reusing the shared
  Google client; new google-ads provider registered for token refresh
- google_ads_connections table (SQLite + Postgres) with per-project Ads
  account selection, incl. manager (login-customer-id) support
- Read-only Ads API client (v25, googleAds:search only) and LSA reporting
  service: spend, lead totals, cost per charged lead, weekly-budget pacing
- MCP tools get_local_services_performance / get_local_services_leads,
  wired into SAM and the tool catalog
- Dashboard card (renders only when connected) + integrations settings card;
  pending Google API approval surfaces as an expected state, not an error
- Env/preflight/health, GDPR erasure, workspace-merge wiring; self-hosting doc

Ships dark: with no connection or GOOGLE_ADS_DEVELOPER_TOKEN nothing
existing changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(google-ads): read-only Local Services Ads reporting
When account discovery returns no candidates (most commonly because the
Google Ads permission checkbox was left unchecked on the consent screen,
which surfaces as accountsUnavailable), the picker showed only an empty
state with no way to redo the OAuth flow: the Disconnect button only
renders after a successful connect, so the user was stranded.

Show a Reconnect with Google button in the empty state, with copy that
points at the unchecked-consent cause when Google refused the listing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tonyswartz

Copy link
Copy Markdown
Author

Opened against the wrong repo (meant for a downstream fork). Apologies for the noise.

@tonyswartz tonyswartz closed this Sep 9, 2026
@tonyswartz
tonyswartz deleted the fix/gads-picker-reconnect branch September 9, 2026 23:57
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