Skip to content

feat(onboarding): add artists by Spotify typeahead, not free text (chat#1889 row 8)#1892

Open
sweetmantech wants to merge 1 commit into
mainfrom
feat/add-artist-spotify-typeahead
Open

feat(onboarding): add artists by Spotify typeahead, not free text (chat#1889 row 8)#1892
sweetmantech wants to merge 1 commit into
mainfrom
feat/add-artist-spotify-typeahead

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Matrix row 8 in chat#1889 — the highest-value item after the convergence chain. Independent of #1890/#1891 (different files), so it can be reviewed in parallel.

Why: the payoff was structurally unreachable for most signups

Live cohort from email_send_log, 2026-07-23 → 2026-07-26:

Welcome emails sent 9, to 9 distinct accounts
With a roster + catalog (valuation-funnel signups) 3
With zero artists, zero catalogs, no valuation 6

The welcome email fires on account creation regardless of whether a valuation preceded it, so two-thirds of the cohort are cold-start signups. Their only forward path is AddArtistForm — which took a free-text name and posted only { name }. No Spotify id → no catalog → no valuation. The number those users were emailed about could never exist.

What changed

  • AddArtistForm uses SpotifyArtistSearch — the same typeahead verify-socials adopted in feat(onboarding): Spotify typeahead in verify-socials (item 3b of #1881) #1882 — so a pick resolves to a real Spotify id, profile URL, and avatar.
  • useAddRosterArtist now takes { profileUrl, image } and attaches them via the existing PATCH /api/artists/{id} path after creation, because POST /api/artists accepts only name / account_id / organization_id. Reuses buildSocialFixPayload for the platform detection rather than hand-rolling the shape. Enrichment failure is deliberately non-fatal — the artist is already on the roster and socials can still be fixed in the next step.
  • ConfirmRosterStep copy: an empty roster was told "We set these artists up from your valuation" — false for 6 of 9 signups. It now reads as an instruction to search, and the empty state names the reward.

No api change needed, so no docs/contract change either — this rides two existing endpoints.

Verification

TDD, red → green:

```

RED — free-text field still present, no typeahead

Test Files 1 failed (1)
Tests 2 failed (2)

GREEN

pnpm exec vitest run components/Onboarding lib/onboarding
Test Files 18 passed (18)
Tests 70 passed (70)
```

The new test asserts the free-text Artist name field is gone, the Spotify searchbox is present, and picking a result calls addArtist with the resolved profile URL + avatar.

pnpm exec tsc --noEmit clean for the touched files. Preview click-through pending (needs an authed empty-roster account).

Tracked in chat#1889 (matrix row 8).


Summary by cubic

Replace free-text artist input with Spotify typeahead in onboarding so added artists have a real Spotify id, profile URL, and avatar. This lets cold-start signups proceed to catalog and valuation (chat#1889, row 8).

  • New Features
    • AddArtistForm now uses SpotifyArtistSearch; selection calls addArtist(name, { profileUrl, image }).
    • useAddRosterArtist enriches the created artist via PATCH /api/artists/{id} using buildSocialFixPayload; enrichment is non-fatal and the roster refreshes.
    • Updated ConfirmRosterStep copy to prompt Spotify search for empty rosters and set expectations for the next step.

Written for commit a332e38. Summary will update on new commits.

Review in cubic

chat#1889 matrix row 8 — the highest-value item after convergence.

AddArtistForm took a free-text name and useAddRosterArtist posted only that, so
an added artist had no Spotify id. Without one there is no catalog and no
valuation, which makes the payoff structurally unreachable — and that is the
only forward path for the cold-start cohort: of the 9 accounts that received a
welcome email 2026-07-23..25, 6 had zero artists and had never run a valuation.

- AddArtistForm uses the existing SpotifyArtistSearch typeahead (same component
  verify-socials adopted in #1882), so a pick resolves to a real Spotify id.
- useAddRosterArtist attaches the profile URL (via buildSocialFixPayload) and
  the Spotify avatar through PATCH /api/artists/{id} after creation, since
  POST /api/artists accepts only a name. Enrichment failure is non-fatal.
- ConfirmRosterStep no longer tells an empty roster we set it up "from your
  valuation" — false for two thirds of signups.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview Jul 27, 2026 4:50am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: db7777ec-a68e-4150-8ac4-8fbd1bfb230d

📥 Commits

Reviewing files that changed from the base of the PR and between a2d8cf8 and a332e38.

⛔ Files ignored due to path filters (1)
  • components/Onboarding/__tests__/AddArtistForm.test.tsx is excluded by !**/*.test.* and included by components/**
📒 Files selected for processing (3)
  • components/Onboarding/AddArtistForm.tsx
  • components/Onboarding/ConfirmRosterStep.tsx
  • hooks/onboarding/useAddRosterArtist.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-artist-spotify-typeahead

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: a332e38fe7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +24 to +27
const added = await addArtist(artist.name, {
profileUrl: artist.external_urls.spotify,
image: artist.images?.[0]?.url,
});

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 Seed a valuation from the selected Spotify artist

For a cold-start account with no catalogs, this selection passes only the artist name, profile URL, and image, discarding artist.id; the hook then performs only the artist POST/PATCH. Unlike hooks/useAddSpotifyArtist.ts, it never calls runValuation(accessToken, artist.id), so /setup/catalog can still redirect these users to a catalogs page that says "No catalogs found" despite the new copy promising that adding the artist gets the catalog valued.

Useful? React with 👍 / 👎.

Comment on lines +58 to +61
await saveArtist(accessToken, artist.account_id, {
...(payload ? { profileUrls: payload.profileUrls } : {}),
...(options.image ? { image: options.image } : {}),
});

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 Treat post-create enrichment failures as successful adds

When the initial artist POST succeeds but this PATCH fails (for example, during a transient API/network error), control reaches the outer catch, skips getArtists(), and returns false. The form therefore remains open and invites the user to select the artist again, issuing another POST and potentially creating duplicate roster entries even though the first artist already exists; isolate enrichment failure from creation and still refresh/close after a successful create.

Useful? React with 👍 / 👎.

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 4 files

Confidence score: 2/5

  • In components/Onboarding/AddArtistForm.tsx, adding the first artist from an empty roster can skip triggering catalog valuation, so users may move forward without the valuation step the flow now promises — trigger valuation from the known selected artist.id when the first artist is added.
  • In hooks/onboarding/useAddRosterArtist.ts, a temporary PATCH/enrichment failure is surfaced as a full add failure even after createRosterArtist has already succeeded, which can leave the form open and cause duplicate artists on retry — separate enrichment error handling from create success and treat partial success explicitly.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="hooks/onboarding/useAddRosterArtist.ts">

<violation number="1" location="hooks/onboarding/useAddRosterArtist.ts:57">
P1: A transient PATCH failure makes the form report the add as failed even though `createRosterArtist` already persisted the artist. The form remains open and a retry can create a duplicate; catch enrichment errors separately, notify the user if appropriate, then refresh and return success.</violation>
</file>

<file name="components/Onboarding/AddArtistForm.tsx">

<violation number="1" location="components/Onboarding/AddArtistForm.tsx:24">
P1: Adding the first artist from an empty onboarding roster still never starts catalog valuation, so users can continue past this step without the catalog the new copy promises. This handler has the selected `artist.id`; route it through `useAddSpotifyArtist` (or trigger the same first-roster valuation flow here) rather than the name/profile-only hook.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +57 to +62
if (artist?.account_id && (payload || options.image)) {
await saveArtist(accessToken, artist.account_id, {
...(payload ? { profileUrls: payload.profileUrls } : {}),
...(options.image ? { image: options.image } : {}),
});
}

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: A transient PATCH failure makes the form report the add as failed even though createRosterArtist already persisted the artist. The form remains open and a retry can create a duplicate; catch enrichment errors separately, notify the user if appropriate, then refresh and return success.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/onboarding/useAddRosterArtist.ts, line 57:

<comment>A transient PATCH failure makes the form report the add as failed even though `createRosterArtist` already persisted the artist. The form remains open and a retry can create a duplicate; catch enrichment errors separately, notify the user if appropriate, then refresh and return success.</comment>

<file context>
@@ -27,7 +45,22 @@ export function useAddRosterArtist() {
+      const payload = options.profileUrl
+        ? buildSocialFixPayload(options.profileUrl)
+        : null;
+      if (artist?.account_id && (payload || options.image)) {
+        await saveArtist(accessToken, artist.account_id, {
+          ...(payload ? { profileUrls: payload.profileUrls } : {}),
</file context>
Suggested change
if (artist?.account_id && (payload || options.image)) {
await saveArtist(accessToken, artist.account_id, {
...(payload ? { profileUrls: payload.profileUrls } : {}),
...(options.image ? { image: options.image } : {}),
});
}
if (artist?.account_id && (payload || options.image)) {
try {
await saveArtist(accessToken, artist.account_id, {
...(payload ? { profileUrls: payload.profileUrls } : {}),
...(options.image ? { image: options.image } : {}),
});
} catch {
toast.error("Artist added, but Spotify details could not be saved");
}
}

setIsOpen(false);
}
const handleSelect = async (artist: SpotifyArtistSearchResult) => {
const added = await addArtist(artist.name, {

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: Adding the first artist from an empty onboarding roster still never starts catalog valuation, so users can continue past this step without the catalog the new copy promises. This handler has the selected artist.id; route it through useAddSpotifyArtist (or trigger the same first-roster valuation flow here) rather than the name/profile-only hook.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Onboarding/AddArtistForm.tsx, line 24:

<comment>Adding the first artist from an empty onboarding roster still never starts catalog valuation, so users can continue past this step without the catalog the new copy promises. This handler has the selected `artist.id`; route it through `useAddSpotifyArtist` (or trigger the same first-roster valuation flow here) rather than the name/profile-only hook.</comment>

<file context>
@@ -1,24 +1,31 @@
-      setIsOpen(false);
-    }
+  const handleSelect = async (artist: SpotifyArtistSearchResult) => {
+    const added = await addArtist(artist.name, {
+      profileUrl: artist.external_urls.spotify,
+      image: artist.images?.[0]?.url,
</file context>

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