Skip to content

feat(onboarding): seed the catalog when the first artist is added in setup (chat#1889 row 8) - #1900

Open
sweetmantech wants to merge 3 commits into
mainfrom
feat/seed-catalog-on-first-artist
Open

feat(onboarding): seed the catalog when the first artist is added in setup (chat#1889 row 8)#1900
sweetmantech wants to merge 3 commits into
mainfrom
feat/seed-catalog-on-first-artist

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Matrix row 8 in chat#1889 — the fix for the dead end #1895 surfaced.

Why

/setup/artists added artists through useAddRosterArtist, which creates and enriches but never values. useAddSpotifyArtist — already used by the /artists dialog — also fire-and-forget kicks POST /api/valuation via runValuation when the account has no catalog yet. Its docstring has said so since chat#1867:

seeding the onboarding catalog in the background so the "Claim your catalog" step is already complete by the time the user reaches it

So the same artist added at /artists got a catalog and at /setup/artists did not. Verified live on a fresh account during #1895's review: two artists added through setup, both carrying real Spotify ids, GET /api/accounts/{id}/catalogs still []. With #1895 on prod that means every cold-start account now finishes setup on the "Claim your catalog" fallback, whose CTA lands on /catalogs → "No catalogs found." with no create affordance.

What changed

  • AddArtistForm adds through useAddSpotifyArtist, passing the whole Spotify result.
  • useAddRosterArtist deleted (and its test). Its enrichment duplicated addSpotifyArtist — identical createRosterArtistsaveArtist({ image, profileUrls: { SPOTIFY } }) — which predates chat#1892. One caller, so this is a swap that removes code.
  • addSpotifyArtist enrichment is now non-fatal, carrying chat#1892's guard across. It awaited saveArtist bare, so a failed PATCH threw past the create that had already succeeded: the caller reported failure over an existing artist and a retry duplicated it. This had to land with the swap or the swap would have reintroduced the defect chat#1892 just closed.

No api or docs change: this reuses POST /api/valuation (api#776) exactly as the /artists dialog already does, so there is no contract to write first.

Verification

TDD, red → green, two units:

# RED 1 — enrichment failure propagates
FAIL lib/artists/__tests__/addSpotifyArtist.test.ts > returns the created artist when enrichment fails
Error: Forbidden

# RED 2 — form still bound to the non-seeding hook
FAIL components/Onboarding/__tests__/AddArtistForm.test.tsx (2 failed)

# GREEN
pnpm exec vitest run components lib hooks
  Test Files  72 passed (72)
       Tests  271 passed (271)

pnpm exec tsc --noEmit → 13 errors, identical to main's baseline. eslint clean on all touched files.

Preview verification to follow on this PR.

Tracked in chat#1889 (matrix row 8).


Summary by cubic

Seeds the catalog when the first artist is added during setup by routing AddArtistForm through useAddSpotifyArtist, and adds a measuring state in /setup/valuation so onboarding no longer dead-ends (chat#1889 row 8). This matches the /artists dialog and kicks POST /api/valuation on first add.

  • Bug Fixes

    • Seeding: useAddSpotifyArtist now runs valuation on the first artist added in setup, preventing empty-catalog onboarding paths (fixes the fallback seen after feat(onboarding): end setup on the catalog valuation (chat#1889 row 10) #1895).
    • Measuring: /setup/valuation shows a “Measuring your catalog” state when a catalog exists but has no valuation yet, avoiding an endless skeleton and guiding users to /setup/tasks.
    • Made enrichment in addSpotifyArtist non-fatal to avoid false failures and duplicate creates.
  • Refactors

    • Removed useAddRosterArtist and switched AddArtistForm to useAddSpotifyArtist (passes the full Spotify result). Tests updated.
    • Extracted MeasuringCatalogPanel into its own component and used it in SetupValuation for cleaner composition.

Written for commit 2f6aee0. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Onboarding now adds artists directly from Spotify selections, bringing in profile and image details.
    • Added an intermediate “Measuring your catalog” screen with guidance and a link to setup tasks while valuation is being prepared.
  • Bug Fixes
    • Artist addition now succeeds even if Spotify profile/image enrichment fails temporarily, allowing missing details to be repaired later.
  • UI Improvements
    • Updated valuation rendering so catalog “pending” and “valuation not yet showing” states display the appropriate screens.

…setup

/setup/artists added artists through useAddRosterArtist, which creates and
enriches but never values. useAddSpotifyArtist -- already used by the
/artists dialog -- also kicks POST /api/valuation when the account has no
catalog yet, which is what makes the payoff reachable: without it an
account onboarded through setup finished with no catalog, so chat#1895's
reward could only ever render its 'Claim your catalog' fallback, and that
CTA lands on 'No catalogs found' with nothing to click.

Points AddArtistForm at that hook and deletes useAddRosterArtist, whose
enrichment duplicated addSpotifyArtist (identical createRosterArtist then
saveArtist with image + profileUrls, and it predates chat#1892).

Carries chat#1892's non-fatal-enrichment guard into addSpotifyArtist
first: it awaited saveArtist bare, so a failed PATCH reported failure over
an artist POST /api/artists had already created and a retry duplicated it.

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

cursor Bot commented Jul 28, 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 28, 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 28, 2026 10:35pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 739c7141-de58-426d-bd03-71ff6b4b9222

📥 Commits

Reviewing files that changed from the base of the PR and between 0c88924 and 2f6aee0.

📒 Files selected for processing (2)
  • components/Onboarding/MeasuringCatalogPanel.tsx
  • components/Onboarding/SetupValuation.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/Onboarding/SetupValuation.tsx

📝 Walkthrough

Walkthrough

Onboarding artist selection now uses the Spotify artist hook and payload. Spotify enrichment failures no longer fail artist creation. Setup valuation distinguishes catalog loading from the intermediate catalog-measuring state.

Changes

Spotify onboarding and valuation readiness

Layer / File(s) Summary
Spotify-based onboarding artist creation
components/Onboarding/AddArtistForm.tsx, hooks/useAddSpotifyArtist.ts, lib/artists/addSpotifyArtist.ts
AddArtistForm passes the full Spotify search result through useAddSpotifyArtist; enrichment failures return the created artist. The obsolete roster hook is removed.
Valuation readiness rendering
components/Onboarding/MeasuringCatalogPanel.tsx, components/Onboarding/SetupValuation.tsx
Catalog loading continues to show a skeleton, while unavailable valuation visibility renders a “Measuring your catalog” screen linking to /setup/tasks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AddArtistForm
  participant useAddSpotifyArtist
  participant addSpotifyArtist
  AddArtistForm->>useAddSpotifyArtist: add(selected Spotify artist)
  useAddSpotifyArtist->>addSpotifyArtist: create and enrich artist
  addSpotifyArtist-->>useAddSpotifyArtist: return created or enriched artist
  useAddSpotifyArtist-->>AddArtistForm: update addition state
Loading

Possibly related issues

Possibly related PRs

  • recoupable/chat#1878: Shares the Spotify search and artist-add infrastructure used by this flow.
  • recoupable/chat#1879: Relates to first-artist catalog seeding and valuation triggering through useAddSpotifyArtist.
  • recoupable/chat#1895: Also changes SetupValuation.tsx gating around catalogs and valuation visibility.

Poem

Spotify artists enter the gate,
Their catalogs begin to calculate.
If socials stumble on the way,
The artist still joins the array.
“Measuring” lights the setup path.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Solid & Clean Code ✅ Passed The touched code is focused and maintainable: the pending valuation UI was extracted into a small component, SetupValuation stayed simple, and no duplicate flow remains in the changed files.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/seed-catalog-on-first-artist

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: c7d544d66f

ℹ️ 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".

*/
const AddArtistForm = () => {
const { addArtist, isAdding } = useAddRosterArtist();
const { add, isAdding } = useAddSpotifyArtist();

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 Wait for catalogs before relying on seeding

When /setup/artists is opened directly and the user selects an artist before useCatalogs() has resolved, this swapped-in hook reports success but never starts runValuation: useAddSpotifyArtist only seeds when catalogsQuery.data?.catalogs is already a defined empty array, while ConfirmRosterStep renders this form without waiting for that query. That leaves cold-start setup users in the same no-catalog fallback this change is meant to eliminate, so the add flow should wait/refetch until catalog state is known before deciding whether to seed.

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.

1 issue found across 7 files

Confidence score: 3/5

  • In components/Onboarding/AddArtistForm.tsx, moving to useAddSpotifyArtist appears to make catalog seeding contingent on catalogsQuery.data?.catalogs already being defined, so if ConfirmRosterStep renders before that query resolves the seed path may be skipped and onboarding can fail or behave inconsistently for users—guard this by handling the loading/undefined state explicitly or delaying the action until catalogs are resolved.
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="components/Onboarding/AddArtistForm.tsx">

<violation number="1" location="components/Onboarding/AddArtistForm.tsx:26">
P2: Switching AddArtistForm to useAddSpotifyArtist means catalog seeding now depends on catalogsQuery having already resolved (`catalogsQuery.data?.catalogs` being a defined empty array). If ConfirmRosterStep renders this form before that query settles (e.g. when /setup/artists is opened directly), the add will report success but skip kicking off `runValuation`, leaving the account without a catalog — the exact dead-end this PR is trying to fix. Consider waiting for/refetching catalog state before deciding whether to seed.</violation>
</file>

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

Re-trigger cubic

*/
const AddArtistForm = () => {
const { addArtist, isAdding } = useAddRosterArtist();
const { add, isAdding } = useAddSpotifyArtist();

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: Switching AddArtistForm to useAddSpotifyArtist means catalog seeding now depends on catalogsQuery having already resolved (catalogsQuery.data?.catalogs being a defined empty array). If ConfirmRosterStep renders this form before that query settles (e.g. when /setup/artists is opened directly), the add will report success but skip kicking off runValuation, leaving the account without a catalog — the exact dead-end this PR is trying to fix. Consider waiting for/refetching catalog state before deciding whether to seed.

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 26:

<comment>Switching AddArtistForm to useAddSpotifyArtist means catalog seeding now depends on catalogsQuery having already resolved (`catalogsQuery.data?.catalogs` being a defined empty array). If ConfirmRosterStep renders this form before that query settles (e.g. when /setup/artists is opened directly), the add will report success but skip kicking off `runValuation`, leaving the account without a catalog — the exact dead-end this PR is trying to fix. Consider waiting for/refetching catalog state before deciding whether to seed.</comment>

<file context>
@@ -15,16 +15,19 @@ import type { SpotifyArtistSearchResult } from "@/types/spotify";
  */
 const AddArtistForm = () => {
-  const { addArtist, isAdding } = useAddRosterArtist();
+  const { add, isAdding } = useAddSpotifyArtist();
   const [isOpen, setIsOpen] = useState(false);
 
</file context>

Seeding creates the catalog seconds after the first artist is added, but
its measurements land later. In that window /setup/valuation could not
redirect (a catalog exists) and could not render the hero (nothing
measured), so it sat on a skeleton with no exit -- reproduced live on
preview right after the seeded valuation returned.

Flagged as a theoretical risk when chat#1895 shipped; row 8 makes it
routine, so it is fixed here rather than left.

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

Copy link
Copy Markdown
Collaborator Author

Preview verification — the seeding works end to end, and it surfaced one thing worth fixing here

Verified on chat-qg3k0lgqt-recoup.vercel.app (built from c7d544d6) using the same cold-start account as the #1892/#1895 passes: sweetman+july2820260924@recoupable.com, which had 2 artists and catalogs: [] — the exact state that proved the gap.

The call sequence now includes the seeding

Added Del Water Gap through /setup/artists with fetch instrumented:

GET   /api/artists?
POST  /api/artists                                  ← create
PATCH /api/artists/289d8342-543d-4212-9c44-70f3e6ffdf83   ← enrich (Spotify URL + avatar)
GET   /api/artists?                                 ← roster refresh
POST  /api/valuation                                ← the seeding. Never fired from setup before.

Toast confirmed: "Valuing Del Water Gap's catalog…"

valuation kicked

The catalog materialises, and the dead end is gone

Check Before this PR After
GET /api/accounts/{id}/catalogs [] after two artists added in setup 1 catalog — "Valuation Catalog"
/catalogs (where "Claim your catalog" lands) "No catalogs found." + no create affordance "Valuation Catalog · 67 songs · Created: 7/28/2026"

catalog exists

That is the Done-when for row 8: the CTA that #1895 added now lands somewhere real.

🔴 What this surfaced, and fixed in 0c889248

Immediately after the seeded valuation returned, /setup/valuation rendered a skeleton with no exit: the redirect can't fire (a catalog now exists) and the hero can't render (measurements haven't landed). Reproduced live, not theorised:

blank measuring window

I flagged this as a possible risk when reviewing #1895 and judged it non-blocking because it needed a catalog with no measurements — a state nothing produced routinely. Row 8 produces it on every cold-start signup, for the ~minute between catalog creation and measurement capture. So it is fixed here rather than left: SetupValuation now renders a terminal "Measuring your catalog" state with a "Set up your weekly report" exit, instead of an indefinite skeleton.

Suite

pnpm exec vitest run components lib hooks
  Test Files  72 passed (72)
       Tests  272 passed (272)

tsc --noEmit → 13, identical to main. eslint clean.

Not verified

  • The new "Measuring your catalog" state is unit-tested (RED→GREEN) but not browser-confirmed. The preview Privy session expired before I could re-test 0c889248, and re-authenticating needs an email OTP I can't complete unattended. The bug it fixes is browser-confirmed (screenshot above); the fix itself is not. Worth a click-through before merge.
  • The end-of-setup panel still showed the "Claim your catalog" fallback immediately after seeding, for the same measurement-lag reason. Expected, and it self-resolves once measurements land — but it means a user who races through setup in under a minute still won't see the hero on their first pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
hooks/useAddSpotifyArtist.ts (1)

52-65: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Await catalog resolution before skipping valuation seeding.

catalogsQuery.data can be undefined while the ["catalogs"] query is loading. That path falls through the if (catalogs && catalogs.length === 0) branch permanently, so runValuation() never runs for the newly added artist. Await a successful catalog lookup, or queue the valuation once catalogsQuery.isSuccess && catalogs.length === 0.

🤖 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 `@hooks/useAddSpotifyArtist.ts` around lines 52 - 65, Update the catalog check
in the artist-add flow around catalogsQuery and runValuation so valuation
seeding waits for a completed successful catalog lookup. Only call runValuation
when catalogsQuery.isSuccess and catalogs.length is zero; preserve the existing
toast.promise and query invalidation behavior.
🤖 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.

Inline comments:
In `@components/Onboarding/SetupValuation.tsx`:
- Around line 48-72: Update the !valuation.show branch in SetupValuation so the
measuring state renders only when hasCatalog is true and isError is false. For
missing-catalog or error states, preserve a neutral loading/redirect fallback
until the existing useEffect navigation completes, avoiding the measuring
message and weekly-report CTA.

---

Outside diff comments:
In `@hooks/useAddSpotifyArtist.ts`:
- Around line 52-65: Update the catalog check in the artist-add flow around
catalogsQuery and runValuation so valuation seeding waits for a completed
successful catalog lookup. Only call runValuation when catalogsQuery.isSuccess
and catalogs.length is zero; preserve the existing toast.promise and query
invalidation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f16366a6-6fb5-4b35-a2e4-3a2788c0c7b6

📥 Commits

Reviewing files that changed from the base of the PR and between d0913d1 and 0c88924.

⛔ Files ignored due to path filters (4)
  • components/Onboarding/__tests__/AddArtistForm.test.tsx is excluded by !**/*.test.* and included by components/**
  • components/Onboarding/__tests__/SetupValuation.test.tsx is excluded by !**/*.test.* and included by components/**
  • hooks/onboarding/__tests__/useAddRosterArtist.test.tsx is excluded by !**/*.test.* and included by hooks/**
  • lib/artists/__tests__/addSpotifyArtist.test.ts is excluded by !**/*.test.* and included by lib/**
📒 Files selected for processing (5)
  • components/Onboarding/AddArtistForm.tsx
  • components/Onboarding/SetupValuation.tsx
  • hooks/onboarding/useAddRosterArtist.ts
  • hooks/useAddSpotifyArtist.ts
  • lib/artists/addSpotifyArtist.ts
💤 Files with no reviewable changes (1)
  • hooks/onboarding/useAddRosterArtist.ts

Comment on lines +48 to +72
// A catalog exists but has no valuation yet. Seeding (chat#1889 row 8)
// creates the catalog seconds after the first artist is added and its
// measurements land later, so this window is routine. The redirect above
// cannot fire (there IS a catalog) and the hero cannot render, so without a
// terminal state here the route is a skeleton with no exit.
if (!valuation.show) {
return (
<section className="mx-auto flex w-full max-w-xl flex-col items-center gap-4 px-6 py-8 text-center">
<h1 className="text-2xl font-semibold text-foreground">
Measuring your catalog
</h1>
<p className="text-sm text-muted-foreground">
We are pulling play counts for every track. This usually takes a
minute. Your baseline value appears here as soon as it is ready.
</p>
<Link
href="/setup/tasks"
className={cn(buttonVariants(), "min-w-[200px]")}
>
Set up your weekly report
</Link>
</section>
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard the measuring state with the catalog state.

useEffect redirects only after paint. When hasCatalog is false or isError is true, this new branch can briefly show a misleading measuring screen and weekly-report CTA. Render the terminal state only after confirming a catalog exists; keep a neutral redirect/loading fallback otherwise.

🤖 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 `@components/Onboarding/SetupValuation.tsx` around lines 48 - 72, Update the
!valuation.show branch in SetupValuation so the measuring state renders only
when hasCatalog is true and isError is false. For missing-catalog or error
states, preserve a neutral loading/redirect fallback until the existing
useEffect navigation completes, avoiding the measuring message and weekly-report
CTA.

@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 2 files (changes from recent commits).

Confidence score: 3/5

  • In components/Onboarding/SetupValuation.tsx, the new “Measuring your catalog” end state can become stale because the valuation data is not actively refreshed, so users may wait indefinitely even after processing completes — add polling/refetchInterval (or another explicit refresh trigger) to the valuation-related queries.
  • In components/Onboarding/SetupValuation.tsx, rendering the !valuation.show branch before hasCatalog/isError checks can briefly show the wrong terminal onboarding state before the redirect effect runs, which can confuse users and mask real error/no-catalog paths — gate that branch behind catalog/error guards or reorder the conditional flow.
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="components/Onboarding/SetupValuation.tsx">

<violation number="1" location="components/Onboarding/SetupValuation.tsx:53">
P2: The new `!valuation.show` branch renders the "Measuring your catalog" terminal state without checking `hasCatalog`/`isError` first. Since the redirect to `/catalogs` happens in a `useEffect` (which only fires after paint), a user with no catalog or an error state can briefly flash this measuring screen and weekly-report CTA before being redirected. Consider guarding this branch with `hasCatalog && !isError` (or keeping a neutral loading fallback) so the terminal state only renders once a catalog is confirmed.</violation>

<violation number="2" location="components/Onboarding/SetupValuation.tsx:60">
P2: The new 'Measuring your catalog' state promises the valuation will 'appear here as soon as it is ready,' but there's no polling/refetchInterval on the underlying useCatalogs/useCatalogMeasurements queries, so the user has to manually reload (and even then may hit the 5-minute staleTime cache) to see it update. Consider adding a refetchInterval while valuation.show is false so the page actually updates without a manual refresh.</violation>
</file>

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

Re-trigger cubic

Measuring your catalog
</h1>
<p className="text-sm text-muted-foreground">
We are pulling play counts for every track. This usually takes a

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: The new 'Measuring your catalog' state promises the valuation will 'appear here as soon as it is ready,' but there's no polling/refetchInterval on the underlying useCatalogs/useCatalogMeasurements queries, so the user has to manually reload (and even then may hit the 5-minute staleTime cache) to see it update. Consider adding a refetchInterval while valuation.show is false so the page actually updates without a manual refresh.

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

<comment>The new 'Measuring your catalog' state promises the valuation will 'appear here as soon as it is ready,' but there's no polling/refetchInterval on the underlying useCatalogs/useCatalogMeasurements queries, so the user has to manually reload (and even then may hit the 5-minute staleTime cache) to see it update. Consider adding a refetchInterval while valuation.show is false so the page actually updates without a manual refresh.</comment>

<file context>
@@ -45,6 +45,31 @@ const SetupValuation = () => {
+          Measuring your catalog
+        </h1>
+        <p className="text-sm text-muted-foreground">
+          We are pulling play counts for every track. This usually takes a
+          minute. Your baseline value appears here as soon as it is ready.
+        </p>
</file context>

// measurements land later, so this window is routine. The redirect above
// cannot fire (there IS a catalog) and the hero cannot render, so without a
// terminal state here the route is a skeleton with no exit.
if (!valuation.show) {

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: The new !valuation.show branch renders the "Measuring your catalog" terminal state without checking hasCatalog/isError first. Since the redirect to /catalogs happens in a useEffect (which only fires after paint), a user with no catalog or an error state can briefly flash this measuring screen and weekly-report CTA before being redirected. Consider guarding this branch with hasCatalog && !isError (or keeping a neutral loading fallback) so the terminal state only renders once a catalog is confirmed.

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

<comment>The new `!valuation.show` branch renders the "Measuring your catalog" terminal state without checking `hasCatalog`/`isError` first. Since the redirect to `/catalogs` happens in a `useEffect` (which only fires after paint), a user with no catalog or an error state can briefly flash this measuring screen and weekly-report CTA before being redirected. Consider guarding this branch with `hasCatalog && !isError` (or keeping a neutral loading fallback) so the terminal state only renders once a catalog is confirmed.</comment>

<file context>
@@ -45,6 +45,31 @@ const SetupValuation = () => {
+  // measurements land later, so this window is routine. The redirect above
+  // cannot fire (there IS a catalog) and the hero cannot render, so without a
+  // terminal state here the route is a skeleton with no exit.
+  if (!valuation.show) {
+    return (
+      <section className="mx-auto flex w-full max-w-xl flex-col items-center gap-4 px-6 py-8 text-center">
</file context>

Comment on lines +55 to +69
<section className="mx-auto flex w-full max-w-xl flex-col items-center gap-4 px-6 py-8 text-center">
<h1 className="text-2xl font-semibold text-foreground">
Measuring your catalog
</h1>
<p className="text-sm text-muted-foreground">
We are pulling play counts for every track. This usually takes a
minute. Your baseline value appears here as soon as it is ready.
</p>
<Link
href="/setup/tasks"
className={cn(buttonVariants(), "min-w-[200px]")}
>
Set up your weekly report
</Link>
</section>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

OCP

  • actual: net new code defined inline of existing component file
  • required: new component file for the new component code definition.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done in 2f6aee00 — extracted to components/Onboarding/MeasuringCatalogPanel.tsx (one exported component per file). SetupValuation now composes it (if (!valuation.show) return <MeasuringCatalogPanel />;) instead of carrying the markup inline, so a future state is a new file rather than another branch edited into this one. 89 tests pass; tsc at baseline; eslint clean.

…OCP)

Review feedback on chat#1900: the measuring state was net-new component
code defined inline in an existing component file. It now lives in
components/Onboarding/MeasuringCatalogPanel.tsx, so SetupValuation is
extended by composition rather than modified for each new state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sweetmantech
sweetmantech marked this pull request as draft July 28, 2026 22:33
@sweetmantech

Copy link
Copy Markdown
Collaborator Author

🔴 Blocker found on re-test — seeding creates a duplicate roster artist. Moving this to draft.

Caught by @sweetmantech spotting the same artist twice in the sidebar of my own screenshot. It is real, and it is caused by this PR.

Re-tested from a genuinely empty account (sweetman+july2820261713@…, brand new, zero artists, zero catalogs). Added one artist. GET /api/artists afterwards:

[
  { "name": "Del Water Gap", "account_id": "6e4d3f5f-87d5-47f3-a9b6-b7058d24bb99",
    "socials": ["open.spotify.com/artist/0xPoVNPnxIIUS1vrxAYV00"] },
  { "name": "Del Water Gap", "account_id": "7647f901-a640-434c-b9c9-a18682435092",
    "socials": ["open.spotify.com/artist/0xPoVNPnxIIUS1vrxAYV00"] }
]

Same name, same avatar, same Spotify id, two roster entries.

Mechanism

The instrumented call log shows the client issued exactly one POST /api/artists:

GET   /api/artists?
POST  /api/artists                            ← one create
PATCH /api/artists/6e4d3f5f-…                 ← enrich
GET   /api/artists?
POST  /api/valuation                          ← seeding

So the second artist was created server-side, by the valuation. runValuationHandler step 4 runs createSnapshotCatalogattachCanonicalArtistToAccount, which resolves the artist from the songs graph (ISRC → song_artists → dominant artist) and links that account — a different row from the one this form just minted. Its own docstring names the failure mode:

This replaces the marketing funnel's per-signup artist creation — which minted duplicate, song-less roster artists — as the roster source of truth for funnel signups.

The api deliberately moved away from creating an artist per signup because it duplicated. This PR pairs a client-side create with a server-side canonical attach and reintroduces exactly that.

Scope

Pre-existing on the /artists dialog path, not new code: useAddSpotifyArtist has always done create-then-seed, so any account whose first artist was added through AddArtistDialog gets the same duplicate. What this PR changes is that it becomes the default path for every cold-start signup — so it goes from an edge case to universal. It must not merge as-is.

Options

  1. Reuse instead of insert (api-side, preferred). POST /api/valuation links the account's existing artist when one already carries the same Spotify id, rather than attaching a second. Fixes the dialog path too, and matches attachCanonicalArtistToAccount's stated intent.
  2. Don't pre-create in onboarding. Let the valuation's canonical attach populate the roster. Cleaner conceptually, but the roster step needs an artist to enable Continue, so the user would stare at an empty list for ~20s.
  3. Reconcile client-side after seeding. Drop the locally-created artist once the canonical one appears. Hides the problem rather than fixing it, and races.

I'd take (1). It is the only one that also fixes the existing dialog path, and it belongs in api where the duplicate is actually created.

What still stands from this PR

Everything else verified on the new account: seeding fires on the first add, the catalog materialises, and the "Measuring your catalog" terminal state renders correctly in the real window with a working exit — the case that was a blank skeleton before. Screenshots to follow. The duplicate is the one blocker.

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

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Auto-approval blocked by 3 unresolved issues from previous reviews.

Re-trigger cubic

@sweetmantech

Copy link
Copy Markdown
Collaborator Author

Blocker fix is up: api#791linkSearchedArtistToAccount now reuses the canonical artist for a Spotify id instead of calling createArtistInDb unconditionally.

Per @sweetmantech's call the lookup is global, not per-account: artists are canonical and shared and account_artist_ids is the join that lets many accounts roster one artist (chat#1866), so a per-account dedup would still have given every account its own copy — it would only have stopped one account duplicating itself.

Merge order is api#791 → this PR. Once #791 is on main I'll re-run the cold-start walk on a fresh account and confirm one artist, one catalog, then take this out of draft.

@sweetmantech
sweetmantech marked this pull request as ready for review July 29, 2026 02:57

@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: 2f6aee0069

ℹ️ 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".

*/
const AddArtistForm = () => {
const { addArtist, isAdding } = useAddRosterArtist();
const { add, isAdding } = useAddSpotifyArtist();

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 Preserve selection when adding setup artists

When a valuation-funnel account already has a catalog and uses this setup form to add another artist, this swapped-in hook also selects and persists the newly created artist via getArtists(created.account_id). The setup payoff renders through useHomeValuation, which scopes measurements to the selected artist and hides whole-catalog results when the echoed scope differs; because no new valuation is kicked when a catalog already exists, the newly added artist has no measured catalog and the user loses the existing baseline valuation in the done panel or /setup/valuation. Use an onboarding path that refreshes without selecting, or restore the previous selection after the add.

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.

1 issue found across 10 files

Confidence score: 3/5

  • In components/Onboarding/AddArtistForm.tsx, moving to useAddSpotifyArtist appears to value only the first selected artist while later artists are still selected/persisted via getArtists, which can leave onboarding with partially initialized artist data and inconsistent downstream behavior — ensure each added artist triggers POST /api/valuation (or block persistence until valuation completes).
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="components/Onboarding/AddArtistForm.tsx">

<violation number="1" location="components/Onboarding/AddArtistForm.tsx:26">
P2: Switching AddArtistForm to useAddSpotifyArtist means adding a second artist during setup will select and persist that new artist via getArtists, but useAddSpotifyArtist only triggers POST /api/valuation for the first artist when no catalog exists yet. If an account already has a catalog and baseline valuation, adding another artist here selects it without any valuation, and since useHomeValuation scopes results to the selected artist, the previously-computed baseline valuation would disappear from the done panel / /setup/valuation. Consider refreshing without changing the selection, or restoring the prior selection after the add when a catalog already exists.</violation>
</file>

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

Re-trigger cubic

*/
const AddArtistForm = () => {
const { addArtist, isAdding } = useAddRosterArtist();
const { add, isAdding } = useAddSpotifyArtist();

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: Switching AddArtistForm to useAddSpotifyArtist means adding a second artist during setup will select and persist that new artist via getArtists, but useAddSpotifyArtist only triggers POST /api/valuation for the first artist when no catalog exists yet. If an account already has a catalog and baseline valuation, adding another artist here selects it without any valuation, and since useHomeValuation scopes results to the selected artist, the previously-computed baseline valuation would disappear from the done panel / /setup/valuation. Consider refreshing without changing the selection, or restoring the prior selection after the add when a catalog already exists.

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 26:

<comment>Switching AddArtistForm to useAddSpotifyArtist means adding a second artist during setup will select and persist that new artist via getArtists, but useAddSpotifyArtist only triggers POST /api/valuation for the first artist when no catalog exists yet. If an account already has a catalog and baseline valuation, adding another artist here selects it without any valuation, and since useHomeValuation scopes results to the selected artist, the previously-computed baseline valuation would disappear from the done panel / /setup/valuation. Consider refreshing without changing the selection, or restoring the prior selection after the add when a catalog already exists.</comment>

<file context>
@@ -15,16 +15,19 @@ import type { SpotifyArtistSearchResult } from "@/types/spotify";
  */
 const AddArtistForm = () => {
-  const { addArtist, isAdding } = useAddRosterArtist();
+  const { add, isAdding } = useAddSpotifyArtist();
   const [isOpen, setIsOpen] = useState(false);
 
</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